From 76fc6b586f5bc657729fe5c58f39bea09a6c1fd2 Mon Sep 17 00:00:00 2001 From: Bruno Cruz Date: Tue, 14 Jan 2025 22:50:43 -0300 Subject: [PATCH 1/6] feat: New controller, service, dtos and module for books. Co-authored-by: Guilherme Costa Zanella --- src/books/books.controller.ts | 13 +++++++++++++ src/books/books.module.ts | 9 +++++++++ src/books/books.service.ts | 29 +++++++++++++++++++++++++++++ src/books/dtos/searchBooks.dto.ts | 25 +++++++++++++++++++++++++ 4 files changed, 76 insertions(+) create mode 100644 src/books/books.controller.ts create mode 100644 src/books/books.module.ts create mode 100644 src/books/books.service.ts create mode 100644 src/books/dtos/searchBooks.dto.ts diff --git a/src/books/books.controller.ts b/src/books/books.controller.ts new file mode 100644 index 0000000..0f482ce --- /dev/null +++ b/src/books/books.controller.ts @@ -0,0 +1,13 @@ +import { Controller, Get, Query } from '@nestjs/common'; +import { BooksService } from './books.service'; +import { SearchBooksDto } from './dtos/searchBooks.dto'; + +@Controller('books') +export class BooksController { + constructor(private readonly booksService: BooksService) {} + + @Get('search') + async searchBooks(@Query() searchParams: SearchBooksDto) { + return this.booksService.searchBooks(searchParams); + } +} diff --git a/src/books/books.module.ts b/src/books/books.module.ts new file mode 100644 index 0000000..392162b --- /dev/null +++ b/src/books/books.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { BooksController } from './books.controller'; +import { BooksService } from './books.service'; + +@Module({ + controllers: [BooksController], + providers: [BooksService], +}) +export class BooksModule {} diff --git a/src/books/books.service.ts b/src/books/books.service.ts new file mode 100644 index 0000000..71fd1ea --- /dev/null +++ b/src/books/books.service.ts @@ -0,0 +1,29 @@ +import { Injectable } from '@nestjs/common'; +import { SearchBooksDto } from './dtos/searchBooks.dto'; + +@Injectable() +export class BooksService { + async searchBooks(searchParams: SearchBooksDto) { + const { title, author, theme, page, limit } = searchParams; + const offset = (page - 1) * limit; + + const filters: any = {}; + /* Filtros Exemplo em MondoDb + if (title) filters.title = { $regex: new RegExp(title, 'i') }; + if (author) filters.author = { $regex: new RegExp(author, 'i') }; + if (theme) filters.theme = { $regex: new RegExp(theme, 'i') }; + */ + + const results = []; + const totalResults = 0; + if (results.length === 0) { + return { + message: 'Nenhum livro encontrado para a pesquisa realizada. Tente outros termos.', + totalPages: 0, + currentPage: page, + results: [], + }; + } + + } +} \ No newline at end of file diff --git a/src/books/dtos/searchBooks.dto.ts b/src/books/dtos/searchBooks.dto.ts new file mode 100644 index 0000000..cb41129 --- /dev/null +++ b/src/books/dtos/searchBooks.dto.ts @@ -0,0 +1,25 @@ +import { IsOptional, IsString, IsInt, Min } from 'class-validator'; + +export class SearchBooksDto { + @IsOptional() + @IsString() + title?: string; + + @IsOptional() + @IsString() + author?: string; + + @IsOptional() + @IsString() + theme?: string; + + @IsOptional() + @IsInt() + @Min(1) + page: number = 1; + + @IsOptional() + @IsInt() + @Min(1) + limit: number = 20; +} From 64f67e58839676759b0e00c9a3d90152f42a9643 Mon Sep 17 00:00:00 2001 From: Bruno Cruz Date: Sat, 18 Jan 2025 12:36:40 -0300 Subject: [PATCH 2/6] feat: change on modules --- src/app.module.ts | 2 ++ src/books/books.module.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/app.module.ts b/src/app.module.ts index c0f9ca1..e8c5683 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -3,6 +3,7 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { UsersModule } from './users/users.module'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { AuthModule } from './auth/auth.module'; +import { BooksModule } from './books/books.module'; import typeormConfig from './database/config'; @Module({ @@ -18,6 +19,7 @@ import typeormConfig from './database/config'; }), UsersModule, AuthModule, + BooksModule ], controllers: [], providers: [], diff --git a/src/books/books.module.ts b/src/books/books.module.ts index 392162b..cc1dc3b 100644 --- a/src/books/books.module.ts +++ b/src/books/books.module.ts @@ -1,8 +1,10 @@ import { Module } from '@nestjs/common'; import { BooksController } from './books.controller'; import { BooksService } from './books.service'; +import { TypeOrmModule } from '@nestjs/typeorm'; @Module({ + imports: [TypeOrmModule.forFeature([])], controllers: [BooksController], providers: [BooksService], }) From 0ded97bcf7017e6380060293337408d3a741f6fe Mon Sep 17 00:00:00 2001 From: Bruno Cruz Date: Sat, 18 Jan 2025 16:05:43 -0300 Subject: [PATCH 3/6] feat: entity book created --- src/books/books.module.ts | 3 +- src/books/books.service.ts | 57 ++++++++++++++++++++-------- src/database/entities/book.entity.ts | 22 +++++++++++ 3 files changed, 66 insertions(+), 16 deletions(-) create mode 100644 src/database/entities/book.entity.ts diff --git a/src/books/books.module.ts b/src/books/books.module.ts index cc1dc3b..09b0114 100644 --- a/src/books/books.module.ts +++ b/src/books/books.module.ts @@ -2,9 +2,10 @@ import { Module } from '@nestjs/common'; import { BooksController } from './books.controller'; import { BooksService } from './books.service'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { Book } from '../database/entities/book.entity'; @Module({ - imports: [TypeOrmModule.forFeature([])], + imports: [TypeOrmModule.forFeature([Book])], controllers: [BooksController], providers: [BooksService], }) diff --git a/src/books/books.service.ts b/src/books/books.service.ts index 71fd1ea..ad3f7b9 100644 --- a/src/books/books.service.ts +++ b/src/books/books.service.ts @@ -1,29 +1,56 @@ import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { Book } from '../database/entities/book.entity'; import { SearchBooksDto } from './dtos/searchBooks.dto'; @Injectable() export class BooksService { + constructor( + @InjectRepository(Book) + private booksRepository: Repository, + ) { } + async searchBooks(searchParams: SearchBooksDto) { - const { title, author, theme, page, limit } = searchParams; + let { title, author, theme, page, limit } = searchParams; + + // Garantir que os valores de page e limit sejam números + page = parseInt(page as any, 10) || 1; // Se não for um número, definir como 1 + limit = parseInt(limit as any, 10) || 10; // Se não for um número, definir como 10 const offset = (page - 1) * limit; const filters: any = {}; - /* Filtros Exemplo em MondoDb - if (title) filters.title = { $regex: new RegExp(title, 'i') }; - if (author) filters.author = { $regex: new RegExp(author, 'i') }; - if (theme) filters.theme = { $regex: new RegExp(theme, 'i') }; - */ - - const results = []; - const totalResults = 0; - if (results.length === 0) { + if (title) filters.title = title; + if (author) filters.author = author; + if (theme) filters.theme = theme; + + try { + const [books, totalBooks] = await this.booksRepository.findAndCount({ + where: filters, + skip: offset, + take: limit, + }); + + const totalPages = Math.ceil(totalBooks / limit); + + if (books.length === 0) { + return { + message: 'Nenhum livro encontrado para a pesquisa realizada. Tente outros termos.', + totalPages: 0, + currentPage: page, + results: [], + }; + } + return { - message: 'Nenhum livro encontrado para a pesquisa realizada. Tente outros termos.', - totalPages: 0, + message: 'Livros encontrados com sucesso!', + totalPages, currentPage: page, - results: [], + results: books, }; + } catch (error) { + console.error(error); + throw new Error('Erro ao realizar a busca no banco de dados'); } - } -} \ No newline at end of file +} diff --git a/src/database/entities/book.entity.ts b/src/database/entities/book.entity.ts new file mode 100644 index 0000000..8c92732 --- /dev/null +++ b/src/database/entities/book.entity.ts @@ -0,0 +1,22 @@ +import { + Column, + CreateDateColumn, + Entity, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; + +@Entity() +export class Book { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column() + title: string; + + @Column() + author: string; + + @Column() + theme: string; +} From b85e25ddf9259b485ae9897862b2f27216f2d058 Mon Sep 17 00:00:00 2001 From: Bruno Cruz Date: Sun, 19 Jan 2025 15:30:27 -0300 Subject: [PATCH 4/6] feat: order by average rating and title --- src/books/books.service.ts | 9 ++++++--- src/database/entities/book.entity.ts | 7 +++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/books/books.service.ts b/src/books/books.service.ts index ad3f7b9..ce6314a 100644 --- a/src/books/books.service.ts +++ b/src/books/books.service.ts @@ -14,9 +14,8 @@ export class BooksService { async searchBooks(searchParams: SearchBooksDto) { let { title, author, theme, page, limit } = searchParams; - // Garantir que os valores de page e limit sejam números - page = parseInt(page as any, 10) || 1; // Se não for um número, definir como 1 - limit = parseInt(limit as any, 10) || 10; // Se não for um número, definir como 10 + page = parseInt(page as any, 10) || 1; + limit = parseInt(limit as any, 10) || 10; const offset = (page - 1) * limit; const filters: any = {}; @@ -29,6 +28,10 @@ export class BooksService { where: filters, skip: offset, take: limit, + order:{ + averageRating : 'DESC', + title: 'ASC', + } }); const totalPages = Math.ceil(totalBooks / limit); diff --git a/src/database/entities/book.entity.ts b/src/database/entities/book.entity.ts index 8c92732..4ce9baa 100644 --- a/src/database/entities/book.entity.ts +++ b/src/database/entities/book.entity.ts @@ -19,4 +19,11 @@ export class Book { @Column() theme: string; + + @Column({ type: 'decimal', precision: 2, scale: 1, default: 0 }) + averageRating: number; // Média de avaliação dos usuários (0.0 a 5.0) + + @Column({ nullable: true }) + coverUrl: string; // URL da capa do livro + } From 0c5cc3e5a21e19559682f9b190cd4619affc064c Mon Sep 17 00:00:00 2001 From: Bruno Cruz Date: Sun, 19 Jan 2025 15:37:48 -0300 Subject: [PATCH 5/6] feat: ensure consistent return type for book search results --- src/books/books.service.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/books/books.service.ts b/src/books/books.service.ts index ce6314a..b17e6a7 100644 --- a/src/books/books.service.ts +++ b/src/books/books.service.ts @@ -4,6 +4,14 @@ import { Repository } from 'typeorm'; import { Book } from '../database/entities/book.entity'; import { SearchBooksDto } from './dtos/searchBooks.dto'; + +interface SearchBooksResult { + message: string; + totalPages: number; + currentPage: number; + results: Book[]; +} + @Injectable() export class BooksService { constructor( @@ -11,7 +19,7 @@ export class BooksService { private booksRepository: Repository, ) { } - async searchBooks(searchParams: SearchBooksDto) { + async searchBooks(searchParams: SearchBooksDto): Promise { let { title, author, theme, page, limit } = searchParams; page = parseInt(page as any, 10) || 1; From 4b99d088ab14004912a467c16d325f3e40474fb6 Mon Sep 17 00:00:00 2001 From: Bruno Cruz Date: Sun, 19 Jan 2025 16:45:33 -0300 Subject: [PATCH 6/6] feat: new column enum --- src/books/books.service.ts | 10 +--------- src/database/entities/book.entity.ts | 12 ++++++++++++ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/books/books.service.ts b/src/books/books.service.ts index b17e6a7..ce6314a 100644 --- a/src/books/books.service.ts +++ b/src/books/books.service.ts @@ -4,14 +4,6 @@ import { Repository } from 'typeorm'; import { Book } from '../database/entities/book.entity'; import { SearchBooksDto } from './dtos/searchBooks.dto'; - -interface SearchBooksResult { - message: string; - totalPages: number; - currentPage: number; - results: Book[]; -} - @Injectable() export class BooksService { constructor( @@ -19,7 +11,7 @@ export class BooksService { private booksRepository: Repository, ) { } - async searchBooks(searchParams: SearchBooksDto): Promise { + async searchBooks(searchParams: SearchBooksDto) { let { title, author, theme, page, limit } = searchParams; page = parseInt(page as any, 10) || 1; diff --git a/src/database/entities/book.entity.ts b/src/database/entities/book.entity.ts index 4ce9baa..b5979f1 100644 --- a/src/database/entities/book.entity.ts +++ b/src/database/entities/book.entity.ts @@ -1,3 +1,4 @@ +import { HttpStatus } from '@nestjs/common'; import { Column, CreateDateColumn, @@ -6,6 +7,11 @@ import { UpdateDateColumn, } from 'typeorm'; +export enum Status { + AVAILABLE = "available", + NOTAVAILABLE = "notAvailable", +} + @Entity() export class Book { @PrimaryGeneratedColumn('uuid') @@ -26,4 +32,10 @@ export class Book { @Column({ nullable: true }) coverUrl: string; // URL da capa do livro + @Column({ + type: "enum", + enum: Status, + default: Status.AVAILABLE, + }) + role: Status; }