upd
This commit is contained in:
@@ -1,13 +1,10 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
Injectable,
|
||||
} from '@nestjs/common';
|
||||
import { BadRequestException, ForbiddenException, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { and, eq, or } from 'drizzle-orm';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { DrizzleService } from '../../database/drizzle.service';
|
||||
import { like, match, user } from '../../database/schema';
|
||||
import { like, match, profile, user } from '../../database/schema';
|
||||
import { NotificationsService } from '../../notifications/notifications.service';
|
||||
import { RedisService } from '../../redis/redis.service';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { CreateLikeDto } from './dto/create-like.dto';
|
||||
|
||||
@Injectable()
|
||||
@@ -19,16 +16,18 @@ export class LikesService {
|
||||
private readonly configService: ConfigService,
|
||||
) {}
|
||||
|
||||
async createLike(sourceUserId: string, dto: CreateLikeDto) {
|
||||
if (sourceUserId === dto.targetUserId) {
|
||||
async createLike(userId: string, dto: CreateLikeDto) {
|
||||
await this.assertProfileOwnership(userId, dto.sourceProfileId);
|
||||
|
||||
if (dto.sourceProfileId === dto.targetProfileId) {
|
||||
throw new BadRequestException('Cannot like yourself');
|
||||
}
|
||||
|
||||
const maxMatches = this.configService.get<number>('app.maxMatchesBeforePause');
|
||||
const activeMatchesCount = await this.getActiveMatchesCount(sourceUserId);
|
||||
const activeMatchesCount = await this.getMatchesCount(dto.sourceProfileId);
|
||||
if (activeMatchesCount >= maxMatches) {
|
||||
throw new BadRequestException(
|
||||
`You have ${activeMatchesCount} matches. Resolve them before searching for new ones.`,
|
||||
`Profile has ${activeMatchesCount} matches. Resolve them before searching for new ones.`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -37,92 +36,99 @@ export class LikesService {
|
||||
.from(like)
|
||||
.where(
|
||||
and(
|
||||
eq(like.sourceUser, sourceUserId),
|
||||
eq(like.targetUser, dto.targetUserId),
|
||||
eq(like.sourceProfileId, dto.sourceProfileId),
|
||||
eq(like.targetProfileId, dto.targetProfileId),
|
||||
),
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
if (existing.length > 0) {
|
||||
throw new BadRequestException('Already reacted to this user');
|
||||
}
|
||||
if (existing.length > 0) throw new BadRequestException('Already reacted to this profile');
|
||||
|
||||
const [newLike] = await this.drizzleService.db
|
||||
.insert(like)
|
||||
.values({
|
||||
sourceUser: sourceUserId,
|
||||
targetUser: dto.targetUserId,
|
||||
sourceProfileId: dto.sourceProfileId,
|
||||
targetProfileId: dto.targetProfileId,
|
||||
type: dto.type,
|
||||
})
|
||||
.returning();
|
||||
|
||||
if (dto.type === 'like') {
|
||||
return this.checkAndCreateMatch(sourceUserId, dto.targetUserId, newLike);
|
||||
return this.checkAndCreateMatch(dto.sourceProfileId, dto.targetProfileId, newLike);
|
||||
}
|
||||
|
||||
return { like: newLike, match: null };
|
||||
}
|
||||
|
||||
private async checkAndCreateMatch(userId1: string, userId2: string, newLike: any) {
|
||||
async getMyMatches(userId: string, profileId: string) {
|
||||
await this.assertProfileOwnership(userId, profileId);
|
||||
return this.drizzleService.db
|
||||
.select()
|
||||
.from(match)
|
||||
.where(or(eq(match.profile1Id, profileId), eq(match.profile2Id, profileId)))
|
||||
.orderBy(match.createdAt);
|
||||
}
|
||||
|
||||
private async checkAndCreateMatch(profileId1: string, profileId2: string, newLike: any) {
|
||||
const reverseLike = await this.drizzleService.db
|
||||
.select()
|
||||
.from(like)
|
||||
.where(
|
||||
and(
|
||||
eq(like.sourceUser, userId2),
|
||||
eq(like.targetUser, userId1),
|
||||
eq(like.sourceProfileId, profileId2),
|
||||
eq(like.targetProfileId, profileId1),
|
||||
eq(like.type, 'like'),
|
||||
),
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
if (reverseLike.length === 0) {
|
||||
return { like: newLike, match: null };
|
||||
}
|
||||
if (reverseLike.length === 0) return { like: newLike, match: null };
|
||||
|
||||
const existingMatch = await this.drizzleService.db
|
||||
.select()
|
||||
.from(match)
|
||||
.where(
|
||||
or(
|
||||
and(eq(match.user1Id, userId1), eq(match.user2Id, userId2)),
|
||||
and(eq(match.user1Id, userId2), eq(match.user2Id, userId1)),
|
||||
and(eq(match.profile1Id, profileId1), eq(match.profile2Id, profileId2)),
|
||||
and(eq(match.profile1Id, profileId2), eq(match.profile2Id, profileId1)),
|
||||
),
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
if (existingMatch.length > 0) {
|
||||
return { like: newLike, match: existingMatch[0] };
|
||||
}
|
||||
if (existingMatch.length > 0) return { like: newLike, match: existingMatch[0] };
|
||||
|
||||
const [newMatch] = await this.drizzleService.db
|
||||
.insert(match)
|
||||
.values({ user1Id: userId1, user2Id: userId2 })
|
||||
.values({ profile1Id: profileId1, profile2Id: profileId2 })
|
||||
.returning();
|
||||
|
||||
await this.notifyMatch(userId1, userId2, newMatch.id);
|
||||
await this.notifyMatch(profileId1, profileId2, newMatch.id);
|
||||
|
||||
return { like: newLike, match: newMatch };
|
||||
}
|
||||
|
||||
private async getActiveMatchesCount(userId: string): Promise<number> {
|
||||
private async getMatchesCount(profileId: string): Promise<number> {
|
||||
const matches = await this.drizzleService.db
|
||||
.select({ id: match.id })
|
||||
.from(match)
|
||||
.where(
|
||||
or(eq(match.user1Id, userId), eq(match.user2Id, userId)),
|
||||
);
|
||||
.where(or(eq(match.profile1Id, profileId), eq(match.profile2Id, profileId)));
|
||||
return matches.length;
|
||||
}
|
||||
|
||||
private async notifyMatch(userId1: string, userId2: string, matchId: string) {
|
||||
const users = await this.drizzleService.db
|
||||
.select({ id: user.id, fcmToken: user.fcmToken })
|
||||
.from(user)
|
||||
.where(or(eq(user.id, userId1), eq(user.id, userId2)));
|
||||
private async notifyMatch(profileId1: string, profileId2: string, matchId: string) {
|
||||
const profiles = await this.drizzleService.db
|
||||
.select({ userId: profile.userId })
|
||||
.from(profile)
|
||||
.where(or(eq(profile.id, profileId1), eq(profile.id, profileId2)));
|
||||
|
||||
for (const u of users) {
|
||||
if (u.fcmToken) {
|
||||
for (const p of profiles) {
|
||||
const [u] = await this.drizzleService.db
|
||||
.select({ fcmToken: user.fcmToken })
|
||||
.from(user)
|
||||
.where(eq(user.id, p.userId))
|
||||
.limit(1);
|
||||
|
||||
if (u?.fcmToken) {
|
||||
await this.notificationsService.sendPushNotification(
|
||||
u.fcmToken,
|
||||
'New Match!',
|
||||
@@ -132,16 +138,20 @@ export class LikesService {
|
||||
}
|
||||
}
|
||||
|
||||
await this.redisService.publish('match:created', JSON.stringify({ matchId, userId1, userId2 }));
|
||||
await this.redisService.publish(
|
||||
'match:created',
|
||||
JSON.stringify({ matchId, profileId1, profileId2 }),
|
||||
);
|
||||
}
|
||||
|
||||
async getMyMatches(userId: string) {
|
||||
return this.drizzleService.db
|
||||
.select()
|
||||
.from(match)
|
||||
.where(
|
||||
or(eq(match.user1Id, userId), eq(match.user2Id, userId)),
|
||||
)
|
||||
.orderBy(match.createdAt);
|
||||
private async assertProfileOwnership(userId: string, profileId: string) {
|
||||
const [found] = await this.drizzleService.db
|
||||
.select({ userId: profile.userId })
|
||||
.from(profile)
|
||||
.where(eq(profile.id, profileId))
|
||||
.limit(1);
|
||||
|
||||
if (!found) throw new NotFoundException('Profile not found');
|
||||
if (found.userId !== userId) throw new ForbiddenException('Profile does not belong to you');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user