Skip to content

Commit 3407eea

Browse files
authored
Merge pull request #22 from oodd-team/OD-49
Od 49
2 parents 6802e73 + 7f62a5f commit 3407eea

26 files changed

+287
-38
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"@nestjs/swagger": "^7.4.2",
2929
"@nestjs/typeorm": "^10.0.2",
3030
"@types/passport-kakao": "^1.0.3",
31+
"class-transformer": "^0.5.1",
3132
"class-validator": "^0.14.1",
3233
"dayjs": "^1.11.13",
3334
"mysql2": "^3.11.0",

src/auth/auth.module.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { JwtStrategy } from './strategies/jwt.strategy';
2020
inject: [ConfigService],
2121
useFactory: async (configService: ConfigService) => ({
2222
secret: configService.get('JWT_SECRET'),
23-
signOptions: { expiresIn: '1d' },
23+
signOptions: { expiresIn: '7d' },
2424
}),
2525
}),
2626
],

src/auth/auth.swagger.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { BaseSwaggerDecorator } from 'nestjs-swagger-decorator';
22
import { LoginResponse } from './dto/auth.response';
33
import { BaseResponse } from 'src/common/response/dto';
4+
import { ErrorCodeVo } from 'src/common/exception/error';
5+
import { ApiInternalServerErrorResponse } from '@nestjs/swagger';
46

57
// 카카오 로그인 API Swagger
68
export const KakaoLoginSwagger = (text: string) => {
@@ -19,7 +21,12 @@ export const KakaoLoginSwagger = (text: string) => {
1921
baseResponseDto: BaseResponse,
2022
},
2123
],
22-
[],
24+
[
25+
ApiInternalServerErrorResponse({
26+
type: ErrorCodeVo,
27+
description: '서버에서 발생한 오류',
28+
}),
29+
],
2330
);
2431
};
2532

src/auth/strategies/kakao.strategy.ts

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export class JwtKakaoStrategy extends PassportStrategy(Strategy, 'kakao') {
2222
done: (error: any, user?: SocialUser, info?: any) => void,
2323
) {
2424
try {
25+
console.log('profile', profile);
2526
const { _json } = profile;
2627
const kakaoUser: SocialUser = {
2728
kakaoId: _json.id,
+5-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import { Module } from '@nestjs/common';
22
import { ChatMessageService } from './chat-message.service';
33
import { ChatMessageController } from './chat-message.controller';
4+
import { TypeOrmModule } from '@nestjs/typeorm';
5+
import { ChatMessage } from 'src/common/entities/chat-message.entity';
46

57
@Module({
8+
imports: [TypeOrmModule.forFeature([ChatMessage])],
69
providers: [ChatMessageService],
7-
controllers: [ChatMessageController]
10+
controllers: [ChatMessageController],
11+
exports: [ChatMessageService],
812
})
913
export class ChatMessageModule {}
+24-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,27 @@
11
import { Injectable } from '@nestjs/common';
2+
import { InjectRepository } from '@nestjs/typeorm';
3+
import { ChatMessage } from 'src/common/entities/chat-message.entity';
4+
import { ChatRoom } from 'src/common/entities/chat-room.entity';
5+
import { CreateMatchingReqeust } from 'src/matching/dto/matching.request';
6+
import { QueryRunner, Repository } from 'typeorm';
27

38
@Injectable()
4-
export class ChatMessageService {}
9+
export class ChatMessageService {
10+
constructor(
11+
@InjectRepository(ChatMessage)
12+
private readonly chatMessageRepository: Repository<ChatMessage>,
13+
) {}
14+
15+
async createChatMessage(
16+
queryRunner: QueryRunner,
17+
chatRoom: ChatRoom,
18+
body: CreateMatchingReqeust,
19+
): Promise<ChatMessage> {
20+
return queryRunner.manager.save(ChatMessage, {
21+
chatRoom: chatRoom,
22+
fromUser: { id: body.requesterId },
23+
toUser: { id: body.targetId },
24+
content: body.message,
25+
});
26+
}
27+
}

src/chat-room/chat-room.module.ts

+4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import { Module } from '@nestjs/common';
22
import { ChatRoomController } from './chat-room.controller';
33
import { ChatRoomService } from './chat-room.service';
4+
import { ChatRoom } from 'src/common/entities/chat-room.entity';
5+
import { TypeOrmModule } from '@nestjs/typeorm';
46

57
@Module({
8+
imports: [TypeOrmModule.forFeature([ChatRoom])],
69
controllers: [ChatRoomController],
710
providers: [ChatRoomService],
11+
exports: [ChatRoomService],
812
})
913
export class ChatRoomModule {}

src/chat-room/chat-room.service.ts

+25-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,28 @@
11
import { Injectable } from '@nestjs/common';
2+
import { InjectRepository } from '@nestjs/typeorm';
3+
import { ChatRoom } from 'src/common/entities/chat-room.entity';
4+
import { Matching } from 'src/common/entities/matching.entity';
5+
import { CreateMatchingReqeust } from 'src/matching/dto/matching.request';
6+
import { Repository, QueryRunner } from 'typeorm';
27

38
@Injectable()
4-
export class ChatRoomService {}
9+
export class ChatRoomService {
10+
constructor(
11+
@InjectRepository(ChatRoom)
12+
private readonly chatRoomRepository: Repository<ChatRoom>,
13+
) {}
14+
15+
async createChatRoom(
16+
queryRunner: QueryRunner,
17+
matching: Matching,
18+
body: CreateMatchingReqeust,
19+
): Promise<ChatRoom> {
20+
// 채팅방 생성 로직
21+
return await queryRunner.manager.save(ChatRoom, {
22+
fromUser: { id: body.requesterId },
23+
toUser: { id: body.targetId },
24+
matching: matching,
25+
requestStatus: 'pending',
26+
});
27+
}
28+
}

src/common/entities/chat-room.entity.ts

+3
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,18 @@ import { BaseEntity } from './base.entity';
33
import { User } from './user.entity';
44
import { ChatMessage } from './chat-message.entity';
55
import { Matching } from './matching.entity';
6+
import { ApiProperty } from '@nestjs/swagger';
67

78
@Entity('ChatRoom')
89
export class ChatRoom extends BaseEntity {
910
@ManyToOne(() => User, (user) => user.sentChatRooms)
1011
@JoinColumn({ name: 'fromUserId' })
12+
@ApiProperty({ type: () => User, description: '채팅 요청자' })
1113
fromUser!: User;
1214

1315
@ManyToOne(() => User, (user) => user.receivedChatRooms)
1416
@JoinColumn({ name: 'toUserId' })
17+
@ApiProperty({ type: () => User, description: '채팅 신청 받는사람' })
1518
toUser!: User;
1619

1720
@ManyToOne(() => Matching, (matching) => matching.chatRooms)

src/common/entities/matching.entity.ts

+12
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,33 @@ import { Entity, Column, ManyToOne, JoinColumn, OneToMany } from 'typeorm';
22
import { BaseEntity } from './base.entity';
33
import { User } from './user.entity';
44
import { ChatRoom } from './chat-room.entity';
5+
import { ApiProperty } from '@nestjs/swagger';
56

67
@Entity('Matching')
78
export class Matching extends BaseEntity {
89
@ManyToOne(() => User, (user) => user.requestedMatchings)
910
@JoinColumn({ name: 'requesterId' })
11+
@ApiProperty({ type: User, description: '매칭 요청자' })
1012
requester!: User;
1113

1214
@ManyToOne(() => User, (user) => user.targetedMatchings)
1315
@JoinColumn({ name: 'targetId' })
16+
@ApiProperty({ type: User, description: '매칭 받는사람' })
1417
target!: User;
1518

1619
@Column({ type: 'text' })
20+
@ApiProperty({
21+
description: '매칭 요청 메시지',
22+
example: '매칭 요청 합니다.',
23+
})
1724
message!: string;
1825

1926
@Column({ type: 'enum', enum: ['pending', 'accepted', 'rejected'] })
27+
@ApiProperty({
28+
enum: ['pending', 'accepted', 'rejected'],
29+
description: '매칭 상태',
30+
example: 'pending',
31+
})
2032
requestStatus: 'pending' | 'accepted' | 'rejected' = 'pending';
2133

2234
@Column({ type: 'datetime', nullable: true })

src/common/entities/user.entity.ts

+13
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,46 @@ import { Matching } from './matching.entity';
77
import { PostLike } from './post-like.entity';
88
import { PostReport } from './post-report.entity';
99
import { ChatMessage } from './chat-message.entity';
10+
import { ApiProperty } from '@nestjs/swagger';
1011

1112
@Entity('User')
1213
export class User extends BaseEntity {
1314
@Column({ type: 'varchar', length: 100, nullable: true })
15+
@ApiProperty({ description: '카카오 고유 ID', example: '1234567890' })
1416
kakaoId!: string | null; // 카카오 고유 ID를 저장하는 필드
1517

18+
@ApiProperty({ description: '네이버 고유 ID', example: '1234567890' })
1619
@Column({ type: 'varchar', length: 100, nullable: true })
1720
naverId!: string | null; // 네이버 고유 ID를 저장하는 필드
1821

22+
@ApiProperty({ description: '이름', example: '홍길동' })
1923
@Column({ type: 'varchar', length: 100, nullable: true })
2024
name!: string | null;
2125

26+
@ApiProperty({ description: '이메일', example: '[email protected]' })
2227
@Column({ type: 'varchar', length: 100, nullable: true })
2328
email!: string | null;
2429

30+
@ApiProperty({ description: '닉네임', example: '마이콜' })
2531
@Column({ type: 'varchar', length: 10, nullable: true })
2632
nickname!: string | null;
2733

34+
@ApiProperty({ description: '전화번호', example: '010-1234-5678' })
2835
@Column({ type: 'varchar', length: 15, nullable: true })
2936
phoneNumber!: string | null;
3037

38+
@ApiProperty({
39+
description: '프로필 사진 URL',
40+
example: 'https://naver.com/profile.jpg',
41+
})
3142
@Column({ type: 'text', nullable: true })
3243
profilePictureUrl!: string | null;
3344

45+
@ApiProperty({ description: '자기소개', example: '안녕하세요' })
3446
@Column({ type: 'varchar', length: 100, nullable: true })
3547
bio!: string | null;
3648

49+
@ApiProperty({ description: '회원가입 시각', example: '2021-08-01 00:00:00' })
3750
@Column('datetime')
3851
joinedAt!: Date; // joinedAt는 datetime 타입
3952

src/common/exception/error/error.code.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
class ErrorCodeVo {
1+
import { ApiProperty } from '@nestjs/swagger';
2+
export class ErrorCodeVo {
3+
@ApiProperty({ example: 404, description: 'HTTP 상태 코드' })
24
readonly status: number;
5+
@ApiProperty({ example: 'Entity Not Found', description: '오류 메시지' })
36
readonly message: string;
7+
@ApiProperty({ example: 'ENTITY_NOT_FOUND', description: '오류 메시지' })
48
readonly code: string;
59

610
constructor(status, message, code) {

src/common/exception/service.exception.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {
22
NOT_FOUND_DATA,
33
ErrorCode,
4-
INVALID_INPUT_VALUE,
54
UNAUTHORIZED,
65
FORBIDDEN,
76
INTERNAL_SERVER_ERROR,
@@ -12,9 +11,10 @@ export const DataNotFoundException = (message?: string): ServiceException => {
1211
};
1312

1413
export const InvalidInputValueException = (
14+
code: string,
1515
message?: string,
1616
): ServiceException => {
17-
return new ServiceException(INVALID_INPUT_VALUE, message);
17+
return new ServiceException({ status: 400, message: message, code: code });
1818
};
1919

2020
export const UnauthorizedException = (message?: string): ServiceException => {

src/common/response/base.response.ts

-10
This file was deleted.

src/main.ts

+2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import { NestFactory } from '@nestjs/core';
22
import { AppModule } from './app.module';
33
import { setupSwagger } from './utils/swagger';
44
import { ServiceExceptionToHttpExceptionFilter } from './common/exception-filter';
5+
import { ValidationPipe } from '@nestjs/common';
56

67
async function bootstrap() {
78
const app = await NestFactory.create(AppModule);
89
app.useGlobalFilters(new ServiceExceptionToHttpExceptionFilter());
10+
app.useGlobalPipes(new ValidationPipe());
911
setupSwagger(app);
1012
await app.listen(process.env.PORT);
1113
console.log(`Application is running on: ${await app.getUrl()}`);

src/matching/dto/matching.request.ts

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { ApiProperty } from '@nestjs/swagger';
2+
import { IsInt, IsString, MaxLength, MinLength } from 'class-validator';
3+
4+
export class CreateMatchingReqeust {
5+
@ApiProperty({ example: 1, description: '신청하는 유저 아이디' })
6+
@IsInt()
7+
requesterId: number;
8+
9+
@ApiProperty({ example: 2, description: '매칭 상대 유저 아이디' })
10+
@IsInt()
11+
targetId: number;
12+
13+
@ApiProperty({
14+
description: '매칭 요청 메시지',
15+
example: '안녕하세요! 매칭 요청합니다.',
16+
})
17+
@IsString()
18+
@MinLength(1)
19+
@MaxLength(100)
20+
message: string;
21+
}

src/matching/dto/matching.response.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { ApiProperty } from '@nestjs/swagger';
2+
3+
export class PostMatchingResponse {
4+
@ApiProperty({ example: 1, description: '채팅방 아이디' })
5+
chatRoomId: number;
6+
7+
@ApiProperty({ example: 1, description: '신청한 유저 아이디' })
8+
fromUserId: number;
9+
10+
@ApiProperty({ example: 2, description: '매칭 상대 유저 아이디' })
11+
toUserId: number;
12+
}

src/matching/matching.controller.ts

+40-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
import { Controller, Get, Patch, Post } from '@nestjs/common';
1+
import {
2+
Body,
3+
Controller,
4+
Get,
5+
Patch,
6+
Post,
7+
Req,
8+
UseGuards,
9+
} from '@nestjs/common';
210
import { MatchingService } from './matching.service';
311
import {
412
CreateMatchingSwagger,
@@ -8,17 +16,45 @@ import {
816
PatchMatchingRequestStatusSwagger,
917
} from './matching.swagger';
1018
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
19+
import { CreateMatchingReqeust } from './dto/matching.request';
20+
import { UserService } from 'src/user/user.service';
21+
import { Request } from 'express';
22+
import {
23+
DataNotFoundException,
24+
UnauthorizedException,
25+
} from 'src/common/exception/service.exception';
26+
import { BaseResponse } from 'src/common/response/dto';
27+
import { PostMatchingResponse } from './dto/matching.response';
28+
import { AuthGuard } from 'src/auth/guards/jwt.auth.guard';
1129

1230
@ApiBearerAuth()
1331
@Controller('matching')
1432
@ApiTags('[서비스] 매칭')
1533
export class MatchingController {
16-
constructor(private readonly matchingService: MatchingService) {}
34+
constructor(
35+
private readonly matchingService: MatchingService,
36+
private readonly userService: UserService,
37+
) {}
1738

1839
@Post()
40+
@UseGuards(AuthGuard)
1941
@CreateMatchingSwagger('매칭 생성 API')
20-
createMatching() {
21-
// return this.userService.getHello();
42+
async createMatching(
43+
@Req() req: Request,
44+
@Body() body: CreateMatchingReqeust,
45+
): Promise<BaseResponse<PostMatchingResponse>> {
46+
if (req.user.id !== body.requesterId)
47+
throw UnauthorizedException('권한이 없습니다.');
48+
49+
if (!(await this.userService.getUserById(body.targetId)))
50+
throw DataNotFoundException('대상 유저가 존재하지 않습니다.');
51+
52+
const chatRoom = await this.matchingService.createMatching(body);
53+
return new BaseResponse<PostMatchingResponse>(true, 'SUCCESS', {
54+
chatRoomId: chatRoom.id,
55+
toUserId: chatRoom.toUser.id,
56+
fromUserId: chatRoom.fromUser.id,
57+
});
2258
}
2359

2460
@Patch()

0 commit comments

Comments
 (0)