-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat : AI 분류 폴더 이름 리스트get api, AI 분류 링크 리스트 get api #26
Changes from all commits
8c60056
1ec8b47
2c99b7e
a5b1ac2
8a9d898
77580bd
e9abb46
63e5f58
ccf6809
68ade94
aea6e40
53e166c
09892b7
9764ef8
6f4f0f3
e4e0f1c
33f950a
0df80c4
bd3c19b
68e4b20
b3c8851
e1f50ef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
export class BaseDocument { | ||
createdAt: string; | ||
createdAt: Date; | ||
|
||
updatedAt: string; | ||
updatedAt: Date; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { Controller, Get, UseGuards, Param } from '@nestjs/common'; | ||
import { ClassificationService } from './classification.service'; | ||
import { GetUser } from '@src/common'; | ||
import { | ||
ClassificationControllerDocs, | ||
GetAIFolderNameListDocs, | ||
GetAIPostListDocs, | ||
} from './docs'; | ||
import { JwtGuard } from '../users/guards'; | ||
import { AIFolderNameListResponse } from './response/ai-folder-list.dto'; | ||
import { AIPostListResponse } from './response/ai-post-list.dto'; | ||
|
||
@Controller('classification') | ||
@UseGuards(JwtGuard) | ||
@ClassificationControllerDocs | ||
export class ClassificationController { | ||
constructor(private readonly classificationService: ClassificationService) {} | ||
|
||
@Get('/suggestions') //TODO : 정렬 | ||
@GetAIFolderNameListDocs | ||
async getSuggestedFolderNameList(@GetUser() userId: String) { | ||
const folderNames = | ||
await this.classificationService.getFolderNameList(userId); | ||
|
||
return new AIFolderNameListResponse(folderNames); | ||
} | ||
|
||
@Get('/suggestions/:folderId') | ||
@GetAIPostListDocs | ||
async getSuggestedPostList( | ||
@GetUser() userId: string, | ||
@Param('folderId') folderId: string, | ||
) { | ||
const posts = await this.classificationService.getPostList( | ||
userId, | ||
folderId, | ||
); | ||
|
||
return new AIPostListResponse(posts); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { ClassificationService } from './classification.service'; | ||
import { MongooseModule } from '@nestjs/mongoose'; | ||
import { | ||
Folder, | ||
FolderSchema, | ||
Post, | ||
AIClassification, | ||
PostAIClassificationSchema, | ||
PostSchema, | ||
} from '@src/infrastructure/database/schema'; | ||
|
||
import { ClassificationController } from './classification.controller'; | ||
|
||
@Module({ | ||
imports: [ | ||
MongooseModule.forFeature([ | ||
{ name: Folder.name, schema: FolderSchema }, | ||
{ name: AIClassification.name, schema: PostAIClassificationSchema }, | ||
{ name: Post.name, schema: PostSchema }, | ||
]), | ||
], | ||
controllers: [ClassificationController], | ||
providers: [ClassificationService], | ||
}) | ||
export class ClassificationModule {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { ClassificationService } from './classification.service'; | ||
|
||
describe('ClassificationService', () => { | ||
let service: ClassificationService; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [ClassificationService], | ||
}).compile(); | ||
|
||
service = module.get<ClassificationService>(ClassificationService); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(service).toBeDefined(); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,58 @@ | ||||||||||
import { Injectable } from '@nestjs/common'; | ||||||||||
import { | ||||||||||
Folder, | ||||||||||
Post, | ||||||||||
AIClassification, | ||||||||||
} from '@src/infrastructure/database/schema'; | ||||||||||
|
||||||||||
import { InjectModel } from '@nestjs/mongoose'; | ||||||||||
import { Model, Types } from 'mongoose'; | ||||||||||
import { AIFolderNameServiceDto } from './dto/getAIFolderNameLIst.dto'; | ||||||||||
import { AIPostServiceDto } from './dto/getAIPostList.dto'; | ||||||||||
|
||||||||||
@Injectable() | ||||||||||
export class ClassificationService { | ||||||||||
constructor( | ||||||||||
@InjectModel(Folder.name) private folderModel: Model<Folder>, | ||||||||||
@InjectModel(AIClassification.name) | ||||||||||
private postAiClassificationModel: Model<AIClassification>, | ||||||||||
@InjectModel(Post.name) private postModel: Model<Post>, | ||||||||||
) {} | ||||||||||
|
||||||||||
async getFolderNameList(userId: String): Promise<AIFolderNameServiceDto[]> { | ||||||||||
const folders = await this.folderModel.find({ userId }).exec(); | ||||||||||
const folderIds = folders.map((folder) => folder._id); | ||||||||||
Comment on lines
+23
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. **(/¯◡‿◡)/¯✧·˚ : *✧·˚ : *** https://www.zerocho.com/category/MongoDB/post/59bd148b1474c800194b695a
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오호 땡큐 |
||||||||||
|
||||||||||
const classificationIds = await this.postAiClassificationModel | ||||||||||
.distinct('suggestedFolderId') | ||||||||||
.where('suggestedFolderId') | ||||||||||
.in(folderIds) | ||||||||||
.exec(); | ||||||||||
|
||||||||||
const matchedFolders = await this.folderModel | ||||||||||
.find({ _id: { $in: classificationIds } }) | ||||||||||
.exec(); | ||||||||||
|
||||||||||
return matchedFolders.map((folder) => new AIFolderNameServiceDto(folder)); | ||||||||||
J-Hoplin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
} | ||||||||||
|
||||||||||
async getPostList( | ||||||||||
userId: string, | ||||||||||
folderId: string, | ||||||||||
): Promise<AIPostServiceDto[]> { | ||||||||||
const posts = await this.postModel | ||||||||||
.find({ userId: userId }) | ||||||||||
.populate<{ | ||||||||||
aiClassificationId: AIClassification; | ||||||||||
}>({ | ||||||||||
path: 'aiClassificationId', | ||||||||||
match: { deletedAt: null, suggestedFolderId: folderId }, | ||||||||||
}) | ||||||||||
.sort({ createdAt: -1 }) | ||||||||||
.exec(); | ||||||||||
|
||||||||||
return posts | ||||||||||
.filter((post) => post.aiClassificationId) | ||||||||||
.map((post) => new AIPostServiceDto(post, post.aiClassificationId)); | ||||||||||
} | ||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { applyDecorators } from '@nestjs/common'; | ||
import { ApiTags } from '@nestjs/swagger'; | ||
|
||
export const ClassificationControllerDocs = applyDecorators( | ||
ApiTags('AI classification API'), | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { applyDecorators } from '@nestjs/common'; | ||
import { ApiBearerAuth, ApiOperation, ApiResponse } from '@nestjs/swagger'; | ||
import { AIFolderNameListResponse } from '../response/ai-folder-list.dto'; | ||
|
||
export const GetAIFolderNameListDocs = applyDecorators( | ||
ApiOperation({ | ||
summary: '폴더 리스트', | ||
description: 'AI 분류 폴더 리스트.', | ||
}), | ||
ApiResponse({ | ||
type: AIFolderNameListResponse, | ||
}), | ||
ApiBearerAuth(), | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { applyDecorators } from '@nestjs/common'; | ||
import { ApiBearerAuth, ApiOperation, ApiResponse } from '@nestjs/swagger'; | ||
import { AIPostListResponse } from '../response/ai-post-list.dto'; | ||
|
||
export const GetAIPostListDocs = applyDecorators( | ||
ApiOperation({ | ||
summary: '폴더 안에 들어있는 Post(링크) 리스트', | ||
description: 'AI 분류 추천된 링크 리스트.', | ||
}), | ||
ApiResponse({ | ||
type: AIPostListResponse, | ||
}), | ||
ApiBearerAuth(), | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * from './controller.docs'; | ||
export * from './getAIFolderNameList.docs'; | ||
export * from './getAIPostList.docs'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { ApiProperty } from '@nestjs/swagger'; | ||
import { FolderDocument } from '@src/infrastructure'; | ||
import { IsNotEmpty, IsString } from 'class-validator'; | ||
|
||
export class AIFolderNameServiceDto { | ||
id: string; | ||
|
||
name: string; | ||
|
||
constructor(data: FolderDocument) { | ||
this.id = data._id.toString(); | ||
this.name = data.name; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { ApiProperty } from '@nestjs/swagger'; | ||
import { IsNotEmpty, IsString, IsArray, IsDate } from 'class-validator'; | ||
import { Type } from 'class-transformer'; | ||
import { Post, AIClassification, PostDocument } from '@src/infrastructure'; | ||
import { Document } from 'mongoose'; | ||
import { MergeType } from 'mongoose'; | ||
import { Types } from 'mongoose'; | ||
|
||
export class AIPostServiceDto { | ||
id: string; | ||
|
||
title: string; | ||
|
||
url: string; | ||
|
||
description: string; | ||
|
||
keywords: string[]; | ||
|
||
createdAt: Date; | ||
|
||
constructor(post: PostDocument, aiClassificationId: AIClassification) { | ||
this.id = post.id; | ||
this.title = post.title; | ||
this.url = post.url; | ||
this.description = post.description; | ||
this.keywords = aiClassificationId.keywords; | ||
this.createdAt = aiClassificationId.createdAt; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { ApiProperty } from '@nestjs/swagger'; | ||
import { AIFolderNameServiceDto } from '../dto/getAIFolderNameLIst.dto'; | ||
|
||
export class AIFolderNameListResponse { | ||
@ApiProperty({ | ||
type: AIFolderNameServiceDto, | ||
isArray: true, | ||
}) | ||
list: AIFolderNameServiceDto[]; | ||
|
||
constructor(data: AIFolderNameServiceDto[]) { | ||
this.list = data; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { ApiProperty } from '@nestjs/swagger'; | ||
import { Type } from 'class-transformer'; | ||
import { AIPostServiceDto } from '../dto/getAIPostList.dto'; | ||
|
||
export class AIPostListResponse { | ||
@ApiProperty({ type: [AIPostServiceDto] }) | ||
@Type(() => AIPostServiceDto) | ||
list: AIPostServiceDto[]; | ||
|
||
constructor(data: AIPostServiceDto[]) { | ||
this.list = data; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요거 내가 folderRepo에 findByUserId라는 메소드로 만들어놨으~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오빠꺼 아직 머지 안되서 머지되면 수정할께!