Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions prisma/migrations/20251129081252_add_block/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
-- CreateTable
CREATE TABLE "Block" (
"blockerId" UUID NOT NULL,
"blockingId" UUID NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT "Block_pkey" PRIMARY KEY ("blockingId","blockerId")
);

-- CreateIndex
CREATE INDEX "Block_blockerId_createdAt_idx" ON "Block"("blockerId", "createdAt" DESC);

-- AddForeignKey
ALTER TABLE "Block" ADD CONSTRAINT "Block_blockerId_fkey" FOREIGN KEY ("blockerId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "Block" ADD CONSTRAINT "Block_blockingId_fkey" FOREIGN KEY ("blockingId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
15 changes: 15 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ model User {
albums Album[]
chatUsers ChatUser[]
pushTokens PushToken[]
blockers Block[] @relation("blocker")
blockings Block[] @relation("blocking")

@@unique([provider, providerId])
}
Expand All @@ -62,6 +64,19 @@ model Follow {
@@index([followingId])
}

// 차단
model Block {
blockerId String @db.Uuid
blockingId String @db.Uuid
createdAt DateTime @default(now())

blocker User @relation("blocker", fields: [blockerId], references: [id], onDelete: Cascade)
blocking User @relation("blocking", fields: [blockingId], references: [id], onDelete: Cascade)

@@id([blockingId, blockerId])
@@index([blockerId, createdAt(sort: Desc)])
}

model Feed {
id String @id @default(uuid()) @db.Uuid
authorId String @db.Uuid
Expand Down
6 changes: 3 additions & 3 deletions src/module/chat/chat.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { CurrentUser } from 'src/core/decorator';
import { ChatService } from './chat.service';
import { IdResponse } from 'src/shared/response';
import { ChatsResponse } from './dto/chat.response';
import { UserBaseResponse } from '../user/dto';
import { UserBaseResponse, UserBaseWithBlockedResponse } from '../user/dto';

@ApiTags('/chats')
@ApiBearerAuth()
Expand Down Expand Up @@ -93,12 +93,12 @@ export class ChatController {
}

@ApiOperation({ summary: '상대 유저 조회' })
@ApiResponse({ status: 200, type: UserBaseResponse })
@ApiResponse({ status: 200, type: UserBaseWithBlockedResponse })
@Get(':id/user')
async getOpponentUser(
@CurrentUser() userId: string,
@Param('id', new ParseUUIDPipe()) chatId: string,
) {
): Promise<UserBaseWithBlockedResponse> {
return await this.chatService.getOpponentUser(userId, chatId);
}

Expand Down
8 changes: 6 additions & 2 deletions src/module/chat/chat.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,13 @@ export class ChatService {
}

async getOpponentUser(userId: string, chatId: string) {
const chatUsers = await this.chatReader.findUsersByChatId(chatId);
const opponentUser = await this.chatReader.findOpponentUserByChatId(
userId,
chatId,
);

if (!opponentUser) throw new HttpException('CHAT', 404);

const opponentUser = chatUsers.filter((user) => user.id !== userId)[0];
return {
...opponentUser,
image: getImageUrl(opponentUser.image),
Expand Down
34 changes: 34 additions & 0 deletions src/module/chat/repository/chat.reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,40 @@ export class ChatReader {
}));
}

async findOpponentUserByChatId(userId: string, chatId: string) {
const result = await this.txHost.tx.$kysely
.selectFrom('ChatUser')
.where('ChatUser.chatId', '=', kyselyUuid(chatId))
.where('ChatUser.userId', '!=', kyselyUuid(userId))
.innerJoin('User', 'ChatUser.userId', 'User.id')
.select([
'User.id as id',
'User.name as name',
'User.image as image',
'User.url as url',
])
.select((eb) =>
eb
.fn<boolean>('EXISTS', [
eb
.selectFrom('Block')
.where('Block.blockerId', '=', kyselyUuid(userId))
.whereRef('Block.blockingId', '=', 'User.id'),
])
.as('isBlocked'),
)
.executeTakeFirst();

if (!result) return null;
return {
id: result.id,
name: result.name,
image: result.image,
url: result.url,
isBlocked: result.isBlocked,
};
}

async findMessageById(id: string) {
return await this.txHost.tx.chatMessage.findUnique({
where: { id },
Expand Down
2 changes: 1 addition & 1 deletion src/module/feed-comment/dto/feed-comment.response.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApiProperty } from '@nestjs/swagger';
import { UserBaseResponse } from '../../user/dto/user.response';
import { UserBaseResponse } from '../../user/dto/user.dto.response';

export class FeedCommentBaseResponse {
@ApiProperty()
Expand Down
15 changes: 9 additions & 6 deletions src/module/feed/dto/feed.response.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { ApiProperty } from '@nestjs/swagger';
import { UserBaseResponse } from '../../user/dto/user.response';
import {
UserBaseResponse,
UserBaseWithBlockedResponse,
} from '../../user/dto/user.dto.response';
import {
CursorAndCountResponse,
CursorResponse,
Expand Down Expand Up @@ -37,9 +40,6 @@ export class FeedResponse extends FeedBaseResponse {

@ApiProperty()
tags: string[];

@ApiProperty({ type: UserBaseResponse })
author: UserBaseResponse;
}

export class SearchedFeedResponse extends FeedBaseResponse {
Expand Down Expand Up @@ -89,6 +89,9 @@ export class FollowingFeedResponse extends FeedResponse {

@ApiProperty({ type: FeedCommentBaseResponse, nullable: true })
comment: FeedCommentBaseResponse | null;

@ApiProperty({ type: UserBaseResponse })
author: UserBaseResponse;
}

export class FollowingFeedsResponse extends CursorResponse {
Expand All @@ -106,8 +109,8 @@ export class FeedDetailResponse extends FeedResponse {
@ApiProperty()
commentCount: number;

@ApiProperty({ type: AlbumBaseResponse, nullable: true })
album: AlbumBaseResponse | null;
@ApiProperty({ type: UserBaseWithBlockedResponse })
author: UserBaseWithBlockedResponse;
}

export class FeedMetaResponse extends FeedBaseResponse {
Expand Down
5 changes: 3 additions & 2 deletions src/module/feed/feed.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Module } from '@nestjs/common';
import { Module, forwardRef } from '@nestjs/common';
import { FeedController } from './feed.controller';
import { FeedService } from './feed.service';
import { FeedWriter } from './repository/feed.writer';
Expand All @@ -7,9 +7,10 @@ import { SearchModule } from 'src/database/search/search.module';
import { DdbModule } from 'src/database/ddb/ddb.module';
import { RedisModule } from 'src/database/redis/redis.module';
import { TagController } from './tag.controller';
import { UserModule } from '../user/user.module';

@Module({
imports: [SearchModule, DdbModule, RedisModule],
imports: [SearchModule, DdbModule, RedisModule, forwardRef(() => UserModule)],
controllers: [FeedController, TagController],
providers: [FeedService, FeedReader, FeedWriter],
exports: [FeedReader, FeedWriter],
Expand Down
7 changes: 7 additions & 0 deletions src/module/feed/feed.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { RedisService } from 'src/database/redis/redis.service';
import { getImageUrl } from 'src/shared/util/get-image-url';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { Transactional } from '@nestjs-cls/transactional';
import { UserReader } from '../user/repository/user.reader';

@Injectable()
export class FeedService {
Expand All @@ -17,6 +18,7 @@ export class FeedService {
private redisService: RedisService,
@Inject(SearchService) private searchService: SearchService,
private eventEmitter: EventEmitter2,
private userReader: UserReader,
) {}

async create(userId: string, createFeedInput: CreateFeedInput) {
Expand Down Expand Up @@ -176,10 +178,15 @@ export class FeedService {
}

async getLatestFeeds(userId: string | null, { cursor, size }: GetFeedsInput) {
let blockingIds: string[] = [];
if (userId !== null) {
blockingIds = await this.userReader.findMyBlockingIds(userId);
}
const result = await this.feedReader.findManyLatestWithCursor({
userId,
cursor,
size,
blockingIds,
});

return {
Expand Down
30 changes: 25 additions & 5 deletions src/module/feed/repository/feed.reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export class FeedReader {
'likeCount',
'content',
])
.innerJoin('User', 'Feed.authorId', 'User.id')
.innerJoin('User as Author', 'Feed.authorId', 'Author.id')
.leftJoin('Album', 'Album.id', 'Feed.albumId')
.select(['Album.id as albumId', 'Album.name as albumName'])
.select((eb) =>
Expand All @@ -58,9 +58,9 @@ export class FeedReader {
.as('commentCount'),
)
.select([
'User.id as authorId',
'User.name',
'User.image as image',
'Author.id as authorId',
'Author.name',
'Author.image as image',
'url',
])
.select((eb) =>
Expand Down Expand Up @@ -90,6 +90,14 @@ export class FeedReader {
.where('Save.userId', '=', kyselyUuid(userId!)),
])
.as('isSave'),
eb
.fn<boolean>('EXISTS', [
eb
.selectFrom('Block')
.whereRef('Block.blockerId', '=', 'Author.id')
.where('Block.blockingId', '=', kyselyUuid(userId!)),
])
.as('isBlocked'),
]),
)
.execute();
Expand All @@ -114,6 +122,7 @@ export class FeedReader {
name: feed.name,
image: feed.image,
url: feed.url,
isBlocked: feed.isBlocked ?? false,
},
album:
feed.albumId && feed.albumName
Expand Down Expand Up @@ -236,7 +245,12 @@ export class FeedReader {
};
}

async findManyLatestWithCursor({ userId, cursor, size }: GetFeedsInput) {
async findManyLatestWithCursor({
userId,
cursor,
size,
blockingIds,
}: GetFeedsInput) {
let query = this.txHost.tx.$kysely
.selectFrom('Feed')
.select([
Expand All @@ -261,6 +275,11 @@ export class FeedReader {
.as('isLike'),
]),
)
.$if(blockingIds.length > 0, (eb) =>
eb.where((eb) =>
eb('Feed.authorId', 'not in', blockingIds.map(kyselyUuid)),
),
)
.orderBy('Feed.createdAt', 'desc')
.orderBy('Feed.id', 'desc')
.limit(size);
Expand Down Expand Up @@ -887,6 +906,7 @@ type GetFeedsInput = {
userId?: string | null;
cursor: string | null;
size: number;
blockingIds: string[];
};

type FindFeedsByUserInput = {
Expand Down
2 changes: 1 addition & 1 deletion src/module/post-comment/dto/post-comment.response.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApiProperty } from '@nestjs/swagger';
import { UserBaseResponse } from '../../user/dto/user.response';
import { UserBaseResponse } from '../../user/dto/user.dto.response';

export class PostCommentBaseResponse {
@ApiProperty()
Expand Down
2 changes: 1 addition & 1 deletion src/module/post/dto/post.response.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApiProperty } from '@nestjs/swagger';
import { UserBaseResponse } from '../../user/dto/user.response';
import { UserBaseResponse } from '../../user/dto';
import { TotalCountResponse } from '../../../shared/response/total-count.response';
import { postTypes } from '../../../common/constants/post.constant';

Expand Down
2 changes: 2 additions & 0 deletions src/module/user/dto/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export * from './user.dto.response';

export * from './user.request';
export * from './user.response';
24 changes: 24 additions & 0 deletions src/module/user/dto/user.dto.response.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ApiProperty } from '@nestjs/swagger';

export class UserBaseResponse {
@ApiProperty()
id: string;

@ApiProperty()
name: string;

@ApiProperty({
example: 'profile/{UUID}.jpg',
nullable: true,
type: 'string',
})
image: string | null;

@ApiProperty({ description: '라우팅용 url' })
url: string;
}

export class UserBaseWithBlockedResponse extends UserBaseResponse {
@ApiProperty({ description: '차단 여부' })
isBlocked: boolean;
}
Loading
Loading