✨ feat(database-schema): добавляет ссылку на активный чат в схему профиля пользователя
✨ feat(likes.service): обновляет логику создания лайков с учетом активного чата ✨ feat(likes.module): импортирует модуль Gateways для работы с чатами ✨ feat(feed.service): добавляет условие для фильтрации профилей без активного чата ✨ feat(storage.service): устанавливает политику доступа к бакету S3 для получения объектов ✨ feat(likes-response.dto): добавляет поле чата в ответ на лайк ✨ feat(media.controller): добавляет описание для загрузки медиафайлов ✨ feat(chat.service): добавляет возможность закрытия чата с отчетом или сообщением ✨ feat(chat.controller): обновляет метод закрытия чата для обработки отчетов ✨ feat(app.module): добавляет модуль Dev для разработки в не продакшн среде
This commit is contained in:
@@ -2,9 +2,10 @@ import { BadRequestException, ForbiddenException, Injectable, NotFoundException
|
||||
import { and, eq, or } from 'drizzle-orm';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { DrizzleService } from '../../database/drizzle.service';
|
||||
import { like, match, profile, user } from '../../database/schema';
|
||||
import { chat, like, match, profile, user } from '../../database/schema';
|
||||
import { NotificationsService } from '../../notifications/notifications.service';
|
||||
import { RedisService } from '../../redis/redis.service';
|
||||
import { ChatGateway } from '../../gateways/chat.gateway';
|
||||
import { CreateLikeDto } from './dto/create-like.dto';
|
||||
|
||||
@Injectable()
|
||||
@@ -14,6 +15,7 @@ export class LikesService {
|
||||
private readonly notificationsService: NotificationsService,
|
||||
private readonly redisService: RedisService,
|
||||
private readonly configService: ConfigService,
|
||||
private readonly chatGateway: ChatGateway,
|
||||
) {}
|
||||
|
||||
async createLike(userId: string, dto: CreateLikeDto) {
|
||||
@@ -23,11 +25,15 @@ export class LikesService {
|
||||
throw new BadRequestException('Cannot like yourself');
|
||||
}
|
||||
|
||||
const maxMatches = this.configService.get<number>('app.maxMatchesBeforePause');
|
||||
const activeMatchesCount = await this.getMatchesCount(dto.sourceProfileId);
|
||||
if (activeMatchesCount >= maxMatches) {
|
||||
const [sourceProfile] = await this.drizzleService.db
|
||||
.select({ activeChatId: profile.activeChatId })
|
||||
.from(profile)
|
||||
.where(eq(profile.id, dto.sourceProfileId))
|
||||
.limit(1);
|
||||
|
||||
if (sourceProfile?.activeChatId) {
|
||||
throw new BadRequestException(
|
||||
`Profile has ${activeMatchesCount} matches. Resolve them before searching for new ones.`,
|
||||
'У вас уже есть активный матч. Завершите текущий диалог перед поиском.',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,7 +63,7 @@ export class LikesService {
|
||||
return this.checkAndCreateMatch(dto.sourceProfileId, dto.targetProfileId, newLike);
|
||||
}
|
||||
|
||||
return { like: newLike, match: null };
|
||||
return { like: newLike, match: null, chat: null };
|
||||
}
|
||||
|
||||
async getMyMatches(userId: string, profileId: string) {
|
||||
@@ -82,7 +88,17 @@ export class LikesService {
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
if (reverseLike.length === 0) return { like: newLike, match: null };
|
||||
if (reverseLike.length === 0) return { like: newLike, match: null, chat: null };
|
||||
|
||||
const [targetProfile] = await this.drizzleService.db
|
||||
.select({ activeChatId: profile.activeChatId })
|
||||
.from(profile)
|
||||
.where(eq(profile.id, profileId2))
|
||||
.limit(1);
|
||||
|
||||
if (targetProfile?.activeChatId) {
|
||||
return { like: newLike, match: null, chat: null };
|
||||
}
|
||||
|
||||
const existingMatch = await this.drizzleService.db
|
||||
.select()
|
||||
@@ -95,27 +111,56 @@ export class LikesService {
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
if (existingMatch.length > 0) return { like: newLike, match: existingMatch[0] };
|
||||
if (existingMatch.length > 0) {
|
||||
const existingChat = await this.drizzleService.db
|
||||
.select()
|
||||
.from(chat)
|
||||
.where(
|
||||
or(
|
||||
and(eq(chat.profile1Id, profileId1), eq(chat.profile2Id, profileId2)),
|
||||
and(eq(chat.profile1Id, profileId2), eq(chat.profile2Id, profileId1)),
|
||||
),
|
||||
)
|
||||
.limit(1);
|
||||
return { like: newLike, match: existingMatch[0], chat: existingChat[0] ?? null };
|
||||
}
|
||||
|
||||
const [newMatch] = await this.drizzleService.db
|
||||
.insert(match)
|
||||
.values({ profile1Id: profileId1, profile2Id: profileId2 })
|
||||
.returning();
|
||||
|
||||
await this.notifyMatch(profileId1, profileId2, newMatch.id);
|
||||
const [newChat] = await this.drizzleService.db
|
||||
.insert(chat)
|
||||
.values({
|
||||
profile1Id: profileId1,
|
||||
profile2Id: profileId2,
|
||||
status: 'active',
|
||||
} as any)
|
||||
.returning();
|
||||
|
||||
return { like: newLike, match: newMatch };
|
||||
await this.drizzleService.db
|
||||
.update(profile)
|
||||
.set({ activeChatId: newChat.id } as any)
|
||||
.where(or(eq(profile.id, profileId1), eq(profile.id, profileId2)));
|
||||
|
||||
await this.notifyMatch(profileId1, profileId2, newMatch.id, newChat.id);
|
||||
|
||||
return { like: newLike, match: newMatch, chat: newChat };
|
||||
}
|
||||
|
||||
private async getMatchesCount(profileId: string): Promise<number> {
|
||||
const matches = await this.drizzleService.db
|
||||
.select({ id: match.id })
|
||||
.from(match)
|
||||
.where(or(eq(match.profile1Id, profileId), eq(match.profile2Id, profileId)));
|
||||
return matches.length;
|
||||
}
|
||||
private async notifyMatch(profileId1: string, profileId2: string, matchId: string, chatId: string) {
|
||||
this.chatGateway.emitToProfile(profileId1, 'match_created', {
|
||||
matchId,
|
||||
chatId,
|
||||
partnerProfileId: profileId2,
|
||||
});
|
||||
this.chatGateway.emitToProfile(profileId2, 'match_created', {
|
||||
matchId,
|
||||
chatId,
|
||||
partnerProfileId: profileId1,
|
||||
});
|
||||
|
||||
private async notifyMatch(profileId1: string, profileId2: string, matchId: string) {
|
||||
const profiles = await this.drizzleService.db
|
||||
.select({ userId: profile.userId })
|
||||
.from(profile)
|
||||
@@ -131,16 +176,16 @@ export class LikesService {
|
||||
if (u?.fcmToken) {
|
||||
await this.notificationsService.sendPushNotification(
|
||||
u.fcmToken,
|
||||
'New Match!',
|
||||
'You have a new match! Start chatting now.',
|
||||
{ matchId, type: 'match' },
|
||||
'Новый матч!',
|
||||
'У вас новый матч. Начните общение прямо сейчас.',
|
||||
{ matchId, chatId, type: 'match_created' },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await this.redisService.publish(
|
||||
'match:created',
|
||||
JSON.stringify({ matchId, profileId1, profileId2 }),
|
||||
JSON.stringify({ matchId, chatId, profileId1, profileId2 }),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user