From 737bc7d9369c56ad64cd89cb4252d27c017605c9 Mon Sep 17 00:00:00 2001 From: "Marcio J. Costa" <87935294+MarcioCosta013@users.noreply.github.com> Date: Sat, 13 Dec 2025 01:27:30 -0300 Subject: [PATCH 01/13] Update CI workflow to use JDK 21 and develop branch --- .github/workflows/ci-develop.yml | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/ci-develop.yml diff --git a/.github/workflows/ci-develop.yml b/.github/workflows/ci-develop.yml new file mode 100644 index 0000000..3fbfeb4 --- /dev/null +++ b/.github/workflows/ci-develop.yml @@ -0,0 +1,33 @@ +# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: CI Develop + +on: + push: + branches: [ "develop" ] + pull_request: + branches: [ "develop" ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: maven + - name: Run Maven verify (tests + Spotless + SpotBugs) + run: mvn -B clean verifyl From 5e97d220268c9ea9e6fb5ef58c578ac5e97c545c Mon Sep 17 00:00:00 2001 From: "Marcio J. Costa" <87935294+MarcioCosta013@users.noreply.github.com> Date: Sat, 13 Dec 2025 02:42:52 -0300 Subject: [PATCH 02/13] Add CD workflow for main branch with Docker build --- .github/workflows/cd-main.yml | 37 +++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/cd-main.yml diff --git a/.github/workflows/cd-main.yml b/.github/workflows/cd-main.yml new file mode 100644 index 0000000..30dcacd --- /dev/null +++ b/.github/workflows/cd-main.yml @@ -0,0 +1,37 @@ +name: CD main (Prod) + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: maven + + - name: Run Maven verify (tests + Spotless + SpotBugs) + run: mvn -B clean verify + + docker: + runs-on: ubuntu-latest + needs: build + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t vemprofut-api:latest . + + From 4bf034a5da9b1b57309353b7563cb80ea327b891 Mon Sep 17 00:00:00 2001 From: "Marcio J. Costa" <87935294+MarcioCosta013@users.noreply.github.com> Date: Sat, 13 Dec 2025 03:04:22 -0300 Subject: [PATCH 03/13] Rename cd-main.yml to cicd-main.yml adicionei as secrets --- .github/workflows/{cd-main.yml => cicd-main.yml} | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) rename .github/workflows/{cd-main.yml => cicd-main.yml} (61%) diff --git a/.github/workflows/cd-main.yml b/.github/workflows/cicd-main.yml similarity index 61% rename from .github/workflows/cd-main.yml rename to .github/workflows/cicd-main.yml index 30dcacd..e76da07 100644 --- a/.github/workflows/cd-main.yml +++ b/.github/workflows/cicd-main.yml @@ -1,4 +1,4 @@ -name: CD main (Prod) +name: CI/CD Main (Prod) on: push: @@ -31,7 +31,16 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: Log in to Docker Hub + run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin + - name: Build Docker image run: docker build -t vemprofut-api:latest . - + - name: Tag Docker image + run: docker tag vemprofut-api:latest ${{ secrets.DOCKER_USERNAME }}/vemprofut-api:latest + + - name: Push Docker image + run: docker push ${{ secrets.DOCKER_USERNAME }}/vemprofut-api:latest + + From 69738fe112714fbc3cbb7a278edacf9a99164538 Mon Sep 17 00:00:00 2001 From: "Marcio J. Costa" <87935294+MarcioCosta013@users.noreply.github.com> Date: Sat, 13 Dec 2025 03:17:22 -0300 Subject: [PATCH 04/13] Specify working directory for Maven verify Set working directory for Maven verify step. --- .github/workflows/cicd-main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cicd-main.yml b/.github/workflows/cicd-main.yml index e76da07..bb8800a 100644 --- a/.github/workflows/cicd-main.yml +++ b/.github/workflows/cicd-main.yml @@ -23,6 +23,7 @@ jobs: - name: Run Maven verify (tests + Spotless + SpotBugs) run: mvn -B clean verify + working-directory: ./VemProFutApi docker: runs-on: ubuntu-latest From dc5851f817d78809717fa2f7fe7b9a8ac6898c29 Mon Sep 17 00:00:00 2001 From: "Marcio J. Costa" <87935294+MarcioCosta013@users.noreply.github.com> Date: Sat, 13 Dec 2025 21:59:16 -0300 Subject: [PATCH 05/13] Change Docker build context to VemProFutApi directory (#7) --- .github/workflows/cicd-main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd-main.yml b/.github/workflows/cicd-main.yml index bb8800a..ad14fa4 100644 --- a/.github/workflows/cicd-main.yml +++ b/.github/workflows/cicd-main.yml @@ -36,7 +36,7 @@ jobs: run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin - name: Build Docker image - run: docker build -t vemprofut-api:latest . + run: docker build -t vemprofut-api:latest ./VemProFutApi - name: Tag Docker image run: docker tag vemprofut-api:latest ${{ secrets.DOCKER_USERNAME }}/vemprofut-api:latest From e8d0f28e4b9bbc95c3c86886ee54925a1cd6be6f Mon Sep 17 00:00:00 2001 From: "Marcio J. Costa" <87935294+MarcioCosta013@users.noreply.github.com> Date: Sat, 13 Dec 2025 23:31:37 -0300 Subject: [PATCH 06/13] Fix Maven command to run Spotless and verify --- .github/workflows/ci-develop.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-develop.yml b/.github/workflows/ci-develop.yml index 3fbfeb4..f2ef092 100644 --- a/.github/workflows/ci-develop.yml +++ b/.github/workflows/ci-develop.yml @@ -30,4 +30,5 @@ jobs: distribution: 'temurin' cache: maven - name: Run Maven verify (tests + Spotless + SpotBugs) - run: mvn -B clean verifyl + run: mvn spotless:apply && mvn -B clean verify + working-directory: ./VemProFutApi From 41ce06b138091ed2f889aea50dcf826b6a4558ef Mon Sep 17 00:00:00 2001 From: "Marcio J. Costa" <87935294+MarcioCosta013@users.noreply.github.com> Date: Sat, 13 Dec 2025 23:33:07 -0300 Subject: [PATCH 07/13] Update CI workflow to apply Spotless before verify --- .github/workflows/cicd-main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd-main.yml b/.github/workflows/cicd-main.yml index ad14fa4..2d79829 100644 --- a/.github/workflows/cicd-main.yml +++ b/.github/workflows/cicd-main.yml @@ -22,7 +22,7 @@ jobs: cache: maven - name: Run Maven verify (tests + Spotless + SpotBugs) - run: mvn -B clean verify + run: mvn spotless:apply && mvn -B clean verify working-directory: ./VemProFutApi docker: From b1b028ba30dbce5ee1231b81cf04e63eaeea02f4 Mon Sep 17 00:00:00 2001 From: "Marcio J. Costa" <87935294+MarcioCosta013@users.noreply.github.com> Date: Sat, 13 Dec 2025 23:43:11 -0300 Subject: [PATCH 08/13] Develop merge (#11) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feature/implement test repository (#5) * test(BaseIntegrationTest): Criando a Base para testes usando conteiner Mysql para ser o mais semelhante possivel com o de producao * feat(Dockenizando): Dockerzinado a aplicacao para está preparada para subir para prod * feat(BaseIntegrationTest): Iniciando a implementacao dos testes usando o testconteiner com mysql * fix(testconteiner/docker): resolvendo erro de comunicacao do testconteine com o docker * feat(RepositoryTest):Refactorando o banco de dados de Testconteiner para H2 em memoria e criando os testes de Peladeiro, Fut,Banimento e Cartoes repositories * test(organizando diretorios de pastas): Separando os testes unitarios dos testes de intregracao e etc * test(organizando diretorios): alterei os nomes dos testes de integracao para o final IT; configurei o spotbugs para verificar os codigos antes de enviar o PR; add arquivo de filtro do spotbugs; * refactor:(mapeando DTOS): -Mapeando os atributos dos DTOs para os models; -Ignorando os atributos de models que nao vao ser usados nos DTOs; -alterei os nomes dos atributos nos DTOs para ficarem iguais ao da classe model para facilitaro mapeamento; -refatorei os locais onde usava o nome antigo dos atributos dos DTOs para o nome novo; * Feature/implement test repository (#10) * test(BaseIntegrationTest): Criando a Base para testes usando conteiner Mysql para ser o mais semelhante possivel com o de producao * feat(Dockenizando): Dockerzinado a aplicacao para está preparada para subir para prod * feat(BaseIntegrationTest): Iniciando a implementacao dos testes usando o testconteiner com mysql * fix(testconteiner/docker): resolvendo erro de comunicacao do testconteine com o docker * feat(RepositoryTest):Refactorando o banco de dados de Testconteiner para H2 em memoria e criando os testes de Peladeiro, Fut,Banimento e Cartoes repositories * test(organizando diretorios de pastas): Separando os testes unitarios dos testes de intregracao e etc * test(organizando diretorios): alterei os nomes dos testes de integracao para o final IT; configurei o spotbugs para verificar os codigos antes de enviar o PR; add arquivo de filtro do spotbugs; * refactor:(mapeando DTOS): -Mapeando os atributos dos DTOs para os models; -Ignorando os atributos de models que nao vao ser usados nos DTOs; -alterei os nomes dos atributos nos DTOs para ficarem iguais ao da classe model para facilitaro mapeamento; -refatorei os locais onde usava o nome antigo dos atributos dos DTOs para o nome novo; --- VemProFutApi/Docker-compose.yml | 52 ++- VemProFutApi/Dockerfile | 8 +- VemProFutApi/excludeFilter.xml | 35 ++ VemProFutApi/mysql-init/init.sql | 8 + VemProFutApi/pom.xml | 41 +- .../configs/OAuth2LoginSuccessHandler.java | 3 +- .../vemprofut/controllers/FutController.java | 58 ++- .../controllers/PeladeiroController.java | 20 +- .../request/SaveFutRequestDTO.java | 2 +- .../response/CartoesResponseDTO.java | 2 +- .../response/SavePeladeiroResponseDTO.java | 2 +- .../response/UpdatePeladeiroResponseDTO.java | 3 +- .../com/vemprofut/mappers/ICartoesMapper.java | 3 + .../br/com/vemprofut/mappers/IFutMapper.java | 10 + .../vemprofut/mappers/IMappersDefault.java | 19 +- .../vemprofut/mappers/IPeladeiroMapper.java | 17 + .../com/vemprofut/models/BanimentoModel.java | 15 +- .../br/com/vemprofut/models/CartoesModel.java | 10 + .../models/DTOs/CartaoCountProjection.java | 1 + .../com/vemprofut/models/DTOs/CartoesDTO.java | 3 +- .../com/vemprofut/models/DTOs/EditorDTO.java | 2 +- .../br/com/vemprofut/models/DTOs/FutDTO.java | 2 +- .../br/com/vemprofut/models/EditorModel.java | 5 + .../br/com/vemprofut/models/FutModel.java | 17 + .../com/vemprofut/models/PeladeiroModel.java | 21 +- .../repositories/BanimentoRepository.java | 8 +- .../repositories/CartoesRepository.java | 8 +- .../repositories/EditorRepository.java | 2 +- .../services/implementacao/FutService.java | 2 +- .../services/query/ICartoesQueryService.java | 6 - .../implementacao/BanimentoQueryService.java | 8 +- .../implementacao/CartoesQueryService.java | 22 +- .../main/resources/application-dev.properties | 60 ++- .../resources/application-prod.properties | 39 ++ .../src/main/resources/application.properties | 61 --- ..._table_partidas_contrains_fk_partidas2.sql | 4 +- ...__alter_table_gols_constraint_fk_gols2.sql | 4 +- ...__alter_table_gols_constraint_fk_gols3.sql | 4 + ...200__alter_table_gols_constraint_gols3,sql | 4 - ...articipapeladeiro_contraint_participa1.sql | 2 +- ...icipapeladeiro_contraint_fk_participa2.sql | 2 +- ...er_table_estapeladeio_constraint_esta1.sql | 2 +- ...able_estapeladeiro_constraint_fk_esta2.sql | 2 +- ...ter_table_editor_constraint_fk_editor1.sql | 2 +- ...ter_table_editor_constraint_fk_editor2.sql | 2 +- ...2511241710__alter_table_fk_banimento_2.sql | 2 +- ...2511241712__alter_table_fk_banimento_3.sql | 2 +- .../vemprofut/VemProFutApplicationTests.java | 12 - .../repositories/BanimentoRepositoryIT.java | 152 +++++++ .../repositories/CartoesRepositoryIT.java | 424 ++++++++++++++++++ .../repositories/EditorRepositoryIT.java | 90 ++++ .../repositories/FutRepositoryIT.java | 78 ++++ .../repositories/PeladeiroRepositoryIT.java | 147 ++++++ .../resources/application-test.properties | 12 + 54 files changed, 1325 insertions(+), 197 deletions(-) create mode 100644 VemProFutApi/excludeFilter.xml create mode 100644 VemProFutApi/mysql-init/init.sql create mode 100644 VemProFutApi/src/main/resources/application-prod.properties create mode 100644 VemProFutApi/src/main/resources/db/migration/V202511092200__alter_table_gols_constraint_fk_gols3.sql delete mode 100644 VemProFutApi/src/main/resources/db/migration/V202511092200__alter_table_gols_constraint_gols3,sql delete mode 100644 VemProFutApi/src/test/java/br/com/vemprofut/VemProFutApplicationTests.java create mode 100644 VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/BanimentoRepositoryIT.java create mode 100644 VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/CartoesRepositoryIT.java create mode 100644 VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/EditorRepositoryIT.java create mode 100644 VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/FutRepositoryIT.java create mode 100644 VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/PeladeiroRepositoryIT.java create mode 100644 VemProFutApi/src/test/resources/application-test.properties diff --git a/VemProFutApi/Docker-compose.yml b/VemProFutApi/Docker-compose.yml index 27b2827..8f6b081 100644 --- a/VemProFutApi/Docker-compose.yml +++ b/VemProFutApi/Docker-compose.yml @@ -1,32 +1,62 @@ services: # Serviço do Banco de Dados MySQL db: - image: mysql:8.0 + image: mysql:8.0.32 container_name: vemprofut-db restart: always ports: - - "3306:3306" + - "3307:3306" + env_file: + - .env environment: - MYSQL_DATABASE: ${MYSQL_DATABASE} - MYSQL_USER: ${MYSQL_USER} - MYSQL_PASSWORD: ${MYSQL_PASSWORD} MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: ${MYSQL_DATABASE} volumes: - vemprofut-db-data:/var/lib/mysql + - ./mysql-init:/docker-entrypoint-initdb.d # criar o usuario automaticamente dentro do container MySQL + + # 👇 Healthcheck para garantir que o MySQL está pronto + healthcheck: + test: ["CMD-SHELL", "mysqladmin ping -h localhost -uroot -p$MYSQL_ROOT_PASSWORD"] + interval: 10s + timeout: 5s + retries: 15 + start_period: 30s # Serviço da API VemProFut api: build: . container_name: vemprofut-api + env_file: + - .env restart: on-failure + depends_on: + db: + condition: service_healthy ports: - "8080:8080" - depends_on: - - db environment: - SPRING_DATASOURCE_URL: ${SPRING_DATASOURCE_URL} - SPRING_DATASOURCE_USERNAME: ${MYSQL_USER} - SPRING_DATASOURCE_PASSWORD: ${MYSQL_PASSWORD} + SPRING_PROFILES_ACTIVE: prod + + # Conexão com MySQL com URL do banco dentro da rede Docker + SPRING_DATASOURCE_USERNAME: ${SPRING_DATASOURCE_USERNAME} + SPRING_DATASOURCE_PASSWORD: ${MYSQL_ROOT_PASSWORD} + SPRING_DATASOURCE_URL: jdbc:mysql://db:3306/${MYSQL_DATABASE}?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC&connectTimeout=30000&socketTimeout=60000 + + # Flyway - tenta reconectar várias vezes enquanto MySQL inicializa + SPRING_FLYWAY_CONNECT_RETRIES: 10 + SPRING_FLYWAY_CONNECT_RETRIES_INTERVAL: 5s + + # Auth0 - Dessa forma, o Spring vai ler direto do ambiente, sem precisar de '.properties' para mapear + SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_AUTH0_CLIENT_ID: ${AUTH0_CLIENT_ID} + SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_AUTH0_CLIENT_SECRET: ${AUTH0_CLIENT_SECRET} + SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_AUTH0_ISSUER_URI: ${AUTH0_ISSUER} + SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: ${AUTH0_ISSUER} + + # Forca Spring Boot a ouvir em todas interfaces (necessario para Docker) + SERVER_ADDRESS: 0.0.0.0 + SERVER_PORT: 8080 + SERVER_SERVLET_SESSION_COOKIE_SECURE: false volumes: - vemprofut-db-data: # garante que os dados do banco de dados não sejam perdidos quando o contêiner do MySQL for reiniciado. + vemprofut-db-data: # garante que os dados do DB não sejam perdidos quando o contêiner do MySQL for reiniciado. diff --git a/VemProFutApi/Dockerfile b/VemProFutApi/Dockerfile index 1faa1cc..8f0609d 100644 --- a/VemProFutApi/Dockerfile +++ b/VemProFutApi/Dockerfile @@ -1,3 +1,4 @@ +# ===================== Builder (Maven + JDK 21) ======================== # Utilizei uma abordagem de múltiplos estágios (multi-stage build), que gera uma imagem final muito menor e mais segura. # Estágio 1: Build da aplicação com Maven e JDK 21 FROM maven:3.9-eclipse-temurin-21 AS builder @@ -11,11 +12,16 @@ RUN mvn dependency:go-offline # Copia o restante do código-fonte e constrói o JAR COPY src ./src -RUN mvn clean install -DskipTests +RUN mvn clean package -DskipTests +#package mais adequado que install quando construindo imagem... + +# ===================== Runtime (JRE 21) ========================== # Estágio 2: Criação da imagem final com JRE FROM eclipse-temurin:21-jre-jammy WORKDIR /app + COPY --from=builder /app/target/VemProFut-0.0.1-SNAPSHOT.jar app.jar + EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"] diff --git a/VemProFutApi/excludeFilter.xml b/VemProFutApi/excludeFilter.xml new file mode 100644 index 0000000..64356c8 --- /dev/null +++ b/VemProFutApi/excludeFilter.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/VemProFutApi/mysql-init/init.sql b/VemProFutApi/mysql-init/init.sql new file mode 100644 index 0000000..4f8138c --- /dev/null +++ b/VemProFutApi/mysql-init/init.sql @@ -0,0 +1,8 @@ +-- Cria o usuário +CREATE USER IF NOT EXISTS 'vpfadm'@'%' IDENTIFIED BY 'root123'; + +-- Dá permissões no banco +GRANT ALL PRIVILEGES ON vemprofutdb.* TO 'vpfadm'@'%'; + +-- Atualiza permissões +FLUSH PRIVILEGES; \ No newline at end of file diff --git a/VemProFutApi/pom.xml b/VemProFutApi/pom.xml index 6399b1a..fa31b61 100644 --- a/VemProFutApi/pom.xml +++ b/VemProFutApi/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.5.7 + 3.4.0 br.com.vemprofut @@ -29,6 +29,7 @@ 21 + org.springframework.boot @@ -46,6 +47,7 @@ com.mysql mysql-connector-j + 8.0.32 runtime @@ -83,6 +85,7 @@ org.flywaydb flyway-core + 10.20.0 @@ -118,11 +121,25 @@ 10.6 - - + + + + + + com.h2database + h2 + test + + + junit + junit + test + + @@ -180,19 +197,25 @@ - + - org.apache.maven.plugins - maven-pmd-plugin - 3.21.0 + com.github.spotbugs + spotbugs-maven-plugin + 4.8.3.0 - true + Max + Low + true + true + ${project.build.directory}/spotbugs + ${project.basedir}/excludeFilter.xml verify check + spotbugs @@ -200,4 +223,4 @@ - + \ No newline at end of file diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/configs/OAuth2LoginSuccessHandler.java b/VemProFutApi/src/main/java/br/com/vemprofut/configs/OAuth2LoginSuccessHandler.java index 0047d74..1381d0d 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/configs/OAuth2LoginSuccessHandler.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/configs/OAuth2LoginSuccessHandler.java @@ -12,7 +12,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.Authentication; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; @@ -25,7 +24,7 @@ public class OAuth2LoginSuccessHandler implements AuthenticationSuccessHandler { // Agora vamos interceptar o retorno do Auth0... private final PeladeiroRepository peladeiroRepository; - private final OAuth2AuthorizedClientService authorizedClientService; + // private final OAuth2AuthorizedClientService authorizedClientService; private final IHistoricoPeladeiroService historicoPeladeiroService; private final IHistoricoPeladeiroMapper historicoPeladeiroMapper; diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/FutController.java b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/FutController.java index b90f325..59df0f1 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/FutController.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/FutController.java @@ -5,7 +5,6 @@ import br.com.vemprofut.models.FutModel; import br.com.vemprofut.services.IFutService; import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import java.util.List; import lombok.AllArgsConstructor; @@ -15,7 +14,6 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; - @RestController @RequestMapping("fut/") @AllArgsConstructor @@ -25,7 +23,9 @@ public class FutController { // =============== CRUD basico ========================== @PostMapping - @Operation(summary = "Cadastra um novo Fut", tags = {"FutController - CRUD Básico"}) + @Operation( + summary = "Cadastra um novo Fut", + tags = {"FutController - CRUD Básico"}) public ResponseEntity create( @Valid @RequestBody final SaveFutRequestDTO requestDTO) { var response = futService.create(requestDTO); @@ -33,13 +33,17 @@ public ResponseEntity create( } @GetMapping("{id}") - @Operation(summary = "Busca um Fut pelo ID", tags = {"FutController - CRUD Básico"}) + @Operation( + summary = "Busca um Fut pelo ID", + tags = {"FutController - CRUD Básico"}) public ResponseEntity findByFut(@PathVariable final Long id) { return ResponseEntity.ok(futService.findById(id)); } @PutMapping("{id}") - @Operation(summary = "Altera um Fut já cadastrado, informando o id", tags = {"FutController - CRUD Básico"}) + @Operation( + summary = "Altera um Fut já cadastrado, informando o id", + tags = {"FutController - CRUD Básico"}) public ResponseEntity update( @PathVariable final Long id, UpdateFutRequestDTO dto) { var obj = futService.update(id, dto); @@ -47,7 +51,9 @@ public ResponseEntity update( } @DeleteMapping("{id}") - @Operation(summary = "Apaga um Fut por meio do id, cuidado! ", tags = {"FutController - CRUD Básico"}) + @Operation( + summary = "Apaga um Fut por meio do id, cuidado! ", + tags = {"FutController - CRUD Básico"}) public ResponseEntity delete(@PathVariable final Long id) { futService.delete(id); return ResponseEntity.noContent().build(); @@ -56,7 +62,9 @@ public ResponseEntity delete(@PathVariable final Long id) { // =================== acoes partidas ======================= @PostMapping("partida") - @Operation(summary = "Criar uma nova partida...", tags = {"FutController - Ações Partidas"}) + @Operation( + summary = "Criar uma nova partida...", + tags = {"FutController - Ações Partidas"}) public ResponseEntity criarPartida( @RequestBody SavePartidaRequestDTO requestDTO) { FutModel futModel = futService.findByIdModel(requestDTO.futId()); @@ -65,7 +73,9 @@ public ResponseEntity criarPartida( } @PostMapping("partidaslist") - @Operation(summary = "Cria varias partidas de uma só vez, todas com resultados preenchidos", tags = {"FutController - Ações Partidas"}) + @Operation( + summary = "Cria varias partidas de uma só vez, todas com resultados preenchidos", + tags = {"FutController - Ações Partidas"}) public ResponseEntity> criarPartidasLista( @RequestBody List requestDTO) { return ResponseEntity.status(HttpStatus.CREATED).body(futService.criarPartidasList(requestDTO)); @@ -74,7 +84,9 @@ public ResponseEntity> criarPartidasLista( // ================ lista peladeiro ===================== @PostMapping("add-peladeiro") - @Operation(summary = "Adiciona um peladeiro a lista de peladeiros cadastrado no fut", tags = {"FutController - Lista de Peladeiros"}) + @Operation( + summary = "Adiciona um peladeiro a lista de peladeiros cadastrado no fut", + tags = {"FutController - Lista de Peladeiros"}) public ResponseEntity adicionarPeladeiroLista( @RequestBody AddPeladeiroInFutListRequestDTO addPeladeiroRequestDTO) { futService.addPeladeiro(addPeladeiroRequestDTO); @@ -82,7 +94,9 @@ public ResponseEntity adicionarPeladeiroLista( } @GetMapping("lista-peladeiros/{idFut}") - @Operation(summary = "busca a lista de todos peldadeiros cadastrados no fut", tags = {"FutController - Lista de Peladeiros"}) + @Operation( + summary = "busca a lista de todos peldadeiros cadastrados no fut", + tags = {"FutController - Lista de Peladeiros"}) public ResponseEntity> listarPeladeirosCadastrados( @PathVariable final Long idFut) { return ResponseEntity.ok().body(futService.listarPeladeiroCadastradosFut(idFut)); @@ -91,7 +105,9 @@ public ResponseEntity> listarPeladeirosCadastrados( // =============== lista Editores =========================== @PostMapping("add-editor") - @Operation(summary = "Adiciona um Editor a lista de editores de um fut em especifico", tags = {"FutController - Lista de Editores do Fut"}) + @Operation( + summary = "Adiciona um Editor a lista de editores de um fut em especifico", + tags = {"FutController - Lista de Editores do Fut"}) public ResponseEntity adicionarEditorLista( @RequestBody AddEditorInFutListResquestDTO editor) { futService.addEditor(editor); @@ -99,7 +115,9 @@ public ResponseEntity adicionarEditorLista( } @GetMapping("lista-editores/{idFut}") - @Operation(summary = "busca a lista de editores de um fut em especifico", tags = {"FutController - Lista de Editores do Fut"}) + @Operation( + summary = "busca a lista de editores de um fut em especifico", + tags = {"FutController - Lista de Editores do Fut"}) public ResponseEntity> listarEditoresFut( @PathVariable final Long idFut) { @@ -109,7 +127,9 @@ public ResponseEntity> listarEditoresFut( // ================= upload arquivos fotos ================== @PostMapping(value = "uploadFoto/{id}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - @Operation(summary = "Para enviar a foto de capa do Fut", tags = {"FutController - Upload de Imagens"}) + @Operation( + summary = "Para enviar a foto de capa do Fut", + tags = {"FutController - Upload de Imagens"}) public ResponseEntity uploadFotoFut( @PathVariable Long id, @RequestPart("file") MultipartFile file) { futService.atualizarFotoCapa(id, file); @@ -119,21 +139,27 @@ public ResponseEntity uploadFotoFut( // ======================== Banimentos ======================= @PostMapping(value = "addBanimento") - @Operation(summary = "Adicionando um peladeiro da lista para o banimento", tags = {"FutController - Banimento no Fut"}) + @Operation( + summary = "Adicionando um peladeiro da lista para o banimento", + tags = {"FutController - Banimento no Fut"}) public ResponseEntity adicionarBanimento(@RequestBody SaveBanimentoRequestDTO dto) { futService.addBanimentoList(dto); return ResponseEntity.noContent().build(); } @GetMapping("lista-banidos/{idFut}") - @Operation(summary = "Busca a lista de Banidos do Fut em questao", tags = {"FutController - Banimento no Fut"}) + @Operation( + summary = "Busca a lista de Banidos do Fut em questao", + tags = {"FutController - Banimento no Fut"}) public ResponseEntity> buscarListaBanidos( @PathVariable Long idFut) { return ResponseEntity.ok().body(futService.findAllBanidos(idFut)); } @DeleteMapping("delete-banimento/{idFut}/{idPeladeiro}") - @Operation(summary = "retira um peladeiro da lista de banidos", tags = {"FutController - Banimento no Fut"}) + @Operation( + summary = "retira um peladeiro da lista de banidos", + tags = {"FutController - Banimento no Fut"}) public ResponseEntity retirandoBanimento( @PathVariable Long idFut, @PathVariable Long idPeladeiro) { futService.removeBanido(idPeladeiro, idFut); diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/PeladeiroController.java b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/PeladeiroController.java index 4e57a49..0e7e4f7 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/PeladeiroController.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/PeladeiroController.java @@ -23,7 +23,9 @@ public class PeladeiroController { private final IPeladeiroService peladeiroService; @PostMapping - @Operation(summary = "Cadastra um novo peladeiro", tags = {"PeladeiroController"}) + @Operation( + summary = "Cadastra um novo peladeiro", + tags = {"PeladeiroController"}) public ResponseEntity create( @Valid @RequestBody final SavePeladeiroRequestDTO requestDTO) { var obj = peladeiroService.create(requestDTO); @@ -31,14 +33,18 @@ public ResponseEntity create( } @GetMapping("{id}") - @Operation(summary = "Busca um Peladeiro pelo id", tags = {"PeladeiroController"}) + @Operation( + summary = "Busca um Peladeiro pelo id", + tags = {"PeladeiroController"}) public ResponseEntity findById(@PathVariable final Long id) { var obj = peladeiroService.findById(id); return ResponseEntity.ok(obj); } @PutMapping("{id}") - @Operation(summary = "Faz alteraçoes no Peladeiro cujo id é informado.", tags = {"PeladeiroController"}) + @Operation( + summary = "Faz alteraçoes no Peladeiro cujo id é informado.", + tags = {"PeladeiroController"}) public ResponseEntity update( @PathVariable final Long id, @Valid @RequestBody UpdatePeladeiroRequestDTO dto) { peladeiroService.update(id, dto); @@ -46,14 +52,18 @@ public ResponseEntity update( } @DeleteMapping("{id}") - @Operation(summary = "Deleta o peladeiro cujo id foi informado. Cuidado!", tags = {"PeladeiroController"}) + @Operation( + summary = "Deleta o peladeiro cujo id foi informado. Cuidado!", + tags = {"PeladeiroController"}) public ResponseEntity delete(@PathVariable final Long id) { peladeiroService.delete(id); return ResponseEntity.noContent().build(); } @PostMapping(value = "uploadFoto/{id}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - @Operation(summary = "Caso nao logado pelo gmail, enviar a foto do perfil", tags = {"PeladeiroController"}) + @Operation( + summary = "Caso nao logado pelo gmail, enviar a foto do perfil", + tags = {"PeladeiroController"}) public ResponseEntity uploadFotoPeladeiro( @PathVariable Long id, @RequestPart("file") MultipartFile file) throws IOException { peladeiroService.atualizarFoto(id, file); diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/request/SaveFutRequestDTO.java b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/request/SaveFutRequestDTO.java index 53bc28d..f4d8874 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/request/SaveFutRequestDTO.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/request/SaveFutRequestDTO.java @@ -8,4 +8,4 @@ public record SaveFutRequestDTO( @PositiveOrZero Integer jogadoresPorTime, @PositiveOrZero Integer tempoMaxPartida, @PositiveOrZero Integer maxGolsVitoria, - @PositiveOrZero Long administradorPeladeiroId) {} + @PositiveOrZero Long administradorPeladeiro) {} diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/CartoesResponseDTO.java b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/CartoesResponseDTO.java index 95ec35e..36b9ee7 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/CartoesResponseDTO.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/CartoesResponseDTO.java @@ -3,4 +3,4 @@ import br.com.vemprofut.models.enuns.TipoCartao; public record CartoesResponseDTO( - Long id, Long partidaId, Long peladeiroId, Long futId, TipoCartao tipoCartao) {} + Long id, Long partida, Long peladeiro, Long fut, TipoCartao tipoCartao) {} diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/SavePeladeiroResponseDTO.java b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/SavePeladeiroResponseDTO.java index 0ed4f04..64a52b3 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/SavePeladeiroResponseDTO.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/SavePeladeiroResponseDTO.java @@ -12,7 +12,7 @@ public record SavePeladeiroResponseDTO( @JsonProperty("peDominante") String peDominante, @JsonProperty("whatsapp") String whatsapp, @JsonProperty("historicoPeladeiro") Long historicoPeladeiro, - @JsonProperty("foto_url") String foto_url, + @JsonProperty("fotoUrl") String fotoUrl, @JsonProperty("partidasIDs") List partidas, @JsonProperty("futsIDs") List futs, @JsonProperty("cartoes") List cartoes) {} diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/UpdatePeladeiroResponseDTO.java b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/UpdatePeladeiroResponseDTO.java index 30e21a8..02fcf35 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/UpdatePeladeiroResponseDTO.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/UpdatePeladeiroResponseDTO.java @@ -8,4 +8,5 @@ public record UpdatePeladeiroResponseDTO( @JsonProperty("apelido") String apelido, @JsonProperty("descricao") String descricao, @JsonProperty("whatsapp") String whatsapp, - @JsonProperty("peDominante") String peDominante) {} + @JsonProperty("peDominante") String peDominante, + @JsonProperty("fotoURL") String fotoUrl) {} diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/mappers/ICartoesMapper.java b/VemProFutApi/src/main/java/br/com/vemprofut/mappers/ICartoesMapper.java index bfbfede..0017fdc 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/mappers/ICartoesMapper.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/mappers/ICartoesMapper.java @@ -1,5 +1,6 @@ package br.com.vemprofut.mappers; +import br.com.vemprofut.controllers.response.CartoesResponseDTO; import br.com.vemprofut.models.CartoesModel; import br.com.vemprofut.models.DTOs.CartoesDTO; import org.mapstruct.Mapper; @@ -24,4 +25,6 @@ Esses dois campos (`cartoesPartida` e `cartoesPeladeiro`) são listas de objetos // De Entity/Model --> DTO CartoesDTO toDTO(CartoesModel model); + + CartoesResponseDTO toResponseDTO(CartoesModel model); } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/mappers/IFutMapper.java b/VemProFutApi/src/main/java/br/com/vemprofut/mappers/IFutMapper.java index 21e7077..b15083f 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/mappers/IFutMapper.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/mappers/IFutMapper.java @@ -7,6 +7,7 @@ import br.com.vemprofut.models.DTOs.FutDTO; import br.com.vemprofut.models.FutModel; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; @Mapper( componentModel = "spring", @@ -14,8 +15,17 @@ public interface IFutMapper { // DTO --> Model + @Mapping(target = "foto_url", ignore = true) + @Mapping(target = "banidos", ignore = true) FutModel toModel(FutDTO dto); + @Mapping(target = "id", ignore = true) + @Mapping(target = "foto_url", ignore = true) + @Mapping(target = "historicoFutId", ignore = true) + @Mapping(target = "editores", ignore = true) + @Mapping(target = "peladeiros", ignore = true) + @Mapping(target = "cartoes", ignore = true) + @Mapping(target = "banidos", ignore = true) FutModel saveRequestToModel(SaveFutRequestDTO dto); SaveFutResponseDTO toSaveResponse(FutModel dto); diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/mappers/IMappersDefault.java b/VemProFutApi/src/main/java/br/com/vemprofut/mappers/IMappersDefault.java index 87e60ae..b848a69 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/mappers/IMappersDefault.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/mappers/IMappersDefault.java @@ -1,5 +1,6 @@ package br.com.vemprofut.mappers; +import br.com.vemprofut.controllers.response.CartoesResponseDTO; import br.com.vemprofut.controllers.response.CartoesResumoResponseDTO; import br.com.vemprofut.controllers.response.PeladeiroResponseDTO; import br.com.vemprofut.models.CartoesModel; @@ -64,6 +65,18 @@ default CartoesResumoResponseDTO mapResumoCartoes(List cartoes) { return resultados; } + default CartoesResponseDTO mapResponceCartoesDTO(CartoesModel model) { + CartoesResponseDTO cartoesResponseDTO = + new CartoesResponseDTO( + model.getId(), + model.getPartida().getId(), + model.getPeladeiro().getId(), + model.getFut().getId(), + model.getTipoCartao()); + + return cartoesResponseDTO; + } + // ========================EDITOR===================== // Converte EditorModel -> Long (pegando o id) @@ -179,8 +192,8 @@ default PeladeiroResponseDTO mapModelToResponse(PeladeiroModel model) { // return new PeladeiroNameIdResponseDTO(peladeiroId, nome); // } - //======================Banimento================================= - //TODO: coversao de peladeiro para o long no DTODetails - //TODO: coversao de fut para o long no DTODetails + // ======================Banimento================================= + // TODO: coversao de peladeiro para o long no DTODetails + // TODO: coversao de fut para o long no DTODetails } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/mappers/IPeladeiroMapper.java b/VemProFutApi/src/main/java/br/com/vemprofut/mappers/IPeladeiroMapper.java index 3a9c9a1..454e824 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/mappers/IPeladeiroMapper.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/mappers/IPeladeiroMapper.java @@ -18,6 +18,12 @@ public interface IPeladeiroMapper { // DTO --> Model + @Mapping(target = "administra", ignore = true) + @Mapping(target = "editores", ignore = true) + @Mapping(target = "fotoUrl", ignore = true) + @Mapping(target = "golsPeladeiro", ignore = true) + @Mapping(target = "banimentos", ignore = true) + @Mapping(target = "authProvider", ignore = true) PeladeiroModel toModel(PeladeiroDTO dto); @BeanMapping( @@ -32,10 +38,21 @@ public interface IPeladeiroMapper { PeladeiroDetailResponse modelToDetailsDTO(PeladeiroModel model); @Mapping(target = "id", ignore = true) + @Mapping(target = "historicoPeladeiro", ignore = true) + @Mapping(target = "partidas", ignore = true) + @Mapping(target = "cartoes", ignore = true) + @Mapping(target = "futs", ignore = true) + @Mapping(target = "administra", ignore = true) + @Mapping(target = "editores", ignore = true) + @Mapping(target = "fotoUrl", ignore = true) + @Mapping(target = "golsPeladeiro", ignore = true) + @Mapping(target = "banimentos", ignore = true) + @Mapping(target = "authProvider", ignore = true) PeladeiroModel saveRequestToModel(SavePeladeiroRequestDTO dto); UpdatePeladeiroResponseDTO modelToUpdateResponse(PeladeiroModel model); + @Mapping(target = "fotoUrl", ignore = true) SavePeladeiroResponseDTO modelToSaveResponse(PeladeiroModel model); @Mapping(source = "id", target = "peladeiroId") diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/models/BanimentoModel.java b/VemProFutApi/src/main/java/br/com/vemprofut/models/BanimentoModel.java index 9b56881..8dac986 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/models/BanimentoModel.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/models/BanimentoModel.java @@ -2,7 +2,6 @@ import jakarta.persistence.*; import java.time.LocalDate; -import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -11,10 +10,22 @@ @Table(name = "banimento") @Getter @Setter -@AllArgsConstructor @NoArgsConstructor public class BanimentoModel { + public BanimentoModel( + String motivo, + LocalDate dataBanimento, + LocalDate dataFimBanimento, + PeladeiroModel peladeiro, + FutModel fut) { + this.motivo = motivo; + this.dataBanimento = dataBanimento; + this.dataFimBanimento = dataFimBanimento; + this.peladeiro = peladeiro; + this.fut = fut; + } + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id_banimento") diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/models/CartoesModel.java b/VemProFutApi/src/main/java/br/com/vemprofut/models/CartoesModel.java index f5e8ae2..3d67694 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/models/CartoesModel.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/models/CartoesModel.java @@ -3,14 +3,24 @@ import br.com.vemprofut.models.enuns.TipoCartao; import jakarta.persistence.*; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; @Entity @Table(name = "cartoes_peladeiro") @Getter @Setter +@NoArgsConstructor public class CartoesModel { + public CartoesModel( + PartidasModel partida, PeladeiroModel peladeiro, FutModel fut, TipoCartao tipoCartao) { + this.partida = partida; + this.peladeiro = peladeiro; + this.fut = fut; + this.tipoCartao = tipoCartao; + } + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id_cartoes_peladeiro") diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/models/DTOs/CartaoCountProjection.java b/VemProFutApi/src/main/java/br/com/vemprofut/models/DTOs/CartaoCountProjection.java index dc9d8db..6dd1420 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/models/DTOs/CartaoCountProjection.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/models/DTOs/CartaoCountProjection.java @@ -2,6 +2,7 @@ import br.com.vemprofut.models.enuns.TipoCartao; +// Essa interface está em DTOs porque ela representa um objeto de transferência de dados. public interface CartaoCountProjection { TipoCartao getTipo(); diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/models/DTOs/CartoesDTO.java b/VemProFutApi/src/main/java/br/com/vemprofut/models/DTOs/CartoesDTO.java index 15c353c..b7563f0 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/models/DTOs/CartoesDTO.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/models/DTOs/CartoesDTO.java @@ -2,5 +2,4 @@ import br.com.vemprofut.models.enuns.TipoCartao; -public record CartoesDTO( - Long id, Long partidaId, Long peladeiroId, Long futId, TipoCartao tipoCartao) {} +public record CartoesDTO(Long id, Long partida, Long peladeiro, Long fut, TipoCartao tipoCartao) {} diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/models/DTOs/EditorDTO.java b/VemProFutApi/src/main/java/br/com/vemprofut/models/DTOs/EditorDTO.java index fb50131..027f37a 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/models/DTOs/EditorDTO.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/models/DTOs/EditorDTO.java @@ -1,3 +1,3 @@ package br.com.vemprofut.models.DTOs; -public record EditorDTO(Long id, Long peladeiroId, Long futId) {} +public record EditorDTO(Long id, Long peladeiro, Long fut) {} diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/models/DTOs/FutDTO.java b/VemProFutApi/src/main/java/br/com/vemprofut/models/DTOs/FutDTO.java index c9a63f8..d7f1eb5 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/models/DTOs/FutDTO.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/models/DTOs/FutDTO.java @@ -9,7 +9,7 @@ public record FutDTO( Integer tempoMaxPartida, Integer maxGolsVitoria, Long historicoFutId, - Long administradorPeladeiroId, + Long administradorPeladeiro, List editores, List peladeiros, List cartoes) {} diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/models/EditorModel.java b/VemProFutApi/src/main/java/br/com/vemprofut/models/EditorModel.java index b18fcca..f1b2e5d 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/models/EditorModel.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/models/EditorModel.java @@ -12,6 +12,11 @@ @NoArgsConstructor public class EditorModel { + public EditorModel(PeladeiroModel peladeiro, FutModel fut) { + this.peladeiro = peladeiro; + this.fut = fut; + } + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id_editor") diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/models/FutModel.java b/VemProFutApi/src/main/java/br/com/vemprofut/models/FutModel.java index 5e36f06..0d316eb 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/models/FutModel.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/models/FutModel.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; @@ -11,8 +12,24 @@ @Table(name = "fut") @Getter @Setter +@NoArgsConstructor public class FutModel { + public FutModel( + String nome, + Integer jogadoresPorTime, + Integer tempoMaxPartida, + Integer maxGolsVitoria, + HistoricoFutModel historicoFutId, + PeladeiroModel administradorPeladeiro) { + this.nome = nome; + this.jogadoresPorTime = jogadoresPorTime; + this.tempoMaxPartida = tempoMaxPartida; + this.maxGolsVitoria = maxGolsVitoria; + this.historicoFutId = historicoFutId; + this.administradorPeladeiro = administradorPeladeiro; + } + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id_fut") diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/models/PeladeiroModel.java b/VemProFutApi/src/main/java/br/com/vemprofut/models/PeladeiroModel.java index 00be016..bd1176c 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/models/PeladeiroModel.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/models/PeladeiroModel.java @@ -3,16 +3,31 @@ import jakarta.persistence.*; import java.util.ArrayList; import java.util.List; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; +import lombok.*; @Entity @Table(name = "peladeiro") @Getter @Setter +@NoArgsConstructor +@AllArgsConstructor public class PeladeiroModel { + public PeladeiroModel( + String nome, + String email, + String apelido, + String descricao, + String whatsapp, + String peDominante) { + this.nome = nome; + this.email = email; + this.apelido = apelido; + this.descricao = descricao; + this.whatsapp = whatsapp; + this.peDominante = peDominante; + } + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id_peladeiro") diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/repositories/BanimentoRepository.java b/VemProFutApi/src/main/java/br/com/vemprofut/repositories/BanimentoRepository.java index 3b30e88..d5c14d6 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/repositories/BanimentoRepository.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/repositories/BanimentoRepository.java @@ -11,13 +11,13 @@ public interface BanimentoRepository extends JpaRepository @Query( """ - SELECT b FROM BanimentoModel b WHERE b.fut = :idFut AND b.peladeiro = :idPeladeiro - """) + SELECT b FROM BanimentoModel b WHERE b.fut = :idFut AND b.peladeiro = :idPeladeiro + """) Optional buscarBanimentoFutPeladeiro( @Param("idFut") Long idFut, @Param("idPeladeiro") Long idPeladeiro); @Query(""" - SELECT b FROM BanimentoModel b WHERE b.fut = :idFut - """) + SELECT b FROM BanimentoModel b WHERE b.fut = :idFut + """) List buscarListBanidos(@Param("idFut") Long idFut); } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/repositories/CartoesRepository.java b/VemProFutApi/src/main/java/br/com/vemprofut/repositories/CartoesRepository.java index 9a055e9..f0855df 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/repositories/CartoesRepository.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/repositories/CartoesRepository.java @@ -24,17 +24,17 @@ SELECT c.tipoCartao AS tipo, COUNT(c) AS quantidade FROM CartoesModel c WHERE c.peladeiro.id = :peladeiroId GROUP BY c.tipoCartao - """) + """) List countByTipoAndPeladeiro(@Param("peladeiroId") Long peladeiroId); @Query( """ SELECT c.tipoCartao AS tipo, COUNT(c) AS quantidade FROM CartoesModel c - WHERE c.fut.id = :futId + WHERE c.fut.id = :fut GROUP BY c.tipoCartao - """) - List countByTipoAndFut(@Param("futId") Long futId); + """) + List countByTipoAndFut(@Param("fut") Long futId); /* Agora o retorno já vem fortemente tipado: cada item da lista é um 'CartaoCountProjection' com getTipo() diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/repositories/EditorRepository.java b/VemProFutApi/src/main/java/br/com/vemprofut/repositories/EditorRepository.java index d8bf585..92b99e8 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/repositories/EditorRepository.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/repositories/EditorRepository.java @@ -13,7 +13,7 @@ public interface EditorRepository extends JpaRepository { @Query( """ SELECT e FROM EditorModel e WHERE e.peladeiro = :peladeiro AND e.fut= :fut - """) + """) EditorModel findByPeladeiroAndFut( @Param("peladeiro") PeladeiroModel peladeiroModel, @Param("fut") FutModel futModel); diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/FutService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/FutService.java index 8a418dd..3aa0c5f 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/FutService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/FutService.java @@ -60,7 +60,7 @@ public class FutService implements IFutService { @Transactional public SaveFutResponseDTO create(SaveFutRequestDTO dto) { queryService.verifyNomeFutExist(dto.nome()); - var peladeiro = peladeiroQueryService.verifyPeladeiroExist(dto.administradorPeladeiroId()); + var peladeiro = peladeiroQueryService.verifyPeladeiroExist(dto.administradorPeladeiro()); FutModel saved = repository.save(mapper.saveRequestToModel(dto)); diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/ICartoesQueryService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/ICartoesQueryService.java index d55d01a..1839fa9 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/ICartoesQueryService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/ICartoesQueryService.java @@ -2,15 +2,9 @@ import br.com.vemprofut.models.CartoesModel; import br.com.vemprofut.models.DTOs.CartoesDTO; -import br.com.vemprofut.models.DTOs.PartidasDTO; -import br.com.vemprofut.models.DTOs.PeladeiroDTO; public interface ICartoesQueryService { - void verifyPeladeiroExist(PeladeiroDTO peladeiroDTO); - - void verifyPartidasExist(PartidasDTO partidasDTO); - void verifyEntitiesExist(CartoesDTO dto); CartoesModel verityCartoesExist(Long id); diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/BanimentoQueryService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/BanimentoQueryService.java index a7a031e..95900bb 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/BanimentoQueryService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/BanimentoQueryService.java @@ -7,16 +7,16 @@ import br.com.vemprofut.models.BanimentoModel; import br.com.vemprofut.repositories.BanimentoRepository; import br.com.vemprofut.services.query.IBanimentoQueryService; -import org.springframework.stereotype.Service; - import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; @Service public class BanimentoQueryService implements IBanimentoQueryService { - private BanimentoRepository repository; + @Autowired private BanimentoRepository repository; - private IBanimentoMapper mapper; + @Autowired private IBanimentoMapper mapper; @Override public void verifyPeladeiroBanidoExist(Long idFut, Long idPeladeiro) { diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/CartoesQueryService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/CartoesQueryService.java index c63bd04..20bd3ec 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/CartoesQueryService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/CartoesQueryService.java @@ -3,37 +3,25 @@ import br.com.vemprofut.exceptions.NotFoundException; import br.com.vemprofut.models.CartoesModel; import br.com.vemprofut.models.DTOs.CartoesDTO; -import br.com.vemprofut.models.DTOs.PartidasDTO; -import br.com.vemprofut.models.DTOs.PeladeiroDTO; import br.com.vemprofut.repositories.CartoesRepository; import br.com.vemprofut.services.query.ICartoesQueryService; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class CartoesQueryService implements ICartoesQueryService { - private CartoesRepository repository; - - @Override - public void verifyPeladeiroExist(PeladeiroDTO peladeiroDTO) { - // TODO: implementar - } - - @Override - public void verifyPartidasExist(PartidasDTO partidasDTO) { - // TODO: implementar - } + @Autowired private CartoesRepository repository; // Verifica se o peladeiro, partida e fut existem public void verifyEntitiesExist(CartoesDTO dto) { - if (!repository.existsById(dto.peladeiroId())) + if (!repository.existsById(dto.peladeiro())) throw new IllegalArgumentException("Peladeiro não encontrado"); - if (!repository.existsById(dto.partidaId())) + if (!repository.existsById(dto.partida())) throw new IllegalArgumentException("Partida não encontrada"); - if (!repository.existsById(dto.futId())) - throw new IllegalArgumentException("Fut não encontrado"); + if (!repository.existsById(dto.fut())) throw new IllegalArgumentException("Fut não encontrado"); } public CartoesModel verityCartoesExist(Long id) { diff --git a/VemProFutApi/src/main/resources/application-dev.properties b/VemProFutApi/src/main/resources/application-dev.properties index 0f1ce81..425cb0e 100644 --- a/VemProFutApi/src/main/resources/application-dev.properties +++ b/VemProFutApi/src/main/resources/application-dev.properties @@ -1,11 +1,49 @@ -spring: - jpa: - show-sql: true - properties: - hibernate: - format_sql: true - devtools: - remote: - secret: 123 - restart: - trigger-file: trigger.txt \ No newline at end of file +spring.application.name=VemProFut + +###################################################### +# == BANCO LOCAL (MySQL Rodando Localmente) ========== +###################################################### +spring.datasource.url=jdbc:mysql://localhost:3306/vemprofutdb?createDatabaseIfNotExist=true&allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC +spring.datasource.username=root +spring.datasource.password=root123 + +spring.jpa.hibernate.ddl-auto=none + + +###################################################### +# == LOGS (DEV: Mais detalhados) ===================== +###################################################### +logging.level.root=INFO +logging.level.br.com.vemprofut=DEBUG + +# Mostra queries SQL (util para dev, mas nao tao agressivo) +logging.level.log.hibernate.SQL=DEBUG +logging.level.log.hibernate.type.descritor.sql.BasicBinder=TRACE + + +###################################################### +# == FLYWAY ========================================== +###################################################### +spring.flyway.enabled=true +spring.flyway.locations=classpath:db/migration + + +###################################################### +# == AUTH0 (Somente para DEV) ======================== +###################################################### +spring.security.oauth2.client.registration.auth0.client-id=jcmpSdlqwwakcitekSVmh0F0Hw6BEcf7 +spring.security.oauth2.client.registration.auth0.client-secret=QWUV1hzjq3fgZy0vNuv_ir9Svsv11JeAw241YuPpMcCCeP7kdTbgajsaptfk52Mx +spring.security.oauth2.client.registration.auth0.redirect-uri=http://localhost:8080/login/oauth2/code/{registrationId} + +spring.security.oauth2.resourceserver.jwt.issuer-uri=https://dev-6nhln802vsqkhnfm.us.auth0.com/ + +spring.security.oauth2.client.registration.auth0.authorization-grant-type=authorization_code +spring.security.oauth2.client.registration.auth0.scope=openid,profile,email + +# Provedor (provider) configurado para o nome 'auth0' +spring.security.oauth2.client.provider.auth0.issuer-uri=https://dev-6nhln802vsqkhnfm.us.auth0.com/ + +###################################################### +# == UPLOAD LOCAL ==================================== +###################################################### +app.upload.dir=uploads diff --git a/VemProFutApi/src/main/resources/application-prod.properties b/VemProFutApi/src/main/resources/application-prod.properties new file mode 100644 index 0000000..be2aa8f --- /dev/null +++ b/VemProFutApi/src/main/resources/application-prod.properties @@ -0,0 +1,39 @@ +spring.application.name=VemProFut + +# MYSQL via Docker Compose +spring.datasource.url=${SPRING_DATASOURCE_URL} +spring.datasource.username=${SPRING_DATASOURCE_USERNAME} +spring.datasource.password=${SPRING_DATASOURCE_PASSWORD} + +spring.jpa.hibernate.ddl-auto=none + +spring.flyway.enabled=true +spring.flyway.locations=classpath:db/migration +spring.flyway.fail-on-missing-locations=false +spring.flyway.connect-retries=10 +spring.flyway.connect-retries-interval=5s + +# LOGS NO CONSOLE (padrao Docker) +logging.level.root=INFO +logging.level.org.springframework=INFO + +# Upload definido por volume +app.upload.dir=/app/uploads + +# ----------- Auth0 / OAuth2 ----------- +# Client registration +spring.security.oauth2.client.registration.auth0.client-id=${AUTH0_CLIENT_ID} +spring.security.oauth2.client.registration.auth0.client-secret=${AUTH0_CLIENT_SECRET} +spring.security.oauth2.client.registration.auth0.scope=openid,profile,email +spring.security.oauth2.client.registration.auth0.redirect-uri={baseUrl}/login/oauth2/code/auth0 + +# Provider configuration +spring.security.oauth2.client.provider.auth0.issuer-uri=${AUTH0_ISSUER} + +# Resource server JWT (para valida\u00E7\u00E3o de tokens) +spring.security.oauth2.resourceserver.jwt.issuer-uri=${AUTH0_ISSUER} + +# For\u00E7a API a aceitar conex\u00F5es externas (necess\u00E1rio no Docker) +server.address=0.0.0.0 +server.port=8080 +server.servlet.session.cookie.secure=false diff --git a/VemProFutApi/src/main/resources/application.properties b/VemProFutApi/src/main/resources/application.properties index 917ad11..d20414d 100644 --- a/VemProFutApi/src/main/resources/application.properties +++ b/VemProFutApi/src/main/resources/application.properties @@ -1,62 +1 @@ -spring.application.name=VemProFut -spring.datasource.url=${SPRING_DATASOURCE_URL:jdbc:mysql://localhost:3306/vemprofutdb?createDatabaseIfNotExist=true&allowPublicKeyRetrieval=true&useSSL=false&serverTimeZone=UTC} -spring.datasource.username=${SPRING_DATASOURCE_USERNAME:root} -spring.datasource.password=${SPRING_DATASOURCE_PASSWORD:root123} - spring.profiles.active=${SPRING_PROFILES_ACTIVE:dev} - -spring.jpa.hibernate.ddl-auto=none - -############################################### -# == Ativando logs de excecoes do Spring ====== -############################################### - -logging.level.org.springframework.aop=DEBUG -logging.level.org.springframework.transaction=DEBUG -logging.level.org.hibernate.SQL=DEBUG -logging.level.org.hibernate.type.descriptor.sql=TRACE - -############################################### -#================ Flyway ====================== -############################################### -spring.flyway.enabled=true -spring.flyway.locations=classpath:db/migration - -############################################################### -# == Logger SLF4J para salvar os logs de erro e etc...========= -############################################################### - -logging.level.root=INFO -logging.level.br.com.vemprofut=DEBUG -logging.level.org.flywaydb=DEBUG - -logging.file.name=logs/vemprofut.log -logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} - %msg%n - -logging.logback.rollingpolicy.file-name-pattern=logs/vemprofut-%d{yyyy-MM-dd}.log -logging.logback.rollingpolicy.max-file-size=10MB -logging.logback.rollingpolicy.max-history=10 - -############################################### -# ========== OAuth2 Client (Auth0) ============ -############################################### - -spring.security.oauth2.client.registration.auth0.client-id=jcmpSdlqwwakcitekSVmh0F0Hw6BEcf7 -spring.security.oauth2.client.registration.auth0.client-secret=QWUV1hzjq3fgZy0vNuv_ir9Svsv11JeAw241YuPpMcCCeP7kdTbgajsaptfk52Mx -spring.security.oauth2.client.registration.auth0.scope=openid, profile, email -spring.security.oauth2.client.registration.auth0.redirect-uri=http://localhost:8080/login/oauth2/code/{registrationId} -spring.security.oauth2.client.registration.auth0.authorization-grant-type=authorization_code - -spring.security.oauth2.client.provider.auth0.issuer-uri=https://dev-6nhln802vsqkhnfm.us.auth0.com/ - - -############################################### -# ===== Resource Server (JWT Validation) ====== -############################################### - -spring.security.oauth2.resourceserver.jwt.issuer-uri=https://dev-6nhln802vsqkhnfm.us.auth0.com/ - -############################################### -# ======== Salvar Foto Localmente ============= -############################################### -app.upload.dir=uploads diff --git a/VemProFutApi/src/main/resources/db/migration/V202511092157__alter_table_partidas_contrains_fk_partidas2.sql b/VemProFutApi/src/main/resources/db/migration/V202511092157__alter_table_partidas_contrains_fk_partidas2.sql index 39c69a9..3ddbe2a 100644 --- a/VemProFutApi/src/main/resources/db/migration/V202511092157__alter_table_partidas_contrains_fk_partidas2.sql +++ b/VemProFutApi/src/main/resources/db/migration/V202511092157__alter_table_partidas_contrains_fk_partidas2.sql @@ -1,4 +1,4 @@ -ALTER TABLE Partidas ADD CONSTRAINT FK_Partidas_2 +ALTER TABLE partidas ADD CONSTRAINT FK_Partidas_2 FOREIGN KEY (fk_fut) - REFERENCES Fut (id_fut) + REFERENCES fut (id_fut) ON DELETE CASCADE; \ No newline at end of file diff --git a/VemProFutApi/src/main/resources/db/migration/V202511092159__alter_table_gols_constraint_fk_gols2.sql b/VemProFutApi/src/main/resources/db/migration/V202511092159__alter_table_gols_constraint_fk_gols2.sql index d458781..5bf7507 100644 --- a/VemProFutApi/src/main/resources/db/migration/V202511092159__alter_table_gols_constraint_fk_gols2.sql +++ b/VemProFutApi/src/main/resources/db/migration/V202511092159__alter_table_gols_constraint_fk_gols2.sql @@ -1,4 +1,4 @@ -ALTER TABLE Gols_Partida ADD CONSTRAINT FK_Gols_Partida_2 +ALTER TABLE gols_partida ADD CONSTRAINT FK_Gols_Partida_2 FOREIGN KEY (fk_peladeiro) - REFERENCES Peladeiro (id_peladeiro) + REFERENCES peladeiro (id_peladeiro) ON DELETE CASCADE; \ No newline at end of file diff --git a/VemProFutApi/src/main/resources/db/migration/V202511092200__alter_table_gols_constraint_fk_gols3.sql b/VemProFutApi/src/main/resources/db/migration/V202511092200__alter_table_gols_constraint_fk_gols3.sql new file mode 100644 index 0000000..8cb57f8 --- /dev/null +++ b/VemProFutApi/src/main/resources/db/migration/V202511092200__alter_table_gols_constraint_fk_gols3.sql @@ -0,0 +1,4 @@ +ALTER TABLE gols_partida ADD CONSTRAINT FK_Gols_Partida_3 + FOREIGN KEY (fk_partida) + REFERENCES partidas (id_partida) + ON DELETE CASCADE; \ No newline at end of file diff --git a/VemProFutApi/src/main/resources/db/migration/V202511092200__alter_table_gols_constraint_gols3,sql b/VemProFutApi/src/main/resources/db/migration/V202511092200__alter_table_gols_constraint_gols3,sql deleted file mode 100644 index 9c70e59..0000000 --- a/VemProFutApi/src/main/resources/db/migration/V202511092200__alter_table_gols_constraint_gols3,sql +++ /dev/null @@ -1,4 +0,0 @@ -ALTER TABLE Gols_Partida ADD CONSTRAINT FK_Gols_Partida_2 - FOREIGN KEY (fk_partida) - REFERENCES Partida (id_partida) - ON DELETE CASCADE; \ No newline at end of file diff --git a/VemProFutApi/src/main/resources/db/migration/V202511092204__alter_table_participapeladeiro_contraint_participa1.sql b/VemProFutApi/src/main/resources/db/migration/V202511092204__alter_table_participapeladeiro_contraint_participa1.sql index 5ef06e9..78c3bb6 100644 --- a/VemProFutApi/src/main/resources/db/migration/V202511092204__alter_table_participapeladeiro_contraint_participa1.sql +++ b/VemProFutApi/src/main/resources/db/migration/V202511092204__alter_table_participapeladeiro_contraint_participa1.sql @@ -1,4 +1,4 @@ ALTER TABLE participa_peladeiro_fut ADD CONSTRAINT FK_participa_peladeiro_fut_1 FOREIGN KEY (fk_fut) - REFERENCES Fut (id_fut) + REFERENCES fut (id_fut) ON DELETE CASCADE; diff --git a/VemProFutApi/src/main/resources/db/migration/V202511092205__alter_table_participapeladeiro_contraint_fk_participa2.sql b/VemProFutApi/src/main/resources/db/migration/V202511092205__alter_table_participapeladeiro_contraint_fk_participa2.sql index e7e9510..5278845 100644 --- a/VemProFutApi/src/main/resources/db/migration/V202511092205__alter_table_participapeladeiro_contraint_fk_participa2.sql +++ b/VemProFutApi/src/main/resources/db/migration/V202511092205__alter_table_participapeladeiro_contraint_fk_participa2.sql @@ -1,4 +1,4 @@ ALTER TABLE participa_peladeiro_fut ADD CONSTRAINT FK_participa_peladeiro_fut_2 FOREIGN KEY (fk_peladeiro) - REFERENCES Peladeiro (id_peladeiro) + REFERENCES peladeiro (id_peladeiro) ON DELETE CASCADE; diff --git a/VemProFutApi/src/main/resources/db/migration/V202511092206__alter_table_estapeladeio_constraint_esta1.sql b/VemProFutApi/src/main/resources/db/migration/V202511092206__alter_table_estapeladeio_constraint_esta1.sql index a838639..6da82e3 100644 --- a/VemProFutApi/src/main/resources/db/migration/V202511092206__alter_table_estapeladeio_constraint_esta1.sql +++ b/VemProFutApi/src/main/resources/db/migration/V202511092206__alter_table_estapeladeio_constraint_esta1.sql @@ -1,4 +1,4 @@ ALTER TABLE esta_peladeiro_partidas ADD CONSTRAINT FK_esta_peladeiro_partidas_1 FOREIGN KEY (fk_partidas) - REFERENCES Partidas (id_partida) + REFERENCES partidas (id_partida) ON DELETE CASCADE; diff --git a/VemProFutApi/src/main/resources/db/migration/V202511092208__alter_table_estapeladeiro_constraint_fk_esta2.sql b/VemProFutApi/src/main/resources/db/migration/V202511092208__alter_table_estapeladeiro_constraint_fk_esta2.sql index 62e90c6..c4f1a39 100644 --- a/VemProFutApi/src/main/resources/db/migration/V202511092208__alter_table_estapeladeiro_constraint_fk_esta2.sql +++ b/VemProFutApi/src/main/resources/db/migration/V202511092208__alter_table_estapeladeiro_constraint_fk_esta2.sql @@ -1,4 +1,4 @@ ALTER TABLE esta_peladeiro_partidas ADD CONSTRAINT FK_esta_peladeiro_partidas_2 FOREIGN KEY (fk_peladeiro) - REFERENCES Peladeiro (id_peladeiro) + REFERENCES peladeiro (id_peladeiro) ON DELETE CASCADE; diff --git a/VemProFutApi/src/main/resources/db/migration/V202511092212__alter_table_editor_constraint_fk_editor1.sql b/VemProFutApi/src/main/resources/db/migration/V202511092212__alter_table_editor_constraint_fk_editor1.sql index 2fb84f8..37cb6d1 100644 --- a/VemProFutApi/src/main/resources/db/migration/V202511092212__alter_table_editor_constraint_fk_editor1.sql +++ b/VemProFutApi/src/main/resources/db/migration/V202511092212__alter_table_editor_constraint_fk_editor1.sql @@ -1,4 +1,4 @@ ALTER TABLE editores_fut ADD CONSTRAINT FK_Editor_1 FOREIGN KEY (fk_fut) - REFERENCES Fut (id_fut) + REFERENCES fut (id_fut) ON DELETE CASCADE; \ No newline at end of file diff --git a/VemProFutApi/src/main/resources/db/migration/V202511092213__alter_table_editor_constraint_fk_editor2.sql b/VemProFutApi/src/main/resources/db/migration/V202511092213__alter_table_editor_constraint_fk_editor2.sql index 09e2f68..18699ae 100644 --- a/VemProFutApi/src/main/resources/db/migration/V202511092213__alter_table_editor_constraint_fk_editor2.sql +++ b/VemProFutApi/src/main/resources/db/migration/V202511092213__alter_table_editor_constraint_fk_editor2.sql @@ -1,4 +1,4 @@ ALTER TABLE editores_fut ADD CONSTRAINT FK_Editor_2 FOREIGN KEY (fk_peladeiro) - REFERENCES Peladeiro (id_peladeiro) + REFERENCES peladeiro (id_peladeiro) ON DELETE CASCADE; \ No newline at end of file diff --git a/VemProFutApi/src/main/resources/db/migration/V202511241710__alter_table_fk_banimento_2.sql b/VemProFutApi/src/main/resources/db/migration/V202511241710__alter_table_fk_banimento_2.sql index 135e5fb..f152c3c 100644 --- a/VemProFutApi/src/main/resources/db/migration/V202511241710__alter_table_fk_banimento_2.sql +++ b/VemProFutApi/src/main/resources/db/migration/V202511241710__alter_table_fk_banimento_2.sql @@ -1,4 +1,4 @@ ALTER TABLE banimento ADD CONSTRAINT FK_banimento_2 FOREIGN KEY (fk_peladeiro) - REFERENCES Peladeiro (id_peladeiro) + REFERENCES peladeiro (id_peladeiro) ON DELETE SET NULL; \ No newline at end of file diff --git a/VemProFutApi/src/main/resources/db/migration/V202511241712__alter_table_fk_banimento_3.sql b/VemProFutApi/src/main/resources/db/migration/V202511241712__alter_table_fk_banimento_3.sql index 6a41c75..95618c9 100644 --- a/VemProFutApi/src/main/resources/db/migration/V202511241712__alter_table_fk_banimento_3.sql +++ b/VemProFutApi/src/main/resources/db/migration/V202511241712__alter_table_fk_banimento_3.sql @@ -1,4 +1,4 @@ ALTER TABLE banimento ADD CONSTRAINT FK_banimento_3 FOREIGN KEY (fk_fut) - REFERENCES Fut (id_fut) + REFERENCES fut (id_fut) ON DELETE SET NULL; \ No newline at end of file diff --git a/VemProFutApi/src/test/java/br/com/vemprofut/VemProFutApplicationTests.java b/VemProFutApi/src/test/java/br/com/vemprofut/VemProFutApplicationTests.java deleted file mode 100644 index 8b8c5d1..0000000 --- a/VemProFutApi/src/test/java/br/com/vemprofut/VemProFutApplicationTests.java +++ /dev/null @@ -1,12 +0,0 @@ -package br.com.vemprofut; - -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class VemProFutApplicationTests { - - // @Test - // void contextLoads() { - // } - -} diff --git a/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/BanimentoRepositoryIT.java b/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/BanimentoRepositoryIT.java new file mode 100644 index 0000000..19f9e2c --- /dev/null +++ b/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/BanimentoRepositoryIT.java @@ -0,0 +1,152 @@ +package br.com.vemprofut.integration.repositories; + +import static org.junit.jupiter.api.Assertions.*; + +import br.com.vemprofut.configs.OAuth2LoginSuccessHandler; +import br.com.vemprofut.models.BanimentoModel; +import br.com.vemprofut.models.FutModel; +import br.com.vemprofut.models.PeladeiroModel; +import br.com.vemprofut.repositories.BanimentoRepository; +import br.com.vemprofut.repositories.FutRepository; +import br.com.vemprofut.repositories.PeladeiroRepository; +import br.com.vemprofut.services.implementacao.UploadLocalService; +import jakarta.transaction.Transactional; +import java.time.LocalDate; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.jwt.JwtDecoder; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.bean.override.mockito.MockitoBean; + +@SpringBootTest +@Transactional +@ActiveProfiles("test") +public class BanimentoRepositoryIT { + + @MockitoBean private OAuth2AuthorizedClientService authorizedClientService; + + // Opcional: Mocka o próprio Handler para evitar que ele tente rodar lógica + @MockitoBean private OAuth2LoginSuccessHandler oAuth2LoginSuccessHandler; + + @MockitoBean private ClientRegistrationRepository clientRegistrationRepository; + + @MockitoBean private JwtDecoder jwtDecoder; + + @MockitoBean private UploadLocalService uploadLocalService; + + @Autowired BanimentoRepository banimentoRepository; + + @Autowired PeladeiroRepository peladeiroRepository; + + @Autowired FutRepository futRepository; + + @Test + @DisplayName("Deve gerar salvar um banimento e retornar um id") + public void save_quandoBanimentoValido_retornaIdGerado() { + FutModel futModel = futRepository.saveAndFlush(new FutModel()); + PeladeiroModel peladeiroModel = + peladeiroRepository.saveAndFlush( + new PeladeiroModel( + "João da Silva", + "joao@test.com", + "Apelido", + "descricao qualquer", + "81999999999", + "Destro")); + + BanimentoModel banimentoModel = + new BanimentoModel( + "quebrou a perna do amigo", + LocalDate.of(2025, 11, 9), + LocalDate.of(2026, 11, 9), + peladeiroModel, + futModel); + + BanimentoModel banimentoSalvo = banimentoRepository.save(banimentoModel); + + assertNotNull(banimentoSalvo.getId()); + assertTrue(banimentoSalvo.getId() > 0); + } + + @Test + @DisplayName("Deve buscar todos os os banimentos") + public void findAll_quandoExistemRegistros_retornaListaComTodos() { + FutModel futModel1 = futRepository.saveAndFlush(new FutModel()); + FutModel futModel2 = futRepository.saveAndFlush(new FutModel()); + PeladeiroModel peladeiroModel1 = + peladeiroRepository.saveAndFlush( + new PeladeiroModel( + "João da Silva", + "joao@test.com", + "Apelido", + "descricao qualquer", + "81999999999", + "Destro")); + PeladeiroModel peladeiroModel2 = + peladeiroRepository.saveAndFlush( + new PeladeiroModel( + "João da Silva2", + "joao2@test.com", + "Apelido2", + "descricao qualquer2", + "81999999999", + "Destro")); + + BanimentoModel banimentoModel1 = + new BanimentoModel( + "quebrou a perna do amigo", + LocalDate.of(2025, 11, 9), + LocalDate.of(2026, 11, 9), + peladeiroModel1, + futModel1); + BanimentoModel banimentoModel2 = + new BanimentoModel( + "quebrou o ombro do amigo", + LocalDate.of(2025, 11, 9), + LocalDate.of(2026, 11, 9), + peladeiroModel2, + futModel2); + + banimentoRepository.saveAndFlush(banimentoModel1); + banimentoRepository.saveAndFlush(banimentoModel2); + + List todos = banimentoRepository.findAll(); + + assertTrue(todos.size() >= 2); + } + + @Test + @DisplayName("Deve apagar um banimento pelo numero do ID") + public void deleteById_quandoIdBanimentoExistir_registroRemovido() { + FutModel futModel = futRepository.saveAndFlush(new FutModel()); + PeladeiroModel peladeiroModel = + peladeiroRepository.saveAndFlush( + new PeladeiroModel( + "João da Silva", + "joao@test.com", + "Apelido", + "descricao qualquer", + "81999999999", + "Destro")); + + BanimentoModel banimentoModel = + new BanimentoModel( + "quebrou a perna do amigo", + LocalDate.of(2025, 11, 9), + LocalDate.of(2026, 11, 9), + peladeiroModel, + futModel); + + BanimentoModel banimentoSalvo = banimentoRepository.saveAndFlush(banimentoModel); + + Long id = banimentoSalvo.getId(); + + banimentoRepository.deleteById(id); + assertFalse(banimentoRepository.findById(id).isPresent()); + } +} diff --git a/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/CartoesRepositoryIT.java b/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/CartoesRepositoryIT.java new file mode 100644 index 0000000..a79912a --- /dev/null +++ b/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/CartoesRepositoryIT.java @@ -0,0 +1,424 @@ +package br.com.vemprofut.integration.repositories; + +import static org.junit.jupiter.api.Assertions.*; + +import br.com.vemprofut.configs.OAuth2LoginSuccessHandler; +import br.com.vemprofut.models.CartoesModel; +import br.com.vemprofut.models.DTOs.CartaoCountProjection; +import br.com.vemprofut.models.FutModel; +import br.com.vemprofut.models.PartidasModel; +import br.com.vemprofut.models.PeladeiroModel; +import br.com.vemprofut.models.enuns.TipoCartao; +import br.com.vemprofut.repositories.CartoesRepository; +import br.com.vemprofut.repositories.FutRepository; +import br.com.vemprofut.repositories.PartidasRepository; +import br.com.vemprofut.repositories.PeladeiroRepository; +import br.com.vemprofut.services.implementacao.UploadLocalService; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.jwt.JwtDecoder; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.transaction.annotation.Transactional; + +@SpringBootTest +@Transactional +@ActiveProfiles("test") +public class CartoesRepositoryIT { + + @MockitoBean private OAuth2AuthorizedClientService authorizedClientService; + + // Opcional: Mocka o próprio Handler para evitar que ele tente rodar lógica + @MockitoBean private OAuth2LoginSuccessHandler oAuth2LoginSuccessHandler; + + @MockitoBean private ClientRegistrationRepository clientRegistrationRepository; + + @MockitoBean private UploadLocalService uploadLocalService; + + @MockitoBean private JwtDecoder jwtDecoder; + + @Autowired CartoesRepository cartoesRepository; + + @Autowired PartidasRepository partidasRepository; + + @Autowired FutRepository futRepository; + + @Autowired PeladeiroRepository peladeiroRepository; + + @Test + @DisplayName("Deve criar um novo cartao ligado ao peladeiro e a partida") + void save_quandoCartaoValido_retornaIdGerado() { + + PartidasModel partidasModel = partidasRepository.saveAndFlush(new PartidasModel()); + + FutModel futModel = futRepository.saveAndFlush(new FutModel()); + + PeladeiroModel peladeiroModel = + peladeiroRepository.saveAndFlush( + new PeladeiroModel( + "João da Silva", + "joao@test.com", + "Apelido", + "descricao qualquer", + "81999999999", + "Destro")); + + CartoesModel cartoesModel = + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel, peladeiroModel, futModel, TipoCartao.AMARELO)); + + assertNotNull(cartoesModel.getId()); + assertTrue(cartoesModel.getId() > 0); + } + + @Test + @DisplayName("Deve buscar todos cartoes e retorna em uma lista") + void findAll_quandoCartoesExistem_retornaListaCartao() { + PartidasModel partidasModel = partidasRepository.saveAndFlush(new PartidasModel()); + PartidasModel partidasModel2 = partidasRepository.saveAndFlush(new PartidasModel()); + + FutModel futModel = futRepository.saveAndFlush(new FutModel()); + FutModel futModel2 = futRepository.saveAndFlush(new FutModel()); + + PeladeiroModel peladeiroModel = + peladeiroRepository.saveAndFlush( + new PeladeiroModel( + "João da Silva", + "joao@test.com", + "Apelido", + "descricao qualquer", + "81999999999", + "Destro")); + + PeladeiroModel peladeiroModel2 = + peladeiroRepository.saveAndFlush( + new PeladeiroModel( + "João da Silva2", + "joao2@test.com", + "Apelido", + "descricao qualquer2", + "81999999999", + "Destro")); + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel, peladeiroModel, futModel, TipoCartao.AMARELO)); + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel2, peladeiroModel2, futModel2, TipoCartao.AMARELO)); + + List todos = cartoesRepository.findAll(); + + assertTrue(todos.size() >= 2); + } + + @Test + @DisplayName("Deve buscar um cartao pelo IDe retornar um Optional") + void findById_quandoCartaoExiste_retornaCartao() { + PartidasModel partidasModel = partidasRepository.saveAndFlush(new PartidasModel()); + + FutModel futModel = futRepository.saveAndFlush(new FutModel()); + + PeladeiroModel peladeiroModel = + peladeiroRepository.saveAndFlush( + new PeladeiroModel( + "João da Silva", + "joao@test.com", + "Apelido", + "descricao qualquer", + "81999999999", + "Destro")); + + CartoesModel cartoesModel = + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel, peladeiroModel, futModel, TipoCartao.AMARELO)); + + Long id = cartoesModel.getId(); + + assertTrue(cartoesRepository.findById(id).isPresent()); + } + + @Test + @DisplayName("Deve buscar um cartao pelo ID e retornar um Optional vazio") + void findById_quandoCartaoExiste_retornaOptinalVazio() { + PartidasModel partidasModel = partidasRepository.saveAndFlush(new PartidasModel()); + + FutModel futModel = futRepository.saveAndFlush(new FutModel()); + + PeladeiroModel peladeiroModel = + peladeiroRepository.saveAndFlush( + new PeladeiroModel( + "João da Silva", + "joao@test.com", + "Apelido", + "descricao qualquer", + "81999999999", + "Destro")); + + CartoesModel cartoesModel = + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel, peladeiroModel, futModel, TipoCartao.AMARELO)); + + Long id = 99999L; + + assertTrue(cartoesRepository.findById(id).isEmpty()); + } + + @Test + @DisplayName("Deve buscar todos os cartoes de um Peladeiro especifico") + void findByPeladeiro_quandoCartoesDoPeladeiroExistem_retornaListCartoes() { + PartidasModel partidasModel = partidasRepository.saveAndFlush(new PartidasModel()); + + FutModel futModel = futRepository.saveAndFlush(new FutModel()); + + PeladeiroModel peladeiroModel = + peladeiroRepository.saveAndFlush( + new PeladeiroModel( + "João da Silva", + "joao@test.com", + "Apelido", + "descricao qualquer", + "81999999999", + "Destro")); + + PeladeiroModel peladeiroModel2 = + peladeiroRepository.saveAndFlush( + new PeladeiroModel( + "Paulo Silva", + "paulo@test.com", + "Apelido", + "descricao qualquer2", + "81999999999", + "Destro")); + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel, peladeiroModel, futModel, TipoCartao.AMARELO)); + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel, peladeiroModel, futModel, TipoCartao.AZUL)); + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel, peladeiroModel2, futModel, TipoCartao.AMARELO)); + + List todosCartoesPeladeiro = cartoesRepository.findByPeladeiro(peladeiroModel); + + assertEquals(2, todosCartoesPeladeiro.size()); + } + + @Test + @DisplayName("Deve buscar todos os cartoes de uma Partida especifico") + void findByPartida_quandoCartoesExistem_retornaListCartoes() { + PartidasModel partidasModel = partidasRepository.saveAndFlush(new PartidasModel()); + PartidasModel partidasModel2 = partidasRepository.saveAndFlush(new PartidasModel()); + + FutModel futModel = futRepository.saveAndFlush(new FutModel()); + + PeladeiroModel peladeiroModel = + peladeiroRepository.saveAndFlush( + new PeladeiroModel( + "João da Silva", + "joao@test.com", + "Apelido", + "descricao qualquer", + "81999999999", + "Destro")); + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel, peladeiroModel, futModel, TipoCartao.AMARELO)); + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel, peladeiroModel, futModel, TipoCartao.AZUL)); + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel2, peladeiroModel, futModel, TipoCartao.AMARELO)); + + List todosCartoesPeladeiro = cartoesRepository.findByPartida(partidasModel); + + assertEquals(2, todosCartoesPeladeiro.size()); + } + + @Test + @DisplayName("Deve buscar todos os cartoes de um Peladeiro especifico") + void findByFut_quandoCartoesExistem_retornaListCartoes() { + PartidasModel partidasModel = partidasRepository.saveAndFlush(new PartidasModel()); + + FutModel futModel = futRepository.saveAndFlush(new FutModel()); + FutModel futModel2 = futRepository.saveAndFlush(new FutModel()); + + PeladeiroModel peladeiroModel = + peladeiroRepository.saveAndFlush( + new PeladeiroModel( + "João da Silva", + "joao@test.com", + "Apelido", + "descricao qualquer", + "81999999999", + "Destro")); + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel, peladeiroModel, futModel, TipoCartao.AMARELO)); + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel, peladeiroModel, futModel, TipoCartao.AZUL)); + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel, peladeiroModel, futModel2, TipoCartao.AMARELO)); + + List todosCartoesPeladeiro = cartoesRepository.findByFut(futModel); + + assertEquals(2, todosCartoesPeladeiro.size()); + } + + @Test + @DisplayName("Deve retornar a quantidade de total de cada cartao de um peladeiro especifico") + void countByTipoAndPeladeiro_quandoCartoesExistem_retornaListCartoesPeladeiroContado() { + + PeladeiroModel peladeiroModel = + peladeiroRepository.saveAndFlush( + new PeladeiroModel( + "João da Silva", + "joao@test.com", + "Apelido", + "descricao qualquer", + "81999999999", + "Destro")); + + PeladeiroModel peladeiroModel2 = + peladeiroRepository.saveAndFlush( + new PeladeiroModel( + "Paulo Silva", + "paulo@test.com", + "Apelido", + "descricao qualquer2", + "81999999999", + "Destro")); + + FutModel futModel = futRepository.saveAndFlush(new FutModel()); + FutModel futModel2 = futRepository.saveAndFlush(new FutModel()); + + PartidasModel partidasModel = partidasRepository.saveAndFlush(new PartidasModel()); + PartidasModel partidasModel2 = partidasRepository.saveAndFlush(new PartidasModel()); + + // dados peladeiro 1 + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel, peladeiroModel, futModel, TipoCartao.AMARELO)); + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel, peladeiroModel, futModel, TipoCartao.AZUL)); + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel2, peladeiroModel, futModel2, TipoCartao.AMARELO)); + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel2, peladeiroModel, futModel2, TipoCartao.VERMELHO)); + + // Dados peladeiro 2 + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel, peladeiroModel2, futModel, TipoCartao.AMARELO)); + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel, peladeiroModel2, futModel, TipoCartao.AZUL)); + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel2, peladeiroModel2, futModel2, TipoCartao.AMARELO)); + + List contagem = + cartoesRepository.countByTipoAndPeladeiro(peladeiroModel.getId()); + + // Transformar em mapa para facilitar + Map resultado = + Arrays.stream(TipoCartao.values()) + .collect( + Collectors.toMap( + tipo -> tipo, + tipo -> + contagem.stream() + .filter(c -> c.getTipo() == tipo) + .map(CartaoCountProjection::getQuantidade) + .findFirst() + .orElse(0L))); + + assertEquals(2, resultado.get(TipoCartao.AMARELO)); + assertEquals(1, resultado.get(TipoCartao.AZUL)); + assertEquals(1, resultado.get(TipoCartao.VERMELHO)); + } + + @Test + @DisplayName("Deve retornar a quantidade de total de cada cartao de um Fut especifico") + void countByTipoAndFut__quandoCartoesExistem_retornaListCartoesFutContado() { + PeladeiroModel peladeiroModel = + peladeiroRepository.saveAndFlush( + new PeladeiroModel( + "João da Silva", + "joao@test.com", + "Apelido", + "descricao qualquer", + "81999999999", + "Destro")); + + PeladeiroModel peladeiroModel2 = + peladeiroRepository.saveAndFlush( + new PeladeiroModel( + "Paulo Silva", + "paulo@test.com", + "Apelido", + "descricao qualquer2", + "81999999999", + "Destro")); + + FutModel futModel = futRepository.saveAndFlush(new FutModel()); + FutModel futModel2 = futRepository.saveAndFlush(new FutModel()); + + PartidasModel partidasModel = partidasRepository.saveAndFlush(new PartidasModel()); + PartidasModel partidasModel2 = partidasRepository.saveAndFlush(new PartidasModel()); + + // dados Fut 1 + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel, peladeiroModel, futModel, TipoCartao.AMARELO)); + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel, peladeiroModel2, futModel, TipoCartao.AMARELO)); + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel, peladeiroModel, futModel, TipoCartao.AZUL)); + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel, peladeiroModel2, futModel, TipoCartao.AZUL)); + + // Dados fut 2 + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel2, peladeiroModel2, futModel2, TipoCartao.AMARELO)); + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel2, peladeiroModel, futModel2, TipoCartao.VERMELHO)); + + cartoesRepository.saveAndFlush( + new CartoesModel(partidasModel2, peladeiroModel, futModel2, TipoCartao.AMARELO)); + + List contagem = cartoesRepository.countByTipoAndFut(futModel.getId()); + + // Transformar em mapa para facilitar + Map resultado = + Arrays.stream(TipoCartao.values()) + .collect( + Collectors.toMap( + tipo -> tipo, + tipo -> + contagem.stream() + .filter(c -> c.getTipo() == tipo) + .map(CartaoCountProjection::getQuantidade) + .findFirst() + .orElse(0L))); + assertEquals(2, resultado.get(TipoCartao.AMARELO)); + assertEquals(2, resultado.get(TipoCartao.AZUL)); + assertEquals(0, resultado.get(TipoCartao.VERMELHO)); + } +} diff --git a/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/EditorRepositoryIT.java b/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/EditorRepositoryIT.java new file mode 100644 index 0000000..a06fb0d --- /dev/null +++ b/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/EditorRepositoryIT.java @@ -0,0 +1,90 @@ +package br.com.vemprofut.integration.repositories; + +import static org.junit.jupiter.api.Assertions.*; + +import br.com.vemprofut.configs.OAuth2LoginSuccessHandler; +import br.com.vemprofut.models.EditorModel; +import br.com.vemprofut.models.FutModel; +import br.com.vemprofut.models.PeladeiroModel; +import br.com.vemprofut.repositories.EditorRepository; +import br.com.vemprofut.repositories.FutRepository; +import br.com.vemprofut.repositories.PeladeiroRepository; +import br.com.vemprofut.services.implementacao.UploadLocalService; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.jwt.JwtDecoder; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.transaction.annotation.Transactional; + +@SpringBootTest +@Transactional +@ActiveProfiles("test") +public class EditorRepositoryIT { + + @MockitoBean private OAuth2AuthorizedClientService authorizedClientService; + + @MockitoBean private OAuth2LoginSuccessHandler oAuth2LoginSuccessHandler; + + @MockitoBean private ClientRegistrationRepository clientRegistrationRepository; + + @MockitoBean private UploadLocalService uploadLocalService; + + @MockitoBean private JwtDecoder jwtDecoder; + + @Autowired EditorRepository editorRepository; + + @Autowired PeladeiroRepository peladeiroRepository; + + @Autowired FutRepository futRepository; + + @Test + @DisplayName("Deve salvar um novo Editor em um Fut especifico") + void save_quandoEditorValido_retornaEditorSalvo() { + + FutModel futModel = futRepository.saveAndFlush(new FutModel()); + + PeladeiroModel peladeiroModel = + peladeiroRepository.saveAndFlush( + new PeladeiroModel( + "Marcio Teste", + "teste@test.com", + "Ronaldo", + "o cara nota 10", + "81992235678", + "Destro")); + + EditorModel editorModel = + editorRepository.saveAndFlush(new EditorModel(peladeiroModel, futModel)); + + assertNotNull(editorModel.getId()); + assertTrue(editorModel.getId() > 0); + } + + @Test + @DisplayName("Deve buscar e retornar um Editor salvo") + void findById_quandoEditorExiste_retornaEditorSalvo() { + FutModel futModel = futRepository.saveAndFlush(new FutModel()); + + PeladeiroModel peladeiroModel = + peladeiroRepository.saveAndFlush( + new PeladeiroModel( + "Marcio Teste", + "teste@test.com", + "Ronaldo", + "o cara nota 10", + "81992235678", + "Destro")); + + EditorModel editorModel = + editorRepository.saveAndFlush(new EditorModel(peladeiroModel, futModel)); + + Long id = editorModel.getId(); + + assertTrue(editorRepository.findById(id).isPresent()); + } +} diff --git a/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/FutRepositoryIT.java b/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/FutRepositoryIT.java new file mode 100644 index 0000000..569aeeb --- /dev/null +++ b/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/FutRepositoryIT.java @@ -0,0 +1,78 @@ +package br.com.vemprofut.integration.repositories; + +import static org.junit.jupiter.api.Assertions.*; + +import br.com.vemprofut.configs.OAuth2LoginSuccessHandler; +import br.com.vemprofut.models.FutModel; +import br.com.vemprofut.repositories.FutRepository; +import br.com.vemprofut.services.implementacao.UploadLocalService; +import jakarta.transaction.Transactional; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.jwt.JwtDecoder; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.bean.override.mockito.MockitoBean; + +@SpringBootTest +@Transactional +@ActiveProfiles("test") +public class FutRepositoryIT { + + @MockitoBean private OAuth2AuthorizedClientService authorizedClientService; + + @MockitoBean private OAuth2LoginSuccessHandler oAuth2LoginSuccessHandler; + + @MockitoBean private ClientRegistrationRepository clientRegistrationRepository; + + @MockitoBean private UploadLocalService uploadLocalService; + + @MockitoBean private JwtDecoder jwtDecoder; + + @Autowired FutRepository futRepository; + + @Test + // @DisplayName("deve gerar um Fut e retornar um id") + public void deveSalvarFut() { + + FutModel futModel = new FutModel("Fut teste", 4, 10, 2, null, null); + + FutModel salvo = futRepository.save(futModel); + + assertNotNull(salvo.getId()); + assertTrue(salvo.getId() > 0); + } + + @Test + // @DisplayName("findById deve retornar Optional vazio para id inexistente") + public void findByIdInexistente() { + Optional encontrado = futRepository.findById(9999L); + assertTrue(encontrado.isEmpty()); + } + + @Test + // @DisplayName("Deve retornar uma lista de Peladeiro") + public void findAllDeveRetornaListaFut() { + futRepository.saveAndFlush(new FutModel("Fut teste1", 4, 10, 2, null, null)); + futRepository.saveAndFlush(new FutModel("Fut teste2", 4, 10, 2, null, null)); + + List lista = futRepository.findAll(); + assertTrue(lista.size() >= 2); + } + + @Test + // @DisplayName("deve deletar um registro") + public void deleteFut() { + FutModel f = futRepository.saveAndFlush(new FutModel("Fut teste1", 4, 10, 2, null, null)); + + Long id = f.getId(); + futRepository.deleteById(id); + assertFalse(futRepository.findById(id).isPresent()); + } + + // TODO: implementar os testes de Fut buscarFutComListPeladeiros e buscarFutComListEditores +} diff --git a/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/PeladeiroRepositoryIT.java b/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/PeladeiroRepositoryIT.java new file mode 100644 index 0000000..826b7a5 --- /dev/null +++ b/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/PeladeiroRepositoryIT.java @@ -0,0 +1,147 @@ +package br.com.vemprofut.integration.repositories; + +import static org.junit.jupiter.api.Assertions.*; + +import br.com.vemprofut.configs.OAuth2LoginSuccessHandler; +import br.com.vemprofut.models.PeladeiroModel; +import br.com.vemprofut.repositories.PeladeiroRepository; +import br.com.vemprofut.services.implementacao.UploadLocalService; +import jakarta.transaction.Transactional; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.jwt.JwtDecoder; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.bean.override.mockito.MockitoBean; + +@SpringBootTest +@Transactional +@ActiveProfiles("test") +public class PeladeiroRepositoryIT { + /* + - Isso roda usando MySQL real via Docker + - Isso roda com Flyway + - Sem mocks — integração real + + Porque: + - Garante que o código realmente funciona no mesmo banco da produção. + - Evita problemas de comportamento diferente entre H2 <-> MySQL. + - Torna o ambiente de Dev ≈ Prod. + */ + // Mocka o serviço que o Spring não está encontrando + /* + Ao usar o @MockitoBean, você diz ao Spring: Não se preocupe em criar essa dependência real; + use um objeto de simulação (mock) no lugar para que o contexto possa ser inicializado. + */ + @MockitoBean private OAuth2AuthorizedClientService authorizedClientService; + + // Opcional: Mocka o próprio Handler para evitar que ele tente rodar lógica + @MockitoBean private OAuth2LoginSuccessHandler oAuth2LoginSuccessHandler; + + @MockitoBean private ClientRegistrationRepository clientRegistrationRepository; + + @MockitoBean private UploadLocalService uploadLocalService; + + @MockitoBean private JwtDecoder jwtDecoder; + + @Autowired PeladeiroRepository peladeiroRepository; + + @Test + @DisplayName("Deve gerar salvar um paladeiro e retornar um id") + public void save_quandoPeladeiroValido_retornaIdGerado() { + PeladeiroModel peladeiroModel = + new PeladeiroModel( + "Marcio Teste", "teste@test.com", "Ronaldo", "o cara nota 10", "81992235678", "Destro"); + + PeladeiroModel salvo = peladeiroRepository.save(peladeiroModel); + + assertNotNull(salvo.getId()); + assertTrue(salvo.getId() > 0); + } + + @Test + @DisplayName("findById deve retornar Optional vazio para id inexistente") + public void findById_quandoIdInexistente_retornaOptionalVazio() { + Optional encontrado = peladeiroRepository.findById(9999L); + assertTrue(encontrado.isEmpty()); + } + + @Test + @DisplayName("findAll deve retornar lista com todos os Peladeiros") + public void findAll_quandoExistemRegistros_retornaListaComTodos() { + peladeiroRepository.saveAndFlush( + new PeladeiroModel("A", "a@test.com", "A", "333", "81555555555", "Destro")); + peladeiroRepository.saveAndFlush( + new PeladeiroModel("B", "b@test.com", "B", "444", "82000000000", "Destro")); + + List todos = peladeiroRepository.findAll(); + assertTrue(todos.size() >= 2); + } + + @Test + @DisplayName("deleteById deve remover um registro existente") + public void deleteById_quandoIdExistente_registroEhRemovido() { + PeladeiroModel p = + peladeiroRepository.saveAndFlush( + new PeladeiroModel("C", "c@test.com", "C", "", "3", "Destro")); + Long id = p.getId(); + peladeiroRepository.deleteById(id); + assertFalse(peladeiroRepository.findById(id).isPresent()); + } + + @Test + @DisplayName("deve retorna de o email ja foi castrado ou nao") + public void existsByEmail_quandoEmailCadastrado_retornaTrue() { + peladeiroRepository.saveAndFlush( + new PeladeiroModel("C", "test@test.com", "C", "", "3", "Destro")); + String email = "test@test.com"; + Boolean exist = peladeiroRepository.existsByEmail(email); + + assertTrue(exist); + } + + @Test + @DisplayName("deve retorna falso de o email ja foi castrado ou nao") + public void existsByEmail_quandoEmailNaoCadastrado_retornaFalse() { + peladeiroRepository.saveAndFlush( + new PeladeiroModel("C", "test@test.com", "C", "", "3", "Destro")); + String email = "test2@test.com"; + Boolean exist = peladeiroRepository.existsByEmail(email); + + assertFalse(exist); + } + + @Test + // @DisplayName("Deve retornar um Peladeiro ao pesquisar pelo email") + public void buscarPeladeiroPeloEmailRetornaPeladeiro() { + PeladeiroModel p = + peladeiroRepository.saveAndFlush( + new PeladeiroModel("C", "test@test.com", "C", "descricao", "377777777777", "Destro")); + String email = "test@test.com"; + PeladeiroModel peladeiroModel = peladeiroRepository.findByEmail(email); + + assertEquals(p.getNome(), peladeiroModel.getNome()); + assertEquals(p.getEmail(), peladeiroModel.getEmail()); + assertEquals(p.getApelido(), peladeiroModel.getApelido()); + assertEquals(p.getWhatsapp(), peladeiroModel.getWhatsapp()); + assertEquals(p.getDescricao(), peladeiroModel.getDescricao()); + assertEquals(p.getPeDominante(), peladeiroModel.getPeDominante()); + } + + @Test + // @DisplayName("Deve retornar null ao buscar um peladeiro pelo email") + public void buscarPeladeiroPeloEmailRetornaNull() { + PeladeiroModel p = + peladeiroRepository.saveAndFlush( + new PeladeiroModel("C", "test@test.com", "C", "descricao", "377777777777", "Destro")); + String email = "test1@test.com"; + PeladeiroModel peladeiroModel = peladeiroRepository.findByEmail(email); + + assertNull(peladeiroModel); + } +} diff --git a/VemProFutApi/src/test/resources/application-test.properties b/VemProFutApi/src/test/resources/application-test.properties new file mode 100644 index 0000000..dd082b7 --- /dev/null +++ b/VemProFutApi/src/test/resources/application-test.properties @@ -0,0 +1,12 @@ +# MODE=MySQL = Isso faz o H2 emular a sintaxe do MySQL (tipos, fun\u00E7\u00F5es, etc.), permitindo que suas migra\u00E7\u00F5es rodem sem precisar reescrev\u00EA-las. +spring.datasource.url=jdbc:h2:mem:testdb;MODE=MySQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE +spring.datasource.driver-class-name=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password= + +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +spring.jpa.hibernate.ddl-auto=none + +# Flyway +spring.flyway.enabled=true +spring.flyway.locations=classpath:db/migration From b649173489bb7472e25dd0b9ece01963e6aaccb7 Mon Sep 17 00:00:00 2001 From: "Marcio J. Costa" <87935294+MarcioCosta013@users.noreply.github.com> Date: Mon, 15 Dec 2025 23:17:42 -0300 Subject: [PATCH 09/13] Clean up comments in CI Develop workflow (#14) Removed comments about the workflow's purpose and third-party actions. --- .github/workflows/ci-develop.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/ci-develop.yml b/.github/workflows/ci-develop.yml index f2ef092..ba2f8ec 100644 --- a/.github/workflows/ci-develop.yml +++ b/.github/workflows/ci-develop.yml @@ -1,11 +1,3 @@ -# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven - -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - name: CI Develop on: From 1f8b02cf58ea1e2af658478c02d76f01cfbb9245 Mon Sep 17 00:00:00 2001 From: "Marcio J. Costa" <87935294+MarcioCosta013@users.noreply.github.com> Date: Fri, 2 Jan 2026 12:43:54 -0300 Subject: [PATCH 10/13] Feature/create testes unitarios (#15) (#16) * test(test unitario create peladeiro): - Primeiro teste unitario create peladeiro - Adicionei a depencia e o plugin para adicionar o mockito como Java Agent ja preparando para mudancas futuras do JDK; - Usando o AssertJ para os Testes unitarios * test(PeladeiroServiceTest): - concluida a implementacao dos testes de PeladeiroService; - algumas modificacoes pontuais em PeladeiroService sem mudar no comportamento do codigo; - adicionei anotacoes lombok no CartoesResumoResponse em controlles/response para adicionar um contrutor com todos os argumentos; * refactore(Trocando string por PeDominanteEnum): - troquei todas as implementacoes de PeDominate que estavam em tipo string para usar o Enum PeDominante; * doc(Adicionando informacoes ao readme.md): -Adicionei como fazer para rodar o projeto em diferentes config: test, prod e dev; --- VemProFutApi/README.md | 52 ++- VemProFutApi/pom.xml | 16 +- .../configs/OAuth2LoginSuccessHandler.java | 3 +- .../request/SavePeladeiroRequestDTO.java | 3 +- .../request/UpdatePeladeiroRequestDTO.java | 3 +- .../response/CartoesResumoResponseDTO.java | 4 + .../response/PeladeiroDetailResponse.java | 3 +- .../response/SavePeladeiroResponseDTO.java | 3 +- .../response/UpdatePeladeiroResponseDTO.java | 3 +- .../com/vemprofut/models/PeladeiroModel.java | 6 +- .../implementacao/PeladeiroService.java | 10 +- .../vemprofut/VemProFutApplicationTests.java | 8 +- .../repositories/BanimentoRepositoryIT.java | 9 +- .../repositories/CartoesRepositoryIT.java | 27 +- .../repositories/EditorRepositoryIT.java | 5 +- .../repositories/GolsPartidaRepositoryIT.java | 23 +- .../repositories/PeladeiroRepositoryIT.java | 24 +- .../unit/services/FutServiceTest.java | 205 ++++++++++++ .../unit/services/PeladeiroServiceTest.java | 303 ++++++++++++++++++ 19 files changed, 647 insertions(+), 63 deletions(-) create mode 100644 VemProFutApi/src/test/java/br/com/vemprofut/unit/services/FutServiceTest.java create mode 100644 VemProFutApi/src/test/java/br/com/vemprofut/unit/services/PeladeiroServiceTest.java diff --git a/VemProFutApi/README.md b/VemProFutApi/README.md index 0f08e16..dc74b21 100644 --- a/VemProFutApi/README.md +++ b/VemProFutApi/README.md @@ -1,4 +1,4 @@ -# VemProFut! API (Em andamento...) +# VemProFut! API (Concluída) ![Java](https://img.shields.io/badge/java-%23ED8B00.svg?style=for-the-badge&logo=openjdk&logoColor=white) ![Spring](https://img.shields.io/badge/spring-%236DB33F.svg?style=for-the-badge&logo=spring&logoColor=white) ![MySQL](https://img.shields.io/badge/mysql-4479A1.svg?style=for-the-badge&logo=mysql&logoColor=white) @@ -6,4 +6,54 @@ API base de todo o projeto VemProFut! +## Organização das pastas: + +``` + VemProFutApi/ + ├── src/ → Código-fonte principal da aplicação + │ ├── main/java/... → Classes Java (controllers, services, repositories, models) + │ ├── main/resources → Configurações (application.properties, templates, static) + │ └── test/java/... → Testes automatizados + │ + ├── mysql-init/ → Scripts de inicialização do banco MySQL + │ └── *.sql → Criação de tabelas, inserts iniciais + │ + ├── .mvn/wrapper/ → Arquivos do Maven Wrapper (executar sem instalar Maven) + │ + ├── Dockerfile → Configuração para criar imagem Docker da API + ├── Docker-compose.yml → Orquestração de containers (API + MySQL) + │ + ├── pom.xml → Arquivo de configuração do Maven (dependências e build) + ├── mvnw / mvnw.cmd → Scripts para rodar Maven Wrapper (Linux/Windows) + │ + ├── .env.exemple → Exemplo de variáveis de ambiente (configuração DB, etc.) + ├── .gitignore → Arquivos/pastas ignorados pelo Git + ├── .gitattributes → Configurações de atributos do Git + ├── excludeFilter.xml → Configuração de exclusões (provavelmente Sonar ou Checkstyle) + └── README.md → Documentação inicial da API +``` + +# 🚀 Como rodar a aplicação + +A aplicação suporta múltiplos perfis (`dev`, `test`, `prod`) configurados via **Spring Profiles**. + +--- +## 🏭 Ambiente de Produção (`prod`) + +Rodar com **Docker Compose**: +```bash +docker compose up +``` +## 🔧 Ambiente de Desenvolvimento (`dev`) + +Rodar diretamente com **Maven**: +```bash +mvn spring-boot:run -Dspring-boot.run.profiles=dev +``` +## 🧪 Ambiente de Testes (`test`) + +Rodar diretamente com **Maven**: +```bash +mvn test +``` diff --git a/VemProFutApi/pom.xml b/VemProFutApi/pom.xml index fa31b61..49ef2cb 100644 --- a/VemProFutApi/pom.xml +++ b/VemProFutApi/pom.xml @@ -134,12 +134,12 @@ test + - junit - junit - test + org.mockito + mockito-inline + 5.2.0 - @@ -220,6 +220,14 @@ + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + + + diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/configs/OAuth2LoginSuccessHandler.java b/VemProFutApi/src/main/java/br/com/vemprofut/configs/OAuth2LoginSuccessHandler.java index 1381d0d..f4aa470 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/configs/OAuth2LoginSuccessHandler.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/configs/OAuth2LoginSuccessHandler.java @@ -3,6 +3,7 @@ import br.com.vemprofut.mappers.IHistoricoPeladeiroMapper; import br.com.vemprofut.models.DTOs.HistoricoPeladeiroDTO; import br.com.vemprofut.models.PeladeiroModel; +import br.com.vemprofut.models.enuns.PeDominante; import br.com.vemprofut.repositories.PeladeiroRepository; import br.com.vemprofut.services.IHistoricoPeladeiroService; import jakarta.servlet.http.HttpServletRequest; @@ -62,7 +63,7 @@ public void onAuthenticationSuccess( usuario.setApelido(name); usuario.setDescricao("Usuário criado via OAuth2"); usuario.setWhatsapp("000000000"); - usuario.setPeDominante("Destro"); // escolha padrão + usuario.setPeDominante(PeDominante.DESTRO); // escolha padrão usuario.setAuthProvider(provider); usuario.setFotoUrl(picture); usuario.setHistoricoPeladeiro(historicoPeladeiroMapper.toModel(historicoPeladeiro)); diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/request/SavePeladeiroRequestDTO.java b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/request/SavePeladeiroRequestDTO.java index 2fda2d0..cce4520 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/request/SavePeladeiroRequestDTO.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/request/SavePeladeiroRequestDTO.java @@ -1,5 +1,6 @@ package br.com.vemprofut.controllers.request; +import br.com.vemprofut.models.enuns.PeDominante; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -9,5 +10,5 @@ public record SavePeladeiroRequestDTO( @Email String email, @NotNull String apelido, @NotBlank String descricao, - @NotNull String peDominante, + @NotNull PeDominante peDominante, @NotBlank String whatsapp) {} diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/request/UpdatePeladeiroRequestDTO.java b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/request/UpdatePeladeiroRequestDTO.java index 8fbb2a3..c86dd68 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/request/UpdatePeladeiroRequestDTO.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/request/UpdatePeladeiroRequestDTO.java @@ -1,5 +1,6 @@ package br.com.vemprofut.controllers.request; +import br.com.vemprofut.models.enuns.PeDominante; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -9,5 +10,5 @@ public record UpdatePeladeiroRequestDTO( @Email String email, @NotNull String apelido, @NotBlank String descricao, - @NotNull String peDominante, + @NotNull PeDominante peDominante, @NotBlank String whatsapp) {} diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/CartoesResumoResponseDTO.java b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/CartoesResumoResponseDTO.java index cdc453a..356778f 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/CartoesResumoResponseDTO.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/CartoesResumoResponseDTO.java @@ -1,10 +1,14 @@ package br.com.vemprofut.controllers.response; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; @Getter @Setter +@NoArgsConstructor +@AllArgsConstructor public class CartoesResumoResponseDTO { private Integer azul; private Integer amarelo; diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/PeladeiroDetailResponse.java b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/PeladeiroDetailResponse.java index 746c8dc..8a9545c 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/PeladeiroDetailResponse.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/PeladeiroDetailResponse.java @@ -1,5 +1,6 @@ package br.com.vemprofut.controllers.response; +import br.com.vemprofut.models.enuns.PeDominante; import com.fasterxml.jackson.annotation.JsonProperty; public record PeladeiroDetailResponse( @@ -9,5 +10,5 @@ public record PeladeiroDetailResponse( @JsonProperty("apelido") String apelido, @JsonProperty("descricao") String descricao, @JsonProperty("whatsapp") String whatsapp, - @JsonProperty("peDominante") String peDominante, + @JsonProperty("peDominante") PeDominante peDominante, @JsonProperty("numeroCartoes") CartoesResumoResponseDTO cartoes) {} diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/SavePeladeiroResponseDTO.java b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/SavePeladeiroResponseDTO.java index 64a52b3..e8162d2 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/SavePeladeiroResponseDTO.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/SavePeladeiroResponseDTO.java @@ -1,5 +1,6 @@ package br.com.vemprofut.controllers.response; +import br.com.vemprofut.models.enuns.PeDominante; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; @@ -9,7 +10,7 @@ public record SavePeladeiroResponseDTO( @JsonProperty("email") String email, @JsonProperty("apelido") String apelido, @JsonProperty("descricao") String descricao, - @JsonProperty("peDominante") String peDominante, + @JsonProperty("peDominante") PeDominante peDominante, @JsonProperty("whatsapp") String whatsapp, @JsonProperty("historicoPeladeiro") Long historicoPeladeiro, @JsonProperty("fotoUrl") String fotoUrl, diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/UpdatePeladeiroResponseDTO.java b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/UpdatePeladeiroResponseDTO.java index 02fcf35..515bf17 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/UpdatePeladeiroResponseDTO.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/response/UpdatePeladeiroResponseDTO.java @@ -1,5 +1,6 @@ package br.com.vemprofut.controllers.response; +import br.com.vemprofut.models.enuns.PeDominante; import com.fasterxml.jackson.annotation.JsonProperty; public record UpdatePeladeiroResponseDTO( @@ -8,5 +9,5 @@ public record UpdatePeladeiroResponseDTO( @JsonProperty("apelido") String apelido, @JsonProperty("descricao") String descricao, @JsonProperty("whatsapp") String whatsapp, - @JsonProperty("peDominante") String peDominante, + @JsonProperty("peDominante") PeDominante peDominante, @JsonProperty("fotoURL") String fotoUrl) {} diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/models/PeladeiroModel.java b/VemProFutApi/src/main/java/br/com/vemprofut/models/PeladeiroModel.java index bd1176c..85b8ec8 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/models/PeladeiroModel.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/models/PeladeiroModel.java @@ -1,5 +1,6 @@ package br.com.vemprofut.models; +import br.com.vemprofut.models.enuns.PeDominante; import jakarta.persistence.*; import java.util.ArrayList; import java.util.List; @@ -19,7 +20,7 @@ public PeladeiroModel( String apelido, String descricao, String whatsapp, - String peDominante) { + PeDominante peDominante) { this.nome = nome; this.email = email; this.apelido = apelido; @@ -49,7 +50,8 @@ public PeladeiroModel( private String whatsapp; @Column(name = "pe_dominante_peladeiro", nullable = false, length = 10) - private String peDominante; + @Enumerated(EnumType.STRING) // salva como texto no banco + private PeDominante peDominante; // TODO: mudar para usar um enum... @ToString.Exclude @OneToOne diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/PeladeiroService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/PeladeiroService.java index 7f894dd..0201483 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/PeladeiroService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/PeladeiroService.java @@ -29,7 +29,7 @@ public class PeladeiroService implements IPeladeiroService { @Autowired private PeladeiroRepository repository; - @Autowired private IPeladeiroMapper IPeladeiroMapper; + @Autowired private IPeladeiroMapper peladeiroMapper; @Autowired private IHistoricoPeladeiroMapper historicoMapper; @@ -42,14 +42,14 @@ public class PeladeiroService implements IPeladeiroService { public SavePeladeiroResponseDTO create(SavePeladeiroRequestDTO dto) { queryService.verifyEmail(dto.email()); log.info("Email verificado!"); - PeladeiroModel peladeiroModel = IPeladeiroMapper.saveRequestToModel(dto); + PeladeiroModel peladeiroModel = peladeiroMapper.saveRequestToModel(dto); PeladeiroModel peladeiroSalvo = repository.save(peladeiroModel); HistoricoPeladeiroDTO historico = historicoPeladeiroService.create(); peladeiroSalvo.setHistoricoPeladeiro(historicoMapper.toModel(historico)); log.info("Peladeiro cadastrado com sucesso!"); - return IPeladeiroMapper.modelToSaveResponse(repository.save(peladeiroSalvo)); + return peladeiroMapper.modelToSaveResponse(repository.save(peladeiroSalvo)); } @Override @@ -66,7 +66,7 @@ public UpdatePeladeiroResponseDTO update(Long id, UpdatePeladeiroRequestDTO dto) peladeiroModel.setPeDominante(dto.peDominante()); log.info("Peladeiro alterado com sucesso!"); - return IPeladeiroMapper.modelToUpdateResponse(repository.save(peladeiroModel)); + return peladeiroMapper.modelToUpdateResponse(repository.save(peladeiroModel)); } @Override @@ -99,7 +99,7 @@ public PeladeiroModel findByIdModel(Long id) { // // return repository.findAll() // .stream() - // .map(IPeladeiroMapper::toDTO) + // .map(peladeiroMapper::toDTO) // .toList(); // } diff --git a/VemProFutApi/src/test/java/br/com/vemprofut/VemProFutApplicationTests.java b/VemProFutApi/src/test/java/br/com/vemprofut/VemProFutApplicationTests.java index 5f15b0d..5b58373 100644 --- a/VemProFutApi/src/test/java/br/com/vemprofut/VemProFutApplicationTests.java +++ b/VemProFutApi/src/test/java/br/com/vemprofut/VemProFutApplicationTests.java @@ -1,12 +1,6 @@ package br.com.vemprofut; -import org.springframework.boot.test.context.SpringBootTest; - - class VemProFutApplicationTests { - - void contextLoads() { - } - + void contextLoads() {} } diff --git a/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/BanimentoRepositoryIT.java b/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/BanimentoRepositoryIT.java index 19f9e2c..74198e6 100644 --- a/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/BanimentoRepositoryIT.java +++ b/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/BanimentoRepositoryIT.java @@ -6,6 +6,7 @@ import br.com.vemprofut.models.BanimentoModel; import br.com.vemprofut.models.FutModel; import br.com.vemprofut.models.PeladeiroModel; +import br.com.vemprofut.models.enuns.PeDominante; import br.com.vemprofut.repositories.BanimentoRepository; import br.com.vemprofut.repositories.FutRepository; import br.com.vemprofut.repositories.PeladeiroRepository; @@ -57,7 +58,7 @@ public void save_quandoBanimentoValido_retornaIdGerado() { "Apelido", "descricao qualquer", "81999999999", - "Destro")); + PeDominante.DESTRO)); BanimentoModel banimentoModel = new BanimentoModel( @@ -86,7 +87,7 @@ public void findAll_quandoExistemRegistros_retornaListaComTodos() { "Apelido", "descricao qualquer", "81999999999", - "Destro")); + PeDominante.DESTRO)); PeladeiroModel peladeiroModel2 = peladeiroRepository.saveAndFlush( new PeladeiroModel( @@ -95,7 +96,7 @@ public void findAll_quandoExistemRegistros_retornaListaComTodos() { "Apelido2", "descricao qualquer2", "81999999999", - "Destro")); + PeDominante.DESTRO)); BanimentoModel banimentoModel1 = new BanimentoModel( @@ -132,7 +133,7 @@ public void deleteById_quandoIdBanimentoExistir_registroRemovido() { "Apelido", "descricao qualquer", "81999999999", - "Destro")); + PeDominante.DESTRO)); BanimentoModel banimentoModel = new BanimentoModel( diff --git a/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/CartoesRepositoryIT.java b/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/CartoesRepositoryIT.java index a79912a..b8a0bc9 100644 --- a/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/CartoesRepositoryIT.java +++ b/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/CartoesRepositoryIT.java @@ -8,6 +8,7 @@ import br.com.vemprofut.models.FutModel; import br.com.vemprofut.models.PartidasModel; import br.com.vemprofut.models.PeladeiroModel; +import br.com.vemprofut.models.enuns.PeDominante; import br.com.vemprofut.models.enuns.TipoCartao; import br.com.vemprofut.repositories.CartoesRepository; import br.com.vemprofut.repositories.FutRepository; @@ -69,7 +70,7 @@ void save_quandoCartaoValido_retornaIdGerado() { "Apelido", "descricao qualquer", "81999999999", - "Destro")); + PeDominante.DESTRO)); CartoesModel cartoesModel = cartoesRepository.saveAndFlush( @@ -96,7 +97,7 @@ void findAll_quandoCartoesExistem_retornaListaCartao() { "Apelido", "descricao qualquer", "81999999999", - "Destro")); + PeDominante.DESTRO)); PeladeiroModel peladeiroModel2 = peladeiroRepository.saveAndFlush( @@ -106,7 +107,7 @@ void findAll_quandoCartoesExistem_retornaListaCartao() { "Apelido", "descricao qualquer2", "81999999999", - "Destro")); + PeDominante.DESTRO)); cartoesRepository.saveAndFlush( new CartoesModel(partidasModel, peladeiroModel, futModel, TipoCartao.AMARELO)); @@ -134,7 +135,7 @@ void findById_quandoCartaoExiste_retornaCartao() { "Apelido", "descricao qualquer", "81999999999", - "Destro")); + PeDominante.DESTRO)); CartoesModel cartoesModel = cartoesRepository.saveAndFlush( @@ -160,7 +161,7 @@ void findById_quandoCartaoExiste_retornaOptinalVazio() { "Apelido", "descricao qualquer", "81999999999", - "Destro")); + PeDominante.DESTRO)); CartoesModel cartoesModel = cartoesRepository.saveAndFlush( @@ -186,7 +187,7 @@ void findByPeladeiro_quandoCartoesDoPeladeiroExistem_retornaListCartoes() { "Apelido", "descricao qualquer", "81999999999", - "Destro")); + PeDominante.DESTRO)); PeladeiroModel peladeiroModel2 = peladeiroRepository.saveAndFlush( @@ -196,7 +197,7 @@ void findByPeladeiro_quandoCartoesDoPeladeiroExistem_retornaListCartoes() { "Apelido", "descricao qualquer2", "81999999999", - "Destro")); + PeDominante.DESTRO)); cartoesRepository.saveAndFlush( new CartoesModel(partidasModel, peladeiroModel, futModel, TipoCartao.AMARELO)); @@ -228,7 +229,7 @@ void findByPartida_quandoCartoesExistem_retornaListCartoes() { "Apelido", "descricao qualquer", "81999999999", - "Destro")); + PeDominante.DESTRO)); cartoesRepository.saveAndFlush( new CartoesModel(partidasModel, peladeiroModel, futModel, TipoCartao.AMARELO)); @@ -260,7 +261,7 @@ void findByFut_quandoCartoesExistem_retornaListCartoes() { "Apelido", "descricao qualquer", "81999999999", - "Destro")); + PeDominante.DESTRO)); cartoesRepository.saveAndFlush( new CartoesModel(partidasModel, peladeiroModel, futModel, TipoCartao.AMARELO)); @@ -288,7 +289,7 @@ void countByTipoAndPeladeiro_quandoCartoesExistem_retornaListCartoesPeladeiroCon "Apelido", "descricao qualquer", "81999999999", - "Destro")); + PeDominante.DESTRO)); PeladeiroModel peladeiroModel2 = peladeiroRepository.saveAndFlush( @@ -298,7 +299,7 @@ void countByTipoAndPeladeiro_quandoCartoesExistem_retornaListCartoesPeladeiroCon "Apelido", "descricao qualquer2", "81999999999", - "Destro")); + PeDominante.DESTRO)); FutModel futModel = futRepository.saveAndFlush(new FutModel()); FutModel futModel2 = futRepository.saveAndFlush(new FutModel()); @@ -361,7 +362,7 @@ void countByTipoAndFut__quandoCartoesExistem_retornaListCartoesFutContado() { "Apelido", "descricao qualquer", "81999999999", - "Destro")); + PeDominante.DESTRO)); PeladeiroModel peladeiroModel2 = peladeiroRepository.saveAndFlush( @@ -371,7 +372,7 @@ void countByTipoAndFut__quandoCartoesExistem_retornaListCartoesFutContado() { "Apelido", "descricao qualquer2", "81999999999", - "Destro")); + PeDominante.DESTRO)); FutModel futModel = futRepository.saveAndFlush(new FutModel()); FutModel futModel2 = futRepository.saveAndFlush(new FutModel()); diff --git a/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/EditorRepositoryIT.java b/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/EditorRepositoryIT.java index a06fb0d..13a5763 100644 --- a/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/EditorRepositoryIT.java +++ b/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/EditorRepositoryIT.java @@ -6,6 +6,7 @@ import br.com.vemprofut.models.EditorModel; import br.com.vemprofut.models.FutModel; import br.com.vemprofut.models.PeladeiroModel; +import br.com.vemprofut.models.enuns.PeDominante; import br.com.vemprofut.repositories.EditorRepository; import br.com.vemprofut.repositories.FutRepository; import br.com.vemprofut.repositories.PeladeiroRepository; @@ -56,7 +57,7 @@ void save_quandoEditorValido_retornaEditorSalvo() { "Ronaldo", "o cara nota 10", "81992235678", - "Destro")); + PeDominante.CANHOTO)); EditorModel editorModel = editorRepository.saveAndFlush(new EditorModel(peladeiroModel, futModel)); @@ -78,7 +79,7 @@ void findById_quandoEditorExiste_retornaEditorSalvo() { "Ronaldo", "o cara nota 10", "81992235678", - "Destro")); + PeDominante.CANHOTO)); EditorModel editorModel = editorRepository.saveAndFlush(new EditorModel(peladeiroModel, futModel)); diff --git a/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/GolsPartidaRepositoryIT.java b/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/GolsPartidaRepositoryIT.java index 12aacbb..254eabc 100644 --- a/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/GolsPartidaRepositoryIT.java +++ b/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/GolsPartidaRepositoryIT.java @@ -6,6 +6,7 @@ import br.com.vemprofut.models.GolsPartidaModel; import br.com.vemprofut.models.PartidasModel; import br.com.vemprofut.models.PeladeiroModel; +import br.com.vemprofut.models.enuns.PeDominante; import br.com.vemprofut.repositories.GolsPartidaRepository; import br.com.vemprofut.repositories.PartidasRepository; import br.com.vemprofut.repositories.PeladeiroRepository; @@ -54,7 +55,7 @@ void save_quandoGolPartidaValido_retornaGolPartidaSalvo() { "Ronaldo", "o cara nota 10", "81992235678", - "Destro")); + PeDominante.DESTRO)); PartidasModel partidasSalvo = partidasRepository.saveAndFlush(new PartidasModel()); GolsPartidaModel golsPartidaSalvo = @@ -75,7 +76,7 @@ void findById_quandoGolPartidaExiste_retornaGolPartidaModel() { "Ronaldo", "o cara nota 10", "81992235678", - "Destro")); + PeDominante.DESTRO)); PartidasModel partidasSalvo = partidasRepository.saveAndFlush(new PartidasModel()); GolsPartidaModel golsPartidaSalvo = golsPartidaRepository.save(new GolsPartidaModel(peladeiroSalvo, partidasSalvo)); @@ -95,7 +96,7 @@ void findById_quandoGolPartidaInexistente_retornaNull() { "Ronaldo", "o cara nota 10", "81992235678", - "Destro")); + PeDominante.DESTRO)); PartidasModel partidasSalvo = partidasRepository.saveAndFlush(new PartidasModel()); golsPartidaRepository.save(new GolsPartidaModel(peladeiroSalvo, partidasSalvo)); @@ -113,7 +114,7 @@ void existsById_quandoGolPartidaExiste_retornaTrue() { "Ronaldo", "o cara nota 10", "81992235678", - "Destro")); + PeDominante.DESTRO)); PartidasModel partidasSalvo = partidasRepository.saveAndFlush(new PartidasModel()); GolsPartidaModel golsPartidaSalvo = golsPartidaRepository.save(new GolsPartidaModel(peladeiroSalvo, partidasSalvo)); @@ -133,7 +134,7 @@ void existsById_quandoGolPartidaExiste_retornaFalse() { "Ronaldo", "o cara nota 10", "81992235678", - "Destro")); + PeDominante.DESTRO)); PartidasModel partidasSalvo = partidasRepository.saveAndFlush(new PartidasModel()); golsPartidaRepository.save(new GolsPartidaModel(peladeiroSalvo, partidasSalvo)); @@ -152,7 +153,7 @@ void findAll_quandoExitemGolPartidas_retornaListGolPartidaModel() { "Ronaldo", "o cara nota 10", "81992235678", - "Destro")); + PeDominante.DESTRO)); PartidasModel partidasSalvo = partidasRepository.saveAndFlush(new PartidasModel()); golsPartidaRepository.saveAndFlush(new GolsPartidaModel(peladeiroSalvo, partidasSalvo)); } @@ -173,10 +174,10 @@ void findByPeladeiro_quandoGolPartidaExistirem_retornaListaGolPartidas() { "Ronaldo", "o cara nota 10", "81992235678", - "Destro")); + PeDominante.DESTRO)); PeladeiroModel peladeiroModel2 = peladeiroRepository.saveAndFlush( - new PeladeiroModel("A", "a@test.com", "A", "333", "81555555555", "Destro")); + new PeladeiroModel("A", "a@test.com", "A", "333", "81555555555", PeDominante.DESTRO)); PartidasModel partidasSalvo = partidasRepository.saveAndFlush(new PartidasModel()); PartidasModel partidasSalvo2 = partidasRepository.saveAndFlush(new PartidasModel()); @@ -210,10 +211,10 @@ void findByPartida_quandoGolPartidaExistirem_retornaListaGolPartidas() { "Ronaldo", "o cara nota 10", "81992235678", - "Destro")); + PeDominante.DESTRO)); PeladeiroModel peladeiroModel2 = peladeiroRepository.saveAndFlush( - new PeladeiroModel("A", "a@test.com", "A", "333", "81555555555", "Destro")); + new PeladeiroModel("A", "a@test.com", "A", "333", "81555555555", PeDominante.DESTRO)); PartidasModel partidasSalvo = partidasRepository.saveAndFlush(new PartidasModel()); PartidasModel partidasSalvo2 = partidasRepository.saveAndFlush(new PartidasModel()); @@ -247,7 +248,7 @@ void deleteById_quandoGolsPartidaExistir() { "Ronaldo", "o cara nota 10", "81992235678", - "Destro")); + PeDominante.DESTRO)); PartidasModel partidasSalvo = partidasRepository.saveAndFlush(new PartidasModel()); GolsPartidaModel golsPartidaSalvo = diff --git a/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/PeladeiroRepositoryIT.java b/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/PeladeiroRepositoryIT.java index 826b7a5..e4e693b 100644 --- a/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/PeladeiroRepositoryIT.java +++ b/VemProFutApi/src/test/java/br/com/vemprofut/integration/repositories/PeladeiroRepositoryIT.java @@ -4,6 +4,7 @@ import br.com.vemprofut.configs.OAuth2LoginSuccessHandler; import br.com.vemprofut.models.PeladeiroModel; +import br.com.vemprofut.models.enuns.PeDominante; import br.com.vemprofut.repositories.PeladeiroRepository; import br.com.vemprofut.services.implementacao.UploadLocalService; import jakarta.transaction.Transactional; @@ -56,7 +57,12 @@ public class PeladeiroRepositoryIT { public void save_quandoPeladeiroValido_retornaIdGerado() { PeladeiroModel peladeiroModel = new PeladeiroModel( - "Marcio Teste", "teste@test.com", "Ronaldo", "o cara nota 10", "81992235678", "Destro"); + "Marcio Teste", + "teste@test.com", + "Ronaldo", + "o cara nota 10", + "81992235678", + PeDominante.DESTRO); PeladeiroModel salvo = peladeiroRepository.save(peladeiroModel); @@ -75,9 +81,9 @@ public void findById_quandoIdInexistente_retornaOptionalVazio() { @DisplayName("findAll deve retornar lista com todos os Peladeiros") public void findAll_quandoExistemRegistros_retornaListaComTodos() { peladeiroRepository.saveAndFlush( - new PeladeiroModel("A", "a@test.com", "A", "333", "81555555555", "Destro")); + new PeladeiroModel("A", "a@test.com", "A", "333", "81555555555", PeDominante.DESTRO)); peladeiroRepository.saveAndFlush( - new PeladeiroModel("B", "b@test.com", "B", "444", "82000000000", "Destro")); + new PeladeiroModel("B", "b@test.com", "B", "444", "82000000000", PeDominante.DESTRO)); List todos = peladeiroRepository.findAll(); assertTrue(todos.size() >= 2); @@ -88,7 +94,7 @@ public void findAll_quandoExistemRegistros_retornaListaComTodos() { public void deleteById_quandoIdExistente_registroEhRemovido() { PeladeiroModel p = peladeiroRepository.saveAndFlush( - new PeladeiroModel("C", "c@test.com", "C", "", "3", "Destro")); + new PeladeiroModel("C", "c@test.com", "C", "", "3", PeDominante.DESTRO)); Long id = p.getId(); peladeiroRepository.deleteById(id); assertFalse(peladeiroRepository.findById(id).isPresent()); @@ -98,7 +104,7 @@ public void deleteById_quandoIdExistente_registroEhRemovido() { @DisplayName("deve retorna de o email ja foi castrado ou nao") public void existsByEmail_quandoEmailCadastrado_retornaTrue() { peladeiroRepository.saveAndFlush( - new PeladeiroModel("C", "test@test.com", "C", "", "3", "Destro")); + new PeladeiroModel("C", "test@test.com", "C", "", "3", PeDominante.DESTRO)); String email = "test@test.com"; Boolean exist = peladeiroRepository.existsByEmail(email); @@ -109,7 +115,7 @@ public void existsByEmail_quandoEmailCadastrado_retornaTrue() { @DisplayName("deve retorna falso de o email ja foi castrado ou nao") public void existsByEmail_quandoEmailNaoCadastrado_retornaFalse() { peladeiroRepository.saveAndFlush( - new PeladeiroModel("C", "test@test.com", "C", "", "3", "Destro")); + new PeladeiroModel("C", "test@test.com", "C", "", "3", PeDominante.DESTRO)); String email = "test2@test.com"; Boolean exist = peladeiroRepository.existsByEmail(email); @@ -121,7 +127,8 @@ public void existsByEmail_quandoEmailNaoCadastrado_retornaFalse() { public void buscarPeladeiroPeloEmailRetornaPeladeiro() { PeladeiroModel p = peladeiroRepository.saveAndFlush( - new PeladeiroModel("C", "test@test.com", "C", "descricao", "377777777777", "Destro")); + new PeladeiroModel( + "C", "test@test.com", "C", "descricao", "377777777777", PeDominante.DESTRO)); String email = "test@test.com"; PeladeiroModel peladeiroModel = peladeiroRepository.findByEmail(email); @@ -138,7 +145,8 @@ public void buscarPeladeiroPeloEmailRetornaPeladeiro() { public void buscarPeladeiroPeloEmailRetornaNull() { PeladeiroModel p = peladeiroRepository.saveAndFlush( - new PeladeiroModel("C", "test@test.com", "C", "descricao", "377777777777", "Destro")); + new PeladeiroModel( + "C", "test@test.com", "C", "descricao", "377777777777", PeDominante.DESTRO)); String email = "test1@test.com"; PeladeiroModel peladeiroModel = peladeiroRepository.findByEmail(email); diff --git a/VemProFutApi/src/test/java/br/com/vemprofut/unit/services/FutServiceTest.java b/VemProFutApi/src/test/java/br/com/vemprofut/unit/services/FutServiceTest.java new file mode 100644 index 0000000..0d6d866 --- /dev/null +++ b/VemProFutApi/src/test/java/br/com/vemprofut/unit/services/FutServiceTest.java @@ -0,0 +1,205 @@ +package br.com.vemprofut.unit.services; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.mockito.Mockito.*; + +import br.com.vemprofut.controllers.request.SaveFutRequestDTO; +import br.com.vemprofut.controllers.response.FutDetailsResponse; +import br.com.vemprofut.controllers.response.SaveFutResponseDTO; +import br.com.vemprofut.exceptions.NomeInUseException; +import br.com.vemprofut.exceptions.NotFoundException; +import br.com.vemprofut.mappers.IEditorMapper; +import br.com.vemprofut.mappers.IFutMapper; +import br.com.vemprofut.mappers.IPartidasMapper; +import br.com.vemprofut.mappers.IPeladeiroMapper; +import br.com.vemprofut.models.DTOs.FutDTO; +import br.com.vemprofut.models.FutModel; +import br.com.vemprofut.models.HistoricoFutModel; +import br.com.vemprofut.models.PeladeiroModel; +import br.com.vemprofut.repositories.*; +import br.com.vemprofut.services.IBanimentoService; +import br.com.vemprofut.services.IEditorService; +import br.com.vemprofut.services.IHistoricoFutService; +import br.com.vemprofut.services.IPartidasService; +import br.com.vemprofut.services.implementacao.FutService; +import br.com.vemprofut.services.query.IFutQueryService; +import br.com.vemprofut.services.query.IPeladeiroQueryService; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class FutServiceTest { + + @Mock private IFutQueryService queryService; + @Mock private IFutMapper mapper; + @Mock private IPartidasMapper partidasMapper; + @Mock private FutRepository repository; + @Mock private IPartidasService partidasService; + @Mock private PartidasRepository partidasRepository; + @Mock private IPeladeiroQueryService peladeiroQueryService; + @Mock private PeladeiroRepository peladeiroRepository; + @Mock private IPeladeiroMapper peladeiroMapper; + @Mock private IHistoricoFutService historicoFutService; + @Mock private CartoesRepository cartoesRepository; + @Mock private GolsPartidaRepository golsRepository; + @Mock private IEditorService editorService; + @Mock private IEditorMapper editorMapper; + @Mock private IBanimentoService banidoService; + + @InjectMocks private FutService futService; + + FutModel futModel; + PeladeiroModel peladeiroModel; + HistoricoFutModel historicoFutModel; + + @BeforeEach + void setup() { + MockitoAnnotations.openMocks(this); + peladeiroModel = new PeladeiroModel(); + peladeiroModel.setId(1L); + peladeiroModel.setNome("Marcio"); + peladeiroModel.setEmail("marcio-costa@gmail.com"); + + historicoFutModel = new HistoricoFutModel(); + historicoFutModel.setId(1L); + + futModel = new FutModel(); + futModel.setId(1L); + futModel.setNome("Fut teste"); + futModel.setAdministradorPeladeiro(peladeiroModel); + } + + // ======================== CRUD basico ========================== + @Test + @DisplayName("Deve criar e salvar um novo Fut") + void create_quandoFutValido_retornaFutSalvo() { + // Arrange: + SaveFutRequestDTO saveFutRequestDTO = new SaveFutRequestDTO("Fut teste", 4, 10, 2, 1L); + + SaveFutResponseDTO saveFutResponseDTO = + new SaveFutResponseDTO( + 1L, "Fut teste", 4, 10, 2, historicoFutModel.getId(), peladeiroModel.getId()); + + doNothing().when(queryService).verifyNomeFutExist(futModel.getNome()); + when(peladeiroQueryService.verifyPeladeiroExist(saveFutRequestDTO.administradorPeladeiro())) + .thenReturn(peladeiroModel); + when(mapper.saveRequestToModel(saveFutRequestDTO)).thenReturn(futModel); + when(repository.save(futModel)).thenReturn(futModel); + when(historicoFutService.create()).thenReturn(historicoFutModel); + when(mapper.toSaveResponse(futModel)).thenReturn(saveFutResponseDTO); + + // Act: + SaveFutResponseDTO response = futService.create(saveFutRequestDTO); + + // Assert: + assertThat(response.id()).isNotNull().isEqualTo(1L); + assertThat(response.nome()).isEqualTo("Fut teste"); + assertThat(response.historicoFutId()).isEqualTo(historicoFutModel.getId()); + assertThat(response.administradorPeladeiro()).isEqualTo(peladeiroModel.getId()); + + verify(queryService).verifyNomeFutExist(saveFutRequestDTO.nome()); + verify(peladeiroQueryService).verifyPeladeiroExist(saveFutRequestDTO.administradorPeladeiro()); + verify(mapper).saveRequestToModel(saveFutRequestDTO); + verify(mapper).toSaveResponse(futModel); + verify(repository, times(2)).save(futModel); + verify(historicoFutService).create(); + } + + @Test + @DisplayName("Deve retornar erro ao tentar criar um fut") + void create_quandoFutInvalido() { + // Arrange: + SaveFutRequestDTO saveFutRequestDTO = new SaveFutRequestDTO("Fut teste", 4, 10, 2, 1L); + + doThrow(new NomeInUseException("O nome '" + saveFutRequestDTO.nome() + "' já está cadastrado!")) + .when(queryService) + .verifyNomeFutExist(saveFutRequestDTO.nome()); + + // Act + Assert: + assertThatThrownBy(() -> futService.create(saveFutRequestDTO)) + .isInstanceOf(NomeInUseException.class) + .hasMessage("O nome '" + saveFutRequestDTO.nome() + "' já está cadastrado!"); + + verify(queryService).verifyNomeFutExist(saveFutRequestDTO.nome()); + verifyNoInteractions(repository); + } + + @Test + @DisplayName("Deve retornar FutDetailsResponse buscando pelo ID") + void findById_quandoFutExiste_retornaFutDetailsResponse() { + + // Arrange: + FutDetailsResponse futDetailsResponse = + new FutDetailsResponse(1L, "Fut teste", 4, 10, 2, 1L, 1L); + + when(queryService.verifyFutExistRetorn(1L)).thenReturn(futModel); + when(mapper.modelToDetailsResponse(futModel)).thenReturn(futDetailsResponse); + + // Act: + FutDetailsResponse response = futService.findById(1L); + + // Assert + assertThat(response.id()).isNotNull().isEqualTo(1L); + assertThat(response.nome()).isEqualTo("Fut teste"); + + verify(queryService).verifyFutExistRetorn(1L); + verify(mapper).modelToDetailsResponse(futModel); + } + + @Test + @DisplayName("Deve retorna NotFoundException ao buscar pelo ID") + void findById_quandoFutInexistente_retornaNotFoundException() { + + // Arrange: + FutDetailsResponse futDetailsResponse = + new FutDetailsResponse(1L, "Fut teste", 4, 10, 2, 1L, 1L); + + when(queryService.verifyFutExistRetorn(99L)) + .thenThrow(new NotFoundException("Não foi encontrado o Fut de id " + 99L)); + + // Act + Assert + assertThatThrownBy(() -> futService.findById(99L)) + .isInstanceOf(NotFoundException.class) + .hasMessage("Não foi encontrado o Fut de id " + 99L); + verify(queryService).verifyFutExistRetorn(99L); + } + + @Test + @DisplayName("Deve buscar Fut pelo Nome") + void findByNome_quandoFutExiste_retornaFutDTO() { + // Arrange + List editores = List.of(3L, 6L, 7L); + List peladeiros = List.of(2L, 8L, 3L, 6L, 7L); + List cartoes = List.of(1L, 2L, 3L, 5L); + FutDTO futDTO = new FutDTO(1L, "Fut Test", 4, 10, 2, 1L, 1L, editores, peladeiros, cartoes); + + when(queryService.verifyNomeFutExistRetorn("Fut Test")).thenReturn(futModel); + when(mapper.toDTO(futModel)).thenReturn(futDTO); + + // Act + FutDTO response = futService.findByNome("Fut Test"); + + // Assert + assertThat(response).isNotNull(); + assertThat(response.id()).isEqualTo(futDTO.id()); + assertThat(response.nome()).isEqualTo("Fut Test"); + assertThat(response.editores().size()).isEqualTo(3); + assertThat(response.peladeiros().size()).isEqualTo(5); + assertThat(response.cartoes().size()).isEqualTo(4); + + verify(queryService).verifyNomeFutExistRetorn("Fut Test"); + verify(mapper).toDTO(futModel); + } + + @Test + @DisplayName("Deve buscar um nome de Fut que nao existe e retornar ") + void findByNome_quandoFutInexistente_retornaEntityNotFoundException() {} +} diff --git a/VemProFutApi/src/test/java/br/com/vemprofut/unit/services/PeladeiroServiceTest.java b/VemProFutApi/src/test/java/br/com/vemprofut/unit/services/PeladeiroServiceTest.java new file mode 100644 index 0000000..ef9d681 --- /dev/null +++ b/VemProFutApi/src/test/java/br/com/vemprofut/unit/services/PeladeiroServiceTest.java @@ -0,0 +1,303 @@ +package br.com.vemprofut.unit.services; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.*; + +import br.com.vemprofut.controllers.request.SavePeladeiroRequestDTO; +import br.com.vemprofut.controllers.request.UpdatePeladeiroRequestDTO; +import br.com.vemprofut.controllers.response.CartoesResumoResponseDTO; +import br.com.vemprofut.controllers.response.PeladeiroDetailResponse; +import br.com.vemprofut.controllers.response.SavePeladeiroResponseDTO; +import br.com.vemprofut.controllers.response.UpdatePeladeiroResponseDTO; +import br.com.vemprofut.exceptions.EmailInUseException; +import br.com.vemprofut.exceptions.NotFoundException; +import br.com.vemprofut.mappers.IHistoricoPeladeiroMapper; +import br.com.vemprofut.mappers.IPeladeiroMapper; +import br.com.vemprofut.models.PeladeiroModel; +import br.com.vemprofut.models.enuns.PeDominante; +import br.com.vemprofut.repositories.PeladeiroRepository; +import br.com.vemprofut.services.ICartoesService; +import br.com.vemprofut.services.IHistoricoPeladeiroService; +import br.com.vemprofut.services.implementacao.PeladeiroService; +import br.com.vemprofut.services.query.IPeladeiroQueryService; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class PeladeiroServiceTest { + + @Mock private IPeladeiroQueryService queryService; + @Mock private PeladeiroRepository repository; + @Mock private IPeladeiroMapper peladeiroMapper; + @Mock private IHistoricoPeladeiroMapper historicoMapper; + @Mock private IHistoricoPeladeiroService historicoPeladeiroService; + @Mock private ICartoesService cartoesService; + + @InjectMocks private PeladeiroService peladeiroService; + + PeladeiroModel peladeiroModel; + + @BeforeEach + void setup() { + MockitoAnnotations.openMocks(this); + peladeiroModel = new PeladeiroModel(); + peladeiroModel.setId(1L); + peladeiroModel.setNome("Marcio"); + peladeiroModel.setEmail("marcio-costa@gmail.com"); + } + + @Test + @DisplayName("Deve Salvar um novo Peladeiro") + void create_quandoPeladeiroValido() { + // Arrange + SavePeladeiroRequestDTO savePeladeiroRequestDTO = + new SavePeladeiroRequestDTO( + "Marcio", + "marcio-costa@gmail.com", + "ronaldinho", + "o cara forte", + PeDominante.DESTRO, + "81999999999"); + + // cria listas simuladas + List partidas = List.of(10L, 20L); + List futs = List.of(30L); + List cartoes = List.of(40L, 50L); + + doNothing().when(queryService).verifyEmail(savePeladeiroRequestDTO.email()); + when(peladeiroMapper.saveRequestToModel(savePeladeiroRequestDTO)).thenReturn(peladeiroModel); + when(repository.save(peladeiroModel)).thenReturn(peladeiroModel); + when(peladeiroMapper.modelToSaveResponse(peladeiroModel)) + .thenReturn( + new SavePeladeiroResponseDTO( + 1L, + "Marcio", + "marcio-costa@gmail.com", + "ronaldinho", + "o cara forte", + PeDominante.DESTRO, + "81999999999", + 1L, + "foto.com/url", + partidas, + futs, + cartoes)); + // Act + SavePeladeiroResponseDTO response = peladeiroService.create(savePeladeiroRequestDTO); + + // Assert + assertThat(response.nome()).isEqualTo("Marcio"); + + verify(queryService).verifyEmail(savePeladeiroRequestDTO.email()); + verify(repository, times(2)).save(peladeiroModel); + verify(peladeiroMapper).saveRequestToModel(savePeladeiroRequestDTO); + verify(peladeiroMapper).modelToSaveResponse(peladeiroModel); + } + + @Test + @DisplayName("Deve retornar um EmailInUseException por email ja existente") + void create_quandoEmailJaCadastrado_retornaEmailInUseException() { + // Arrange + SavePeladeiroRequestDTO savePeladeiroRequestDTO = + new SavePeladeiroRequestDTO( + "Marcio", + "marcio-costa@gmail.com", + "ronaldinho", + "o cara forte", + PeDominante.DESTRO, + "81999999999"); + + // cria listas simuladas + List partidas = List.of(10L, 20L); + List futs = List.of(30L); + List cartoes = List.of(40L, 50L); + + doThrow( + new EmailInUseException( + "O e-mail " + savePeladeiroRequestDTO.email() + " já está em uso")) + .when(queryService) + .verifyEmail(savePeladeiroRequestDTO.email()); + + // Act + Assert + assertThatThrownBy(() -> peladeiroService.create(savePeladeiroRequestDTO)) + .isInstanceOf(EmailInUseException.class) + .hasMessage("O e-mail " + savePeladeiroRequestDTO.email() + " já está em uso"); + + verify(queryService).verifyEmail(savePeladeiroRequestDTO.email()); + verifyNoInteractions(repository); + } + + @Test + @DisplayName("Deve fazer uma Alteracao em um Peladeiro ja cadastrado") + void update_quandoPeladeiroExiste_retornaPeladeiroAlterado() { + // Arrange + UpdatePeladeiroRequestDTO requestDTO = + new UpdatePeladeiroRequestDTO( + "maria", + "maria@gmail.com", + "mari", + "mulher que joga", + PeDominante.CANHOTO, + "81999993332"); + + when(queryService.verifyPeladeiroExist(1L)).thenReturn(peladeiroModel); + when(repository.save(peladeiroModel)).thenReturn(peladeiroModel); + when(peladeiroMapper.modelToUpdateResponse(peladeiroModel)) + .thenReturn( + new UpdatePeladeiroResponseDTO( + "maria", + "maria@gmail.com", + "mari", + "mulher que joga", + "81999993332", + PeDominante.DESTRO, + "foto.com/url")); + + // Act + UpdatePeladeiroResponseDTO responseDTO = peladeiroService.update(1L, requestDTO); + + // Assert + assertThat(responseDTO).isNotNull(); + assertThat(responseDTO.nome()).isEqualTo("maria"); + assertThat(responseDTO.email()).isEqualTo("maria@gmail.com"); + + verify(queryService).verifyPeladeiroExist(1L); + verify(repository).save(peladeiroModel); + } + + @Test + @DisplayName("Deve retornar NotFoundException por nao encontrar o Peladeiro pelo ID") + void update_quandoPeladeiroInexistente_retornaNotFoundException() { + // Arrange + UpdatePeladeiroRequestDTO requestDTO = + new UpdatePeladeiroRequestDTO( + "maria", + "maria@gmail.com", + "mari", + "mulher que joga", + PeDominante.CANHOTO, + "81999993332"); + + when(queryService.verifyPeladeiroExist(99L)) + .thenThrow(new NotFoundException("Não foi encontrado o Peladeiro de id" + 99L)); + + // Act + Assert + assertThatThrownBy(() -> peladeiroService.update(99L, requestDTO)) + .isInstanceOf(NotFoundException.class) + .hasMessage("Não foi encontrado o Peladeiro de id" + 99L); + verify(queryService).verifyPeladeiroExist(99L); + verifyNoInteractions(repository); + } + + @Test + @DisplayName("Deve buscar um peladeiro pelo ID") + void findById_quandoPeladeiroExiste_retornaPeladeiroDetailResponse() { + // Arrange + CartoesResumoResponseDTO cartoesResumoResponseDTO = new CartoesResumoResponseDTO(2, 3, 2); + when(queryService.verifyPeladeiroExist(1L)).thenReturn(peladeiroModel); + when(cartoesService.contarCartoesPeladeiro(1L)).thenReturn(cartoesResumoResponseDTO); + + // Act + PeladeiroDetailResponse peladeiroDetailResponse = peladeiroService.findById(1L); + + // Assert + assertThat(peladeiroDetailResponse).isNotNull(); + assertThat(peladeiroDetailResponse.id()).isEqualTo(1L); + assertThat(peladeiroDetailResponse.cartoes().getAzul()).isEqualTo(2); + assertThat(peladeiroDetailResponse.cartoes().getAmarelo()).isEqualTo(3); + assertThat(peladeiroDetailResponse.cartoes().getVermelho()).isEqualTo(2); + assertThat(peladeiroDetailResponse.nome()).isEqualTo("Marcio"); + assertThat(peladeiroDetailResponse.email()).isEqualTo("marcio-costa@gmail.com"); + + verify(queryService).verifyPeladeiroExist(1L); + verify(cartoesService).contarCartoesPeladeiro(1L); + } + + @Test + @DisplayName("Deve buscar um peladeiro pelo ID") + void findById_quandoPeladeiroInexistente_retornaNotFoundException() { + // Arrange + when(queryService.verifyPeladeiroExist(99L)) + .thenThrow(new NotFoundException("Não foi encontrado o Peladeiro de id " + 99L)); + + // Act + Assert + assertThatThrownBy(() -> peladeiroService.findById(99L)) + .isInstanceOf(NotFoundException.class) + .hasMessage("Não foi encontrado o Peladeiro de id " + 99L); + + verify(queryService).verifyPeladeiroExist(99L); + verifyNoInteractions( + repository); // é usando em verifyPeladeiroExist, mas como ele está mockado nao detecta... + } + + @Test + @DisplayName("") + void findByIdModel_quandoPeladeiroExiste_retornaPeladeiroModel() { + // Arrenge + when(queryService.verifyPeladeiroExist(1L)).thenReturn(peladeiroModel); + + // Act + PeladeiroModel response = peladeiroService.findByIdModel(1L); + + // Assert + assertThat(response.getId()).isEqualTo(1L); + assertThat(response.getNome()).isEqualTo("Marcio"); + assertThat(response.getEmail()).isEqualTo("marcio-costa@gmail.com"); + + verify(queryService).verifyPeladeiroExist(1L); + } + + @Test + @DisplayName("") + void findByIdModel_quandoPeladeiroInexistente_retornaNotFoundException() { + // Arrenge + when(queryService.verifyPeladeiroExist(99L)) + .thenThrow(new NotFoundException("Não foi encontrado o Peladeiro de id " + 99L)); + + // Act + Assert + assertThatThrownBy(() -> peladeiroService.findByIdModel(99L)) + .isInstanceOf(NotFoundException.class) + .hasMessage("Não foi encontrado o Peladeiro de id " + 99L); + + verify(queryService).verifyPeladeiroExist(99L); + } + + @Test + @DisplayName("Deve apagar o Peladeiro indicado pelo numero do ID") + void delete_quandoPeladeiroExiste() { + // Arrenge + when(queryService.verifyPeladeiroExist(1L)).thenReturn(peladeiroModel); + doNothing().when(repository).deleteById(1L); + + // Act + peladeiroService.delete(1L); + + // Assert + verify(queryService).verifyPeladeiroExist(1L); + verify(repository).deleteById(1L); + } + + @Test + @DisplayName("Deve apagar o Peladeiro indicado pelo numero do ID") + void delete_quandoPeladeiroInexistente() { + // Arrenge + when(queryService.verifyPeladeiroExist(99L)) + .thenThrow(new NotFoundException("Não foi encontrado o Peladeiro de id " + 99L)); + + // Act + Assert + + assertThatThrownBy(() -> peladeiroService.delete(99L)) + .isInstanceOf(NotFoundException.class) + .hasMessage("Não foi encontrado o Peladeiro de id " + 99L); + + verify(queryService).verifyPeladeiroExist(99L); + } +} From db8e73c2403b3c4b6e3be09fdfba7484b924ea37 Mon Sep 17 00:00:00 2001 From: Marcio Costa Date: Fri, 2 Jan 2026 13:39:17 -0300 Subject: [PATCH 11/13] feat(Codigo base async): - implementando o primeiro codigo async da aplicacao; --- .../com/vemprofut/VemProFutApplication.java | 2 + .../controllers/PeladeiroController.java | 32 ++++++----- .../vemprofut/services/IPeladeiroService.java | 15 ++--- .../implementacao/PeladeiroService.java | 56 ++++++++++++------- .../unit/services/PeladeiroServiceTest.java | 44 ++++++++++++--- 5 files changed, 101 insertions(+), 48 deletions(-) diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/VemProFutApplication.java b/VemProFutApi/src/main/java/br/com/vemprofut/VemProFutApplication.java index 6ea00f4..31f9621 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/VemProFutApplication.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/VemProFutApplication.java @@ -4,8 +4,10 @@ import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication +@EnableAsync public class VemProFutApplication { private static final Logger log = LoggerFactory.getLogger(VemProFutApplication.class); diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/PeladeiroController.java b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/PeladeiroController.java index 0e7e4f7..2d14343 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/PeladeiroController.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/PeladeiroController.java @@ -8,6 +8,7 @@ import io.swagger.v3.oas.annotations.Operation; import jakarta.validation.Valid; import java.io.IOException; +import java.util.concurrent.CompletableFuture; import lombok.AllArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -26,47 +27,48 @@ public class PeladeiroController { @Operation( summary = "Cadastra um novo peladeiro", tags = {"PeladeiroController"}) - public ResponseEntity create( + public CompletableFuture> create( @Valid @RequestBody final SavePeladeiroRequestDTO requestDTO) { - var obj = peladeiroService.create(requestDTO); - return ResponseEntity.status(HttpStatus.CREATED).body(obj); + + return peladeiroService + .create(requestDTO) + .thenApply(obj -> ResponseEntity.status(HttpStatus.CREATED).body(obj)); } @GetMapping("{id}") @Operation( summary = "Busca um Peladeiro pelo id", tags = {"PeladeiroController"}) - public ResponseEntity findById(@PathVariable final Long id) { - var obj = peladeiroService.findById(id); - return ResponseEntity.ok(obj); + public CompletableFuture> findById( + @PathVariable final Long id) { + return peladeiroService.findById(id).thenApply(ResponseEntity::ok); } @PutMapping("{id}") @Operation( summary = "Faz alteraçoes no Peladeiro cujo id é informado.", tags = {"PeladeiroController"}) - public ResponseEntity update( + public CompletableFuture> update( @PathVariable final Long id, @Valid @RequestBody UpdatePeladeiroRequestDTO dto) { - peladeiroService.update(id, dto); - return ResponseEntity.noContent().build(); + return peladeiroService.update(id, dto).thenApply(obj -> ResponseEntity.noContent().build()); } @DeleteMapping("{id}") @Operation( summary = "Deleta o peladeiro cujo id foi informado. Cuidado!", tags = {"PeladeiroController"}) - public ResponseEntity delete(@PathVariable final Long id) { - peladeiroService.delete(id); - return ResponseEntity.noContent().build(); + public CompletableFuture> delete(@PathVariable final Long id) { + return peladeiroService.delete(id).thenApply(obj -> ResponseEntity.noContent().build()); } @PostMapping(value = "uploadFoto/{id}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @Operation( summary = "Caso nao logado pelo gmail, enviar a foto do perfil", tags = {"PeladeiroController"}) - public ResponseEntity uploadFotoPeladeiro( + public CompletableFuture> uploadFotoPeladeiro( @PathVariable Long id, @RequestPart("file") MultipartFile file) throws IOException { - peladeiroService.atualizarFoto(id, file); - return ResponseEntity.ok("Foto salva!"); + return peladeiroService + .atualizarFoto(id, file) + .thenApply(obj -> ResponseEntity.ok("Foto salva!")); } } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/IPeladeiroService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/IPeladeiroService.java index e1366f5..2307de4 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/IPeladeiroService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/IPeladeiroService.java @@ -6,21 +6,22 @@ import br.com.vemprofut.controllers.response.SavePeladeiroResponseDTO; import br.com.vemprofut.controllers.response.UpdatePeladeiroResponseDTO; import br.com.vemprofut.models.PeladeiroModel; +import java.util.concurrent.CompletableFuture; import org.springframework.web.multipart.MultipartFile; public interface IPeladeiroService { - SavePeladeiroResponseDTO create(SavePeladeiroRequestDTO dto); + CompletableFuture create(SavePeladeiroRequestDTO dto); - UpdatePeladeiroResponseDTO update(Long id, UpdatePeladeiroRequestDTO dto); + CompletableFuture update(Long id, UpdatePeladeiroRequestDTO dto); - PeladeiroDetailResponse findById(Long id); + CompletableFuture findById(Long id); - PeladeiroModel findByIdModel(Long id); + CompletableFuture findByIdModel(Long id); - // List findAll(); + // CompletableFuture> findAll(); - void delete(Long id); + CompletableFuture delete(Long id); - void atualizarFoto(Long id, MultipartFile file); + CompletableFuture atualizarFoto(Long id, MultipartFile file); } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/PeladeiroService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/PeladeiroService.java index 0201483..adb541e 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/PeladeiroService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/PeladeiroService.java @@ -15,8 +15,10 @@ import br.com.vemprofut.services.IHistoricoPeladeiroService; import br.com.vemprofut.services.IPeladeiroService; import br.com.vemprofut.services.query.IPeladeiroQueryService; +import java.util.concurrent.CompletableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @@ -39,7 +41,7 @@ public class PeladeiroService implements IPeladeiroService { @Override @Transactional - public SavePeladeiroResponseDTO create(SavePeladeiroRequestDTO dto) { + public CompletableFuture create(SavePeladeiroRequestDTO dto) { queryService.verifyEmail(dto.email()); log.info("Email verificado!"); PeladeiroModel peladeiroModel = peladeiroMapper.saveRequestToModel(dto); @@ -49,12 +51,17 @@ public SavePeladeiroResponseDTO create(SavePeladeiroRequestDTO dto) { peladeiroSalvo.setHistoricoPeladeiro(historicoMapper.toModel(historico)); log.info("Peladeiro cadastrado com sucesso!"); - return peladeiroMapper.modelToSaveResponse(repository.save(peladeiroSalvo)); + SavePeladeiroResponseDTO response = + peladeiroMapper.modelToSaveResponse(repository.save(peladeiroSalvo)); + + return CompletableFuture.completedFuture(response); } @Override @Transactional - public UpdatePeladeiroResponseDTO update(Long id, UpdatePeladeiroRequestDTO dto) { + @Async + public CompletableFuture update( + Long id, UpdatePeladeiroRequestDTO dto) { var peladeiroModel = queryService.verifyPeladeiroExist(id); log.info("Verificado a existencia de Peladeiro"); @@ -66,35 +73,44 @@ public UpdatePeladeiroResponseDTO update(Long id, UpdatePeladeiroRequestDTO dto) peladeiroModel.setPeDominante(dto.peDominante()); log.info("Peladeiro alterado com sucesso!"); - return peladeiroMapper.modelToUpdateResponse(repository.save(peladeiroModel)); + UpdatePeladeiroResponseDTO response = + peladeiroMapper.modelToUpdateResponse(repository.save(peladeiroModel)); + return CompletableFuture.completedFuture(response); } @Override @Transactional(readOnly = true) - public PeladeiroDetailResponse findById(Long id) { + @Async + public CompletableFuture findById(Long id) { log.info("Buscando peladeiro pelo id... saida de resposta"); PeladeiroModel retorno = queryService.verifyPeladeiroExist(id); CartoesResumoResponseDTO resumo = cartoesService.contarCartoesPeladeiro(id); - return new PeladeiroDetailResponse( - retorno.getId(), - retorno.getNome(), - retorno.getEmail(), - retorno.getApelido(), - retorno.getDescricao(), - retorno.getWhatsapp(), - retorno.getPeDominante(), - resumo); + var response = + new PeladeiroDetailResponse( + retorno.getId(), + retorno.getNome(), + retorno.getEmail(), + retorno.getApelido(), + retorno.getDescricao(), + retorno.getWhatsapp(), + retorno.getPeDominante(), + resumo); + + return CompletableFuture.completedFuture(response); } @Override @Transactional(readOnly = true) - public PeladeiroModel findByIdModel(Long id) { + @Async + public CompletableFuture findByIdModel(Long id) { log.info("Buscando peladeiro pelo id... saida de uso interno"); - return queryService.verifyPeladeiroExist(id); + PeladeiroModel response = queryService.verifyPeladeiroExist(id); + return CompletableFuture.completedFuture(response); } // @Override - // @Transactional(readOnly = true) + // @Transactional + // @Async(readOnly = true) // public List findAll() { // // return repository.findAll() @@ -105,15 +121,17 @@ public PeladeiroModel findByIdModel(Long id) { @Override @Transactional - public void delete(Long id) { + public CompletableFuture delete(Long id) { queryService.verifyPeladeiroExist(id); log.info("Existência de Peladeiro confirmada com sucesso!"); repository.deleteById(id); + return CompletableFuture.completedFuture(null); } @Override @Transactional - public void atualizarFoto(Long id, MultipartFile file) { + public CompletableFuture atualizarFoto(Long id, MultipartFile file) { queryService.verifyPeladeiroSaveFile(id, file); + return CompletableFuture.completedFuture(null); } } diff --git a/VemProFutApi/src/test/java/br/com/vemprofut/unit/services/PeladeiroServiceTest.java b/VemProFutApi/src/test/java/br/com/vemprofut/unit/services/PeladeiroServiceTest.java index ef9d681..bddeffb 100644 --- a/VemProFutApi/src/test/java/br/com/vemprofut/unit/services/PeladeiroServiceTest.java +++ b/VemProFutApi/src/test/java/br/com/vemprofut/unit/services/PeladeiroServiceTest.java @@ -30,6 +30,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.web.multipart.MultipartFile; @ExtendWith(MockitoExtension.class) public class PeladeiroServiceTest { @@ -91,7 +92,7 @@ void create_quandoPeladeiroValido() { futs, cartoes)); // Act - SavePeladeiroResponseDTO response = peladeiroService.create(savePeladeiroRequestDTO); + SavePeladeiroResponseDTO response = peladeiroService.create(savePeladeiroRequestDTO).join(); // Assert assertThat(response.nome()).isEqualTo("Marcio"); @@ -162,7 +163,7 @@ void update_quandoPeladeiroExiste_retornaPeladeiroAlterado() { "foto.com/url")); // Act - UpdatePeladeiroResponseDTO responseDTO = peladeiroService.update(1L, requestDTO); + UpdatePeladeiroResponseDTO responseDTO = peladeiroService.update(1L, requestDTO).join(); // Assert assertThat(responseDTO).isNotNull(); @@ -206,7 +207,7 @@ void findById_quandoPeladeiroExiste_retornaPeladeiroDetailResponse() { when(cartoesService.contarCartoesPeladeiro(1L)).thenReturn(cartoesResumoResponseDTO); // Act - PeladeiroDetailResponse peladeiroDetailResponse = peladeiroService.findById(1L); + PeladeiroDetailResponse peladeiroDetailResponse = peladeiroService.findById(1L).join(); // Assert assertThat(peladeiroDetailResponse).isNotNull(); @@ -239,13 +240,13 @@ void findById_quandoPeladeiroInexistente_retornaNotFoundException() { } @Test - @DisplayName("") + @DisplayName("Deve retornar PeladeiroModel quando ID existe") void findByIdModel_quandoPeladeiroExiste_retornaPeladeiroModel() { // Arrenge when(queryService.verifyPeladeiroExist(1L)).thenReturn(peladeiroModel); // Act - PeladeiroModel response = peladeiroService.findByIdModel(1L); + PeladeiroModel response = peladeiroService.findByIdModel(1L).join(); // Assert assertThat(response.getId()).isEqualTo(1L); @@ -256,7 +257,7 @@ void findByIdModel_quandoPeladeiroExiste_retornaPeladeiroModel() { } @Test - @DisplayName("") + @DisplayName("Deve retornar erro NotFoundException quando ID nao existe") void findByIdModel_quandoPeladeiroInexistente_retornaNotFoundException() { // Arrenge when(queryService.verifyPeladeiroExist(99L)) @@ -278,7 +279,7 @@ void delete_quandoPeladeiroExiste() { doNothing().when(repository).deleteById(1L); // Act - peladeiroService.delete(1L); + peladeiroService.delete(1L).join(); // Assert verify(queryService).verifyPeladeiroExist(1L); @@ -300,4 +301,33 @@ void delete_quandoPeladeiroInexistente() { verify(queryService).verifyPeladeiroExist(99L); } + + @Test + @DisplayName("Deve atualizar a foto do Peladeiro") + void atualizarFoto_quandoPeladeiroExiste() { + MultipartFile file = mock(MultipartFile.class); + + doNothing().when(queryService).verifyPeladeiroSaveFile(1L, file); + peladeiroService.atualizarFoto(1L, file).join(); + + verify(queryService).verifyPeladeiroSaveFile(1L, file); + } + + @Test + @DisplayName("Deve lançar NotFoundException ao tentar atualizar foto de Peladeiro inexistente") + void atualizarFoto_quandoPeladeiroInexistente_retornaNotFoundException() { + MultipartFile file = mock(MultipartFile.class); + + // Arrange: simula que o queryService lança exceção + doThrow(new NotFoundException("Não foi encontrado o Peladeiro de id 99")) + .when(queryService) + .verifyPeladeiroSaveFile(99L, file); + + // Act + Assert + assertThatThrownBy(() -> peladeiroService.atualizarFoto(99L, file).join()) + .isInstanceOf(NotFoundException.class) + .hasMessage("Não foi encontrado o Peladeiro de id 99"); + + verify(queryService).verifyPeladeiroSaveFile(99L, file); + } } From 5d59ae88a47bc41c77aeeb3bc335c0c0c4dd9730 Mon Sep 17 00:00:00 2001 From: Marcio Costa Date: Sun, 4 Jan 2026 01:54:21 -0300 Subject: [PATCH 12/13] =?UTF-8?q?refactore(primeiro=20grupo=20de=20async):?= =?UTF-8?q?=20-=20Mudei=20a=20logica=20do=20codigo=20basico=20do=20commit?= =?UTF-8?q?=20anterior;=20-=20Usei=20a=20logica=20da=20anotacao=20@Async?= =?UTF-8?q?=20para=20todo=20codigo=20menos=20CartoesService;=20-=20Em=20Ca?= =?UTF-8?q?rtoesServices=20usei=20sem=20a=20anotacao=20@Async,=20e=20usei?= =?UTF-8?q?=20o=20metodo=20'supplyAsync'=20s=C3=B3=20para=20aprendizado=20?= =?UTF-8?q?mais=20o=20resultado=20=C3=A9=20o=20mesmo;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../br/com/vemprofut/configs/AsyncConfig.java | 22 ++++ .../vemprofut/controllers/FutController.java | 8 +- .../vemprofut/services/IBanimentoService.java | 7 +- .../vemprofut/services/ICartoesService.java | 19 +-- .../vemprofut/services/IEditorService.java | 11 +- .../com/vemprofut/services/IFutService.java | 37 +++--- .../vemprofut/services/IPartidasService.java | 14 ++- .../implementacao/BanimentoService.java | 16 ++- .../implementacao/CartoesService.java | 109 ++++++++++++------ .../services/implementacao/EditorService.java | 46 ++++++-- .../services/implementacao/FutService.java | 89 ++++++++------ .../implementacao/PartidasService.java | 63 ++++++---- .../implementacao/PeladeiroService.java | 79 +++++++------ .../IHistoricoPeladeiroQueryService.java | 5 +- .../services/query/IPartidasQueryService.java | 3 +- .../query/IPeladeiroQueryService.java | 7 +- .../HistoricoPeladeiroQueryService.java | 18 ++- .../implementacao/PartidasQueryService.java | 11 +- .../implementacao/PeladeiroQueryService.java | 53 +++++---- 19 files changed, 395 insertions(+), 222 deletions(-) create mode 100644 VemProFutApi/src/main/java/br/com/vemprofut/configs/AsyncConfig.java diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/configs/AsyncConfig.java b/VemProFutApi/src/main/java/br/com/vemprofut/configs/AsyncConfig.java new file mode 100644 index 0000000..abd0c5b --- /dev/null +++ b/VemProFutApi/src/main/java/br/com/vemprofut/configs/AsyncConfig.java @@ -0,0 +1,22 @@ +package br.com.vemprofut.configs; + +import java.util.concurrent.Executor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +@Configuration +@EnableAsync +public class AsyncConfig { + @Bean(name = "defaultExecutor") + public Executor defaultExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(10); + executor.setMaxPoolSize(20); + executor.setQueueCapacity(100); + executor.setThreadNamePrefix("async-"); + executor.initialize(); + return executor; + } +} diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/FutController.java b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/FutController.java index 59df0f1..59cf606 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/FutController.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/FutController.java @@ -7,6 +7,7 @@ import io.swagger.v3.oas.annotations.Operation; import jakarta.validation.Valid; import java.util.List; +import java.util.concurrent.CompletableFuture; import lombok.AllArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -26,10 +27,11 @@ public class FutController { @Operation( summary = "Cadastra um novo Fut", tags = {"FutController - CRUD Básico"}) - public ResponseEntity create( + public CompletableFuture> create( @Valid @RequestBody final SaveFutRequestDTO requestDTO) { - var response = futService.create(requestDTO); - return ResponseEntity.status(HttpStatus.CREATED).body(response); + return futService + .create(requestDTO) + .thenApply(obj -> ResponseEntity.status(HttpStatus.CREATED).body(obj)); } @GetMapping("{id}") diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/IBanimentoService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/IBanimentoService.java index b5d71cc..b988598 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/IBanimentoService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/IBanimentoService.java @@ -4,11 +4,12 @@ import br.com.vemprofut.controllers.response.BanimentoDetailsResponseDTO; import br.com.vemprofut.controllers.response.SaveBanimentoResponseDTO; import java.util.List; +import java.util.concurrent.CompletableFuture; public interface IBanimentoService { - SaveBanimentoResponseDTO create(SaveBanimentoRequestDTO dto); + CompletableFuture create(SaveBanimentoRequestDTO dto); - List findAll(Long idFut); + CompletableFuture> findAll(Long idFut); - void delete(Long idPeladeiro); + CompletableFuture delete(Long idPeladeiro); } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/ICartoesService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/ICartoesService.java index 3b67133..1037680 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/ICartoesService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/ICartoesService.java @@ -4,24 +4,25 @@ import br.com.vemprofut.models.CartoesModel; import br.com.vemprofut.models.DTOs.CartoesDTO; import java.util.List; +import java.util.concurrent.CompletableFuture; public interface ICartoesService { - CartoesDTO create(CartoesDTO dto); + CompletableFuture create(CartoesDTO dto); - List findByPeladeiro(Long id); + CompletableFuture> findByPeladeiro(Long id); - List findByPartida(Long id); + CompletableFuture> findByPartida(Long id); - List findByFut(Long id); + CompletableFuture> findByFut(Long id); - List findAll(); + CompletableFuture> findAll(); - CartoesDTO findById(Long id); + CompletableFuture findById(Long id); - CartoesModel findByIdModel(Long id); + CompletableFuture findByIdModel(Long id); - CartoesResumoResponseDTO contarCartoesPeladeiro(Long peladeiroId); + CompletableFuture contarCartoesPeladeiro(Long peladeiroId); - CartoesResumoResponseDTO contarCartoesFut(Long futId); + CompletableFuture contarCartoesFut(Long futId); } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/IEditorService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/IEditorService.java index ad8ad7f..121dcf5 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/IEditorService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/IEditorService.java @@ -4,16 +4,17 @@ import br.com.vemprofut.models.DTOs.FutDTO; import br.com.vemprofut.models.EditorModel; import java.util.List; +import java.util.concurrent.CompletableFuture; public interface IEditorService { - EditorModel create(EditorModel dto); + CompletableFuture create(EditorModel dto); - EditorDTO findById(Long id); + CompletableFuture findById(Long id); - EditorModel findByIdModel(Long id); + CompletableFuture findByIdModel(Long id); - List findAll(FutDTO futDTO); + CompletableFuture> findAll(FutDTO futDTO); - void delete(Long id); + CompletableFuture delete(Long id); } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/IFutService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/IFutService.java index 6b8724d..2656bf5 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/IFutService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/IFutService.java @@ -5,41 +5,44 @@ import br.com.vemprofut.models.DTOs.FutDTO; import br.com.vemprofut.models.FutModel; import java.util.List; +import java.util.concurrent.CompletableFuture; import org.springframework.web.multipart.MultipartFile; public interface IFutService { - SaveFutResponseDTO create(SaveFutRequestDTO dto); + CompletableFuture create(SaveFutRequestDTO dto); - FutDetailsResponse findById(Long id); + CompletableFuture findById(Long id); - FutModel findByIdModel(Long id); + CompletableFuture findByIdModel(Long id); - FutDTO findByNome(String nome); + CompletableFuture findByNome(String nome); - List findAll(); + CompletableFuture> findAll(); - UpdateFutResponseDTO update(Long id, UpdateFutRequestDTO dto); + CompletableFuture update(Long id, UpdateFutRequestDTO dto); - void delete(Long id); + CompletableFuture delete(Long id); - SavePartidasResponseDTO criarPartida(SavePartidaRequestDTO requestDTO, FutModel futModel); + CompletableFuture criarPartida( + SavePartidaRequestDTO requestDTO, FutModel futModel); - void addPeladeiro(AddPeladeiroInFutListRequestDTO requestDTO); + CompletableFuture addPeladeiro(AddPeladeiroInFutListRequestDTO requestDTO); - List criarPartidasList(List requestDTOS); + CompletableFuture> criarPartidasList( + List requestDTOS); - List listarPeladeiroCadastradosFut(Long futId); + CompletableFuture> listarPeladeiroCadastradosFut(Long futId); - void addEditor(AddEditorInFutListResquestDTO resquestDTO); + CompletableFuture addEditor(AddEditorInFutListResquestDTO resquestDTO); - List listarEditoresCadastradosFut(Long idFut); + CompletableFuture> listarEditoresCadastradosFut(Long idFut); - void atualizarFotoCapa(Long id, MultipartFile file); + CompletableFuture atualizarFotoCapa(Long id, MultipartFile file); - SaveBanimentoResponseDTO addBanimentoList(SaveBanimentoRequestDTO dto); + CompletableFuture addBanimentoList(SaveBanimentoRequestDTO dto); - List findAllBanidos(Long idFut); + CompletableFuture> findAllBanidos(Long idFut); - void removeBanido(Long idPeladeiro, Long idFut); + CompletableFuture removeBanido(Long idPeladeiro, Long idFut); } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/IPartidasService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/IPartidasService.java index a7a7d5c..05523a5 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/IPartidasService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/IPartidasService.java @@ -6,18 +6,20 @@ import br.com.vemprofut.models.DTOs.PeladeiroDTO; import br.com.vemprofut.models.FutModel; import br.com.vemprofut.models.PartidasModel; +import java.util.concurrent.CompletableFuture; public interface IPartidasService { - SavePartidasResponseDTO create(SavePartidaRequestDTO requestDTO, FutModel futModel); + CompletableFuture create( + SavePartidaRequestDTO requestDTO, FutModel futModel); - PartidasDTO findById(Long id); + CompletableFuture findById(Long id); - PartidasModel findByIdModel(Long id); + CompletableFuture findByIdModel(Long id); - void addGols(PeladeiroDTO peladeiroDTO, PartidasDTO partidasDTO); + CompletableFuture addGols(PeladeiroDTO peladeiroDTO, PartidasDTO partidasDTO); - void addPeladeiros(Long peladeiroId, PartidasDTO partidasDTO); + CompletableFuture addPeladeiros(Long peladeiroId, PartidasDTO partidasDTO); - void addCartoes(Long cartaoId, PartidasDTO partidasDTO); + CompletableFuture addCartoes(Long cartaoId, PartidasDTO partidasDTO); } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/BanimentoService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/BanimentoService.java index 3dbbf95..76d3f99 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/BanimentoService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/BanimentoService.java @@ -13,7 +13,9 @@ import br.com.vemprofut.services.IBanimentoService; import br.com.vemprofut.services.query.IBanimentoQueryService; import java.util.List; +import java.util.concurrent.CompletableFuture; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service @@ -29,7 +31,7 @@ public class BanimentoService implements IBanimentoService { @Autowired private FutRepository futRepository; - public SaveBanimentoResponseDTO create(SaveBanimentoRequestDTO dto) { + public CompletableFuture create(SaveBanimentoRequestDTO dto) { banimentoQueryService.verifyPeladeiroBanidoExist(dto.fut(), dto.peladeiro()); // Já foi verificado em FutService se Fut e Peladeiro existem... entao é só buscar @@ -43,16 +45,20 @@ public SaveBanimentoResponseDTO create(SaveBanimentoRequestDTO dto) { banimentoModel.setDataBanimento(dto.dataBaninimento()); banimentoModel.setDataFimBanimento(dto.dataFimBanimento()); - return banimentoMapper.toSaveResponse(repository.save(banimentoModel)); + return CompletableFuture.completedFuture( + banimentoMapper.toSaveResponse(repository.save(banimentoModel))); } @Override - public List findAll(Long idFut) { - return banimentoQueryService.verifyExistListBanido(idFut); + @Async + public CompletableFuture> findAll(Long idFut) { + return CompletableFuture.completedFuture(banimentoQueryService.verifyExistListBanido(idFut)); } @Override - public void delete(Long idPeladeiro) { + public CompletableFuture delete(Long idPeladeiro) { + repository.deleteById(idPeladeiro); + return CompletableFuture.completedFuture(null); } } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/CartoesService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/CartoesService.java index 12e16e6..43845bb 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/CartoesService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/CartoesService.java @@ -7,9 +7,6 @@ import br.com.vemprofut.models.CartoesModel; import br.com.vemprofut.models.DTOs.CartaoCountProjection; import br.com.vemprofut.models.DTOs.CartoesDTO; -import br.com.vemprofut.models.FutModel; -import br.com.vemprofut.models.PartidasModel; -import br.com.vemprofut.models.PeladeiroModel; import br.com.vemprofut.repositories.CartoesRepository; import br.com.vemprofut.services.ICartoesService; import br.com.vemprofut.services.IFutService; @@ -17,7 +14,10 @@ import br.com.vemprofut.services.query.IPartidasQueryService; import br.com.vemprofut.services.query.IPeladeiroQueryService; import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -40,65 +40,109 @@ public class CartoesService implements ICartoesService { @Autowired @Lazy private IFutService futService; + @Autowired + @Qualifier("defaultExecutor") + Executor executor; + + /* + Executor = uma forma diferente de tornar async + em vez de usar @Async("defaultExecutor"). + */ + @Override @Transactional - public CartoesDTO create(CartoesDTO dto) { - queryService.verifyEntitiesExist(dto); - - CartoesModel model = mapper.toModel(dto); - CartoesModel saved = repository.save(model); - return mapper.toDTO(saved); + public CompletableFuture create(CartoesDTO dto) { + return CompletableFuture.supplyAsync( + () -> { + queryService.verifyEntitiesExist(dto); + CartoesModel model = mapper.toModel(dto); + CartoesModel saved = repository.save(model); + return mapper.toDTO(saved); + }, + executor); } @Override @Transactional(readOnly = true) - public List findAll() { - return repository.findAll().stream().map(mapper::toDTO).toList(); + public CompletableFuture> findAll() { + return CompletableFuture.supplyAsync( + () -> { + return repository.findAll().stream().map(mapper::toDTO).toList(); + }, + executor); } @Override @Transactional(readOnly = true) - public CartoesDTO findById(Long id) { - - return mapper.toDTO(queryService.verityCartoesExist(id)); + public CompletableFuture findById(Long id) { + + return CompletableFuture.supplyAsync( + () -> { + var cartao = queryService.verityCartoesExist(id); + return mapper.toDTO(cartao); + }, + executor); } @Override @Transactional(readOnly = true) - public CartoesModel findByIdModel(Long id) { - - return queryService.verityCartoesExist(id); + public CompletableFuture findByIdModel(Long id) { + return CompletableFuture.supplyAsync( + () -> { + return queryService.verityCartoesExist(id); + }, + executor); } @Override @Transactional(readOnly = true) - public List findByPeladeiro(Long id) { - PeladeiroModel peladeiroModel = peladeiroQueryService.verifyPeladeiroExist(id); - return repository.findByPeladeiro(peladeiroModel).stream().map(mapper::toDTO).toList(); + public CompletableFuture> findByPeladeiro(Long id) { + /* PeladeiroModel peladeiroModel = peladeiroQueryService.verifyPeladeiroExist(id); + return CompletableFuture.completedFuture( <--- sincrono + repository.findByPeladeiro(peladeiroModel).stream().map(mapper::toDTO).toList()); + */ + // async + return peladeiroQueryService + .verifyPeladeiroExist(id) + .thenApply( + peladeiroModel -> + repository.findByPeladeiro(peladeiroModel).stream().map(mapper::toDTO).toList()); } @Override @Transactional(readOnly = true) - public List findByPartida(Long id) { - PartidasModel partidasModel = partidasQueryService.verifyPartidaExistWithRetorn(id); - return repository.findByPartida(partidasModel).stream().map(mapper::toDTO).toList(); + public CompletableFuture> findByPartida(Long id) { + return partidasQueryService + .verifyPartidaExistWithRetorn(id) + .thenApply( + partidasModel -> + repository.findByPartida(partidasModel).stream().map(mapper::toDTO).toList()); } @Override @Transactional(readOnly = true) - public List findByFut(Long id) { - FutModel futModel = futService.findByIdModel(id); - return repository.findByFut(futModel).stream().map(mapper::toDTO).toList(); + public CompletableFuture> findByFut(Long id) { + return futService + .findByIdModel(id) + .thenApply(futModel -> repository.findByFut(futModel).stream().map(mapper::toDTO).toList()); } - public CartoesResumoResponseDTO contarCartoesPeladeiro(Long peladeiroId) { - List resultados = repository.countByTipoAndPeladeiro(peladeiroId); - return montarResumo(resultados); + public CompletableFuture contarCartoesPeladeiro(Long peladeiroId) { + + return CompletableFuture.supplyAsync( + () -> { + List resultados = repository.countByTipoAndPeladeiro(peladeiroId); + return montarResumo(resultados); + }); } - public CartoesResumoResponseDTO contarCartoesFut(Long futId) { - List resultados = repository.countByTipoAndFut(futId); - return montarResumo(resultados); + public CompletableFuture contarCartoesFut(Long futId) { + + return CompletableFuture.supplyAsync( + () -> { + List resultados = repository.countByTipoAndFut(futId); + return montarResumo(resultados); + }); } private CartoesResumoResponseDTO montarResumo(List resultados) { @@ -111,7 +155,6 @@ private CartoesResumoResponseDTO montarResumo(List result case VERMELHO -> dto.setVermelho(proj.getQuantidade().intValue()); } } - return dto; } } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/EditorService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/EditorService.java index 6869013..2bcc778 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/EditorService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/EditorService.java @@ -8,7 +8,9 @@ import br.com.vemprofut.services.IEditorService; import br.com.vemprofut.services.query.IEditorQueryService; import java.util.List; +import java.util.concurrent.CompletableFuture; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -22,34 +24,54 @@ public class EditorService implements IEditorService { @Autowired private EditorRepository repository; @Override - public EditorModel create(EditorModel model) { - queryService.verityEditorExist(model); - return repository.save(model); + @Async("defaultExecutor") + @Transactional + public CompletableFuture create(EditorModel model) { + // queryService.verityEditorExist(model); ---> nao async + // return CompletableFuture.completedFuture(repository.save(model)); + + // Async + return CompletableFuture.supplyAsync( + () -> { + queryService.verityEditorExist(model); + return repository.save(model); + }); } @Override + @Async("defaultExecutor") @Transactional(readOnly = true) - public EditorDTO findById(Long id) { - return mapper.toDTO(queryService.verityEditorIdExistReturn(id)); + public CompletableFuture findById(Long id) { + return CompletableFuture.supplyAsync( + () -> mapper.toDTO(queryService.verityEditorIdExistReturn(id))); } @Override + @Async("defaultExecutor") @Transactional(readOnly = true) - public EditorModel findByIdModel(Long id) { - return queryService.verityEditorIdExistReturn(id); + public CompletableFuture findByIdModel(Long id) { + return CompletableFuture.supplyAsync(() -> queryService.verityEditorIdExistReturn(id)); } @Override + @Async("defaultExecutor") @Transactional(readOnly = true) - public List findAll(FutDTO futDTO) { - queryService.verityFutExist(futDTO); - - return repository.findByFutId(futDTO.id()).stream().map(mapper::toDTO).toList(); + public CompletableFuture> findAll(FutDTO futDTO) { + queryService.verityFutExist(futDTO); // sincrono + var response = + repository + .findByFutId(futDTO.id()) // sincrono + .stream() + .map(mapper::toDTO) + .toList(); + return CompletableFuture.completedFuture(response); } @Override - public void delete(Long id) { + @Async("defaultExecutor") + public CompletableFuture delete(Long id) { queryService.verityEditorIdExist(id); repository.deleteById(id); + return CompletableFuture.completedFuture(null); } } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/FutService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/FutService.java index 3aa0c5f..80272b3 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/FutService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/FutService.java @@ -14,8 +14,12 @@ import br.com.vemprofut.services.query.IPeladeiroQueryService; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @@ -54,11 +58,15 @@ public class FutService implements IFutService { @Autowired private IBanimentoService banidoService; + @Autowired + @Qualifier("partidasExecutor") + private Executor executor; + // ======================== CRUD basico ========================== @Override @Transactional - public SaveFutResponseDTO create(SaveFutRequestDTO dto) { + public CompletableFuture create(SaveFutRequestDTO dto) { queryService.verifyNomeFutExist(dto.nome()); var peladeiro = peladeiroQueryService.verifyPeladeiroExist(dto.administradorPeladeiro()); @@ -67,67 +75,78 @@ public SaveFutResponseDTO create(SaveFutRequestDTO dto) { HistoricoFutModel historico = historicoFutService.create(); saved.setHistoricoFutId(historico); saved.setAdministradorPeladeiro(peladeiro); - return mapper.toSaveResponse(repository.save(saved)); + SaveFutResponseDTO response = mapper.toSaveResponse(repository.save(saved)); + return CompletableFuture.completedFuture(response); } @Override @Transactional(readOnly = true) - public FutDetailsResponse findById(Long id) { + @Async + public CompletableFuture findById(Long id) { var futModel = queryService.verifyFutExistRetorn(id); - return mapper.modelToDetailsResponse(futModel); + FutDetailsResponse response = mapper.modelToDetailsResponse(futModel); + return CompletableFuture.completedFuture(response); } @Override @Transactional(readOnly = true) - public FutModel findByIdModel(Long id) { - return queryService.verifyFutExistRetorn(id); + @Async + public CompletableFuture findByIdModel(Long id) { + return CompletableFuture.completedFuture(queryService.verifyFutExistRetorn(id)); } @Override @Transactional(readOnly = true) - public FutDTO findByNome(String nome) { + public CompletableFuture findByNome(String nome) { var futModel = queryService.verifyNomeFutExistRetorn(nome); - return mapper.toDTO(futModel); + return CompletableFuture.completedFuture(mapper.toDTO(futModel)); } @Override @Transactional(readOnly = true) - public List findAll() { - return repository.findAll().stream().map(mapper::toDTO).toList(); + @Async + public CompletableFuture> findAll() { + return CompletableFuture.completedFuture( + repository.findAll().stream().map(mapper::toDTO).toList()); } @Override @Transactional - public UpdateFutResponseDTO update(Long id, UpdateFutRequestDTO dto) { + public CompletableFuture update(Long id, UpdateFutRequestDTO dto) { var retorno = queryService.verifyFutExistRetorn(id); retorno.setJogadoresPorTime(dto.jogadoresPorTime()); retorno.setTempoMaxPartida(dto.tempoMaxPartida()); retorno.setMaxGolsVitoria(dto.maxGolsVitoria()); - return mapper.modelToUpdateResponse(repository.save(retorno)); + return CompletableFuture.completedFuture( + mapper.modelToUpdateResponse(repository.save(retorno))); } @Override @Transactional - public void delete(Long id) { + public CompletableFuture delete(Long id) { queryService.verifyFutExistRetorn(id); repository.deleteById(id); + return CompletableFuture.completedFuture(null); } // ======================== acoes partidas ========================== - @Override + /*@Override @Transactional - public SavePartidasResponseDTO criarPartida(SavePartidaRequestDTO requestDTO, FutModel futModel) { - return partidasService.create(requestDTO, futModel); + public CompletableFuture criarPartida(SavePartidaRequestDTO requestDTO, FutModel futModel) { + return CompletableFuture.completedFuture(partidasService.create(requestDTO, futModel)); } @Override - @Transactional - public List criarPartidasList(List requestDTOS) { + public CompletableFuture> criarPartidasList(List requestDTOS) { + // dispara cada DTO em paralelo + List> futures = requestDTOS.stream() + .map(dto -> CompletableFuture.supplyAsync(() -> criarPartida(dto), executor)) + .toList(); //TODO: --------------------------------------------- - List partidaList = new ArrayList<>(); + // espera todos terminarem e junta os resultados for (SavePartidaRequestDTO dto : requestDTOS) { // 1 - criando partida @@ -190,14 +209,14 @@ public List criarPartidasList(List addPeladeiro(AddPeladeiroInFutListRequestDTO requestDTO) { PeladeiroModel peladeiroModel = peladeiroQueryService.verifyPeladeiroExist(requestDTO.peladeiroId()); FutModel futModel = queryService.verifyFutExistRetorn(requestDTO.futId()); @@ -209,11 +228,12 @@ public void addPeladeiro(AddPeladeiroInFutListRequestDTO requestDTO) { .add(peladeiroModel); // Esse adiciona a tabela: "participa_peladeiro_fut". repository.save( futModel); // Esse salva em FutModel (que é onde a table intermediaria foi criada) + return CompletableFuture.completedFuture(null); } @Override @Transactional - public List listarPeladeiroCadastradosFut(Long futId) { + public CompletableFuture> listarPeladeiroCadastradosFut(Long futId) { FutModel futModel = queryService.verifyFutExistRetornListPeladeiro(futId); /* Esse metodo retorna futModel já com peladeiros carregado... resolvendo o problema abaixo. @ManyToMany é LAZY, Ou seja: @@ -230,13 +250,13 @@ public List listarPeladeiroCadastradosFut(Long futId) { for (PeladeiroModel p : futModel.getPeladeiros()) { listResponce.add(peladeiroMapper.modelToPeladeiroResponse(p)); } - return listResponce; + return CompletableFuture.completedFuture(listResponce); } // ===================== lista Editores ============================= @Override @Transactional - public void addEditor(AddEditorInFutListResquestDTO resquestDTO) { + public CompletableFuture addEditor(AddEditorInFutListResquestDTO resquestDTO) { EditorModel editorModel = new EditorModel(); // Verificacoes @@ -259,7 +279,8 @@ public void addEditor(AddEditorInFutListResquestDTO resquestDTO) { @Override @Transactional - public List listarEditoresCadastradosFut(Long idFut) { + public CompletableFuture> listarEditoresCadastradosFut( + Long idFut) { FutModel futModel = queryService.verifyFutExistRetornListEditores(idFut); log.info("Verificacao de existencia de Fut realizada com sucesso!"); @@ -269,14 +290,14 @@ public List listarEditoresCadastradosFut(Long idFut) listResponse.add(editorMapper.toResponseNameId(e)); } - return listResponse; + return CompletableFuture.completedFuture(listResponse); } // ===================== upload arquivos fotos ====================== @Override @Transactional - public void atualizarFotoCapa(Long id, MultipartFile file) { + public CompletableFuture atualizarFotoCapa(Long id, MultipartFile file) { queryService.verifyFutSaveFile(id, file); } @@ -284,30 +305,32 @@ public void atualizarFotoCapa(Long id, MultipartFile file) { @Override @Transactional - public SaveBanimentoResponseDTO addBanimentoList(SaveBanimentoRequestDTO dto) { + public CompletableFuture addBanimentoList(SaveBanimentoRequestDTO dto) { FutModel futModel = queryService.verifyFutExistRetorn(dto.fut()); PeladeiroModel peladeiroModel = peladeiroQueryService.verifyPeladeiroExist(dto.peladeiro()); // Verificacao de existe o peladeiro em questao na lista de peladeiro... queryService.verifyBanidoListPeladeiros(futModel, peladeiroModel); - return banidoService.create(dto); + return CompletableFuture.completedFuture(banidoService.create(dto)); } @Override @Transactional - public List findAllBanidos(Long idFut) { - return banidoService.findAll(idFut); + @Async + public CompletableFuture> findAllBanidos(Long idFut) { + return CompletableFuture.completedFuture(banidoService.findAll(idFut)); } @Override @Transactional - public void removeBanido(Long idPeladeiro, Long idFut) { + public CompletableFuture removeBanido(Long idPeladeiro, Long idFut) { // TODO: retirar um Banido da lista queryService.verifyBanidoListPeladeiros( queryService.verifyFutExistRetorn(idFut), peladeiroQueryService.verifyPeladeiroExist(idPeladeiro)); banidoService.delete(idPeladeiro); + return CompletableFuture.completedFuture(null); } } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/PartidasService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/PartidasService.java index 396b9e7..64a5bba 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/PartidasService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/PartidasService.java @@ -5,17 +5,17 @@ import br.com.vemprofut.mappers.ICartoesMapper; import br.com.vemprofut.mappers.IPartidasMapper; import br.com.vemprofut.mappers.IPeladeiroMapper; -import br.com.vemprofut.models.CartoesModel; import br.com.vemprofut.models.DTOs.PartidasDTO; import br.com.vemprofut.models.DTOs.PeladeiroDTO; import br.com.vemprofut.models.FutModel; import br.com.vemprofut.models.GolsPartidaModel; import br.com.vemprofut.models.PartidasModel; -import br.com.vemprofut.models.PeladeiroModel; import br.com.vemprofut.repositories.PartidasRepository; import br.com.vemprofut.services.*; import br.com.vemprofut.services.query.IPartidasQueryService; +import java.util.concurrent.CompletableFuture; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -40,56 +40,77 @@ public class PartidasService implements IPartidasService { @Override @Transactional - public SavePartidasResponseDTO create(SavePartidaRequestDTO requestDTO, FutModel futModel) { - + @Async("defaultExecutor") + public CompletableFuture create( + SavePartidaRequestDTO requestDTO, FutModel futModel) { PartidasModel partidasModel = new PartidasModel(requestDTO.reservas(), futModel); - return mapperPartidas.toResponse(repository.save(partidasModel)); + SavePartidasResponseDTO response = mapperPartidas.toResponse(repository.save(partidasModel)); + return CompletableFuture.completedFuture(response); } @Override @Transactional(readOnly = true) - public PartidasDTO findById(Long id) { - return mapperPartidas.toDTO(queryService.verifyPartidaExistWithRetorn(id)); + @Async("defaultExecutor") + public CompletableFuture findById(Long id) { + return queryService.verifyPartidaExistWithRetorn(id).thenApply(mapperPartidas::toDTO); } @Override @Transactional(readOnly = true) - public PartidasModel findByIdModel(Long id) { + @Async("defaultExecutor") + public CompletableFuture findByIdModel(Long id) { return queryService.verifyPartidaExistWithRetorn(id); } @Override @Transactional - public void addGols(PeladeiroDTO peladeiroDTO, PartidasDTO partidasDTO) { + @Async("defaultExecutor") + public CompletableFuture addGols(PeladeiroDTO peladeiroDTO, PartidasDTO partidasDTO) { GolsPartidaModel gol = golsService.create(peladeiroDTO, partidasDTO); PartidasModel partida = mapperPartidas.toModel(partidasDTO); partida.getGolsPartida().add(gol); repository.save(partida); + return CompletableFuture.completedFuture(null); } @Override @Transactional - public void addPeladeiros(Long peladeiroId, PartidasDTO partidasDTO) { - // TODO: mudar o DTO para Long de peladeiro. - PeladeiroModel peladeiroModel = peladeiroService.findByIdModel(peladeiroId); - - PartidasModel partida = mapperPartidas.toModel(partidasDTO); - partida.getPeladeiros().add(peladeiroModel); - - repository.save(partida); + @Async("defaultExecutor") + public CompletableFuture addPeladeiros(Long peladeiroId, PartidasDTO partidasDTO) { + /* + Aqui você espera o CompletableFuture terminar. + Quando o PeladeiroModel estiver disponível, adiciona na partida e salva. + O método retorna um CompletableFuture que completa quando tudo terminar. + */ + return peladeiroService + .findByIdModel(peladeiroId) + .thenAccept( + peladeiroModel -> { + PartidasModel partida = mapperPartidas.toModel(partidasDTO); + partida.getPeladeiros().add(peladeiroModel); + }); } @Override @Transactional - public void addCartoes(Long cartaoId, PartidasDTO partidasDTO) { + @Async("defaultExecutor") + public CompletableFuture addCartoes(Long cartaoId, PartidasDTO partidasDTO) { // TODO: mudar o DTO para Long de cartoes. + /* ---> sincrono CartoesModel cartoesModel = cartoesService.findByIdModel(cartaoId); - PartidasModel partida = mapperPartidas.toModel(partidasDTO); partida.getCartoes().add(cartoesModel); - - repository.save(partida); + return repository.save(partida); */ + // Async + return cartoesService + .findByIdModel(cartaoId) + .thenAccept( + cartoesModel -> { + PartidasModel partida = mapperPartidas.toModel(partidasDTO); + partida.getCartoes().add(cartoesModel); + repository.save(partida); + }); } } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/PeladeiroService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/PeladeiroService.java index adb541e..5057376 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/PeladeiroService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/PeladeiroService.java @@ -41,6 +41,7 @@ public class PeladeiroService implements IPeladeiroService { @Override @Transactional + @Async("defaultExecutor") public CompletableFuture create(SavePeladeiroRequestDTO dto) { queryService.verifyEmail(dto.email()); log.info("Email verificado!"); @@ -51,67 +52,67 @@ public CompletableFuture create(SavePeladeiroRequestDT peladeiroSalvo.setHistoricoPeladeiro(historicoMapper.toModel(historico)); log.info("Peladeiro cadastrado com sucesso!"); - SavePeladeiroResponseDTO response = - peladeiroMapper.modelToSaveResponse(repository.save(peladeiroSalvo)); - + var response = peladeiroMapper.modelToSaveResponse(repository.save(peladeiroSalvo)); return CompletableFuture.completedFuture(response); } @Override @Transactional - @Async + @Async("defaultExecutor") public CompletableFuture update( Long id, UpdatePeladeiroRequestDTO dto) { - var peladeiroModel = queryService.verifyPeladeiroExist(id); log.info("Verificado a existencia de Peladeiro"); - - peladeiroModel.setNome(dto.nome()); - peladeiroModel.setEmail(dto.email()); - peladeiroModel.setApelido(dto.apelido()); - peladeiroModel.setDescricao(dto.descricao()); - peladeiroModel.setWhatsapp(dto.whatsapp()); - peladeiroModel.setPeDominante(dto.peDominante()); - - log.info("Peladeiro alterado com sucesso!"); - UpdatePeladeiroResponseDTO response = - peladeiroMapper.modelToUpdateResponse(repository.save(peladeiroModel)); - return CompletableFuture.completedFuture(response); + return queryService + .verifyPeladeiroExist(id) + .thenApply( + peladeiroModel -> { + peladeiroModel.setNome(dto.nome()); + peladeiroModel.setEmail(dto.email()); + peladeiroModel.setApelido(dto.apelido()); + peladeiroModel.setDescricao(dto.descricao()); + peladeiroModel.setWhatsapp(dto.whatsapp()); + peladeiroModel.setPeDominante(dto.peDominante()); + + log.info("Peladeiro alterado com sucesso!"); + + return peladeiroMapper.modelToUpdateResponse(repository.save(peladeiroModel)); + }); } @Override @Transactional(readOnly = true) - @Async + @Async("defaultExecutor") public CompletableFuture findById(Long id) { log.info("Buscando peladeiro pelo id... saida de resposta"); - PeladeiroModel retorno = queryService.verifyPeladeiroExist(id); - CartoesResumoResponseDTO resumo = cartoesService.contarCartoesPeladeiro(id); - var response = - new PeladeiroDetailResponse( - retorno.getId(), - retorno.getNome(), - retorno.getEmail(), - retorno.getApelido(), - retorno.getDescricao(), - retorno.getWhatsapp(), - retorno.getPeDominante(), - resumo); - - return CompletableFuture.completedFuture(response); + CompletableFuture peladeiroFuture = queryService.verifyPeladeiroExist(id); + CompletableFuture resumoFuture = + cartoesService.contarCartoesPeladeiro(id); + return peladeiroFuture.thenCombine( + resumoFuture, + (retorno, resumo) -> + new PeladeiroDetailResponse( + retorno.getId(), + retorno.getNome(), + retorno.getEmail(), + retorno.getApelido(), + retorno.getDescricao(), + retorno.getWhatsapp(), + retorno.getPeDominante(), + resumo)); } @Override @Transactional(readOnly = true) - @Async + @Async("defaultExecutor") public CompletableFuture findByIdModel(Long id) { log.info("Buscando peladeiro pelo id... saida de uso interno"); - PeladeiroModel response = queryService.verifyPeladeiroExist(id); - return CompletableFuture.completedFuture(response); + return queryService.verifyPeladeiroExist(id); } // @Override - // @Transactional - // @Async(readOnly = true) - // public List findAll() { + // @Transactional(readOnly = true) + // @Async("defaultExecutor") + // public CompletableFuture> findAll() { // // return repository.findAll() // .stream() @@ -120,6 +121,7 @@ public CompletableFuture findByIdModel(Long id) { // } @Override + @Async("defaultExecutor") @Transactional public CompletableFuture delete(Long id) { queryService.verifyPeladeiroExist(id); @@ -129,6 +131,7 @@ public CompletableFuture delete(Long id) { } @Override + @Async("defaultExecutor") @Transactional public CompletableFuture atualizarFoto(Long id, MultipartFile file) { queryService.verifyPeladeiroSaveFile(id, file); diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/IHistoricoPeladeiroQueryService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/IHistoricoPeladeiroQueryService.java index 182da8c..134d80a 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/IHistoricoPeladeiroQueryService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/IHistoricoPeladeiroQueryService.java @@ -1,10 +1,11 @@ package br.com.vemprofut.services.query; import br.com.vemprofut.models.HistoricoPeladeiroModel; +import java.util.concurrent.CompletableFuture; public interface IHistoricoPeladeiroQueryService { - HistoricoPeladeiroModel verityHistoricoPeladeiroExistReturn(Long id); + CompletableFuture verityHistoricoPeladeiroExistReturn(Long id); - void verityHistoricoPeladeiroExist(Long id); + CompletableFuture verityHistoricoPeladeiroExist(Long id); } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/IPartidasQueryService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/IPartidasQueryService.java index 1d0d7dd..445a98a 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/IPartidasQueryService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/IPartidasQueryService.java @@ -1,8 +1,9 @@ package br.com.vemprofut.services.query; import br.com.vemprofut.models.PartidasModel; +import java.util.concurrent.CompletableFuture; public interface IPartidasQueryService { - PartidasModel verifyPartidaExistWithRetorn(Long id); + CompletableFuture verifyPartidaExistWithRetorn(Long id); } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/IPeladeiroQueryService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/IPeladeiroQueryService.java index a7057cc..d7e16b2 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/IPeladeiroQueryService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/IPeladeiroQueryService.java @@ -1,13 +1,14 @@ package br.com.vemprofut.services.query; import br.com.vemprofut.models.PeladeiroModel; +import java.util.concurrent.CompletableFuture; import org.springframework.web.multipart.MultipartFile; public interface IPeladeiroQueryService { - void verifyEmail(final String email); + CompletableFuture verifyEmail(final String email); - PeladeiroModel verifyPeladeiroExist(final Long id); + CompletableFuture verifyPeladeiroExist(final Long id); - void verifyPeladeiroSaveFile(Long id, MultipartFile file); + CompletableFuture verifyPeladeiroSaveFile(Long id, MultipartFile file); } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/HistoricoPeladeiroQueryService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/HistoricoPeladeiroQueryService.java index 4ece915..7ddfe08 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/HistoricoPeladeiroQueryService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/HistoricoPeladeiroQueryService.java @@ -4,7 +4,9 @@ import br.com.vemprofut.models.HistoricoPeladeiroModel; import br.com.vemprofut.repositories.HistoricoPeladeiroRepository; import br.com.vemprofut.services.query.IHistoricoPeladeiroQueryService; +import java.util.concurrent.CompletableFuture; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service @@ -12,18 +14,24 @@ public class HistoricoPeladeiroQueryService implements IHistoricoPeladeiroQueryS @Autowired HistoricoPeladeiroRepository repository; @Override - public HistoricoPeladeiroModel verityHistoricoPeladeiroExistReturn(Long id) { - + @Async("defaultExecutor") + public CompletableFuture verityHistoricoPeladeiroExistReturn(Long id) { return repository .findById(id) - .orElseThrow( - () -> new NotFoundException("Não foi encontrado o Historico de Peladeiro de id " + id)); + .map(CompletableFuture::completedFuture) + .orElseGet( + () -> + CompletableFuture.failedFuture( + new NotFoundException( + "Não foi encontrado o Historico de Peladeiro de id " + id))); } @Override - public void verityHistoricoPeladeiroExist(Long id) { + @Async("defaultExecutor") + public CompletableFuture verityHistoricoPeladeiroExist(Long id) { if (!repository.existsById(id)) { throw new NotFoundException("Id: " + id + "de Historico Jogador não encontrado!"); } + return CompletableFuture.completedFuture(null); } } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/PartidasQueryService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/PartidasQueryService.java index dc8abd3..9806409 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/PartidasQueryService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/PartidasQueryService.java @@ -7,7 +7,9 @@ import br.com.vemprofut.repositories.PartidasRepository; import br.com.vemprofut.repositories.PeladeiroRepository; import br.com.vemprofut.services.query.IPartidasQueryService; +import java.util.concurrent.CompletableFuture; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service @@ -21,9 +23,14 @@ public class PartidasQueryService implements IPartidasQueryService { @Autowired private PeladeiroRepository peladeiroRepository; @Override - public PartidasModel verifyPartidaExistWithRetorn(Long id) { + @Async("defaultExecutor") + public CompletableFuture verifyPartidaExistWithRetorn(Long id) { return repository .findById(id) - .orElseThrow(() -> new NotFoundException("Não foi encontrado a Partida de id " + id)); + .map(CompletableFuture::completedFuture) + .orElseGet( + () -> + CompletableFuture.failedFuture( + new NotFoundException("Não foi encontrado a Partida de id " + id))); } } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/PeladeiroQueryService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/PeladeiroQueryService.java index 2670d1f..a96cb9b 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/PeladeiroQueryService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/PeladeiroQueryService.java @@ -8,8 +8,10 @@ import br.com.vemprofut.services.IUploadLocalService; import br.com.vemprofut.services.query.IPeladeiroQueryService; import java.io.IOException; +import java.util.concurrent.CompletableFuture; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @@ -22,42 +24,45 @@ public class PeladeiroQueryService implements IPeladeiroQueryService { private final IUploadLocalService uploadLocalService; @Override - public void verifyEmail(String email) { + @Async("defaultExecutor") + public CompletableFuture verifyEmail(String email) { if (repository.existsByEmail(email)) { var message = "O e-mail " + email + " já está em uso"; log.error(message); throw new EmailInUseException(message); } + return CompletableFuture.completedFuture(null); } @Override - public PeladeiroModel verifyPeladeiroExist(Long id) { + @Async("defaultExecutor") + public CompletableFuture verifyPeladeiroExist(Long id) { log.debug("Verificando existência do Peladeiro com ID: {}", id); - PeladeiroModel peladeiro = - repository - .findById(id) - .orElseThrow( - () -> { - log.warn("Peladeiro com ID {} não encontrado no banco de dados", id); - return new NotFoundException("Não foi encontrado o Peladeiro de id " + id); - }); - - log.debug("Peladeiro encontrado: {}", peladeiro); - return peladeiro; + return repository + .findById(id) + .map(CompletableFuture::completedFuture) + .orElseGet( + () -> + CompletableFuture.failedFuture( + new NotFoundException("Não foi encontrado o Peladeiro de id " + id))); } @Override - public void verifyPeladeiroSaveFile(Long id, MultipartFile file) { - PeladeiroModel peladeiroModel = verifyPeladeiroExist(id); - try { - String url = uploadLocalService.upload(file, "peladeiro"); - peladeiroModel.setFotoUrl(url); - repository.save(peladeiroModel); - log.info("Foto salva!"); - } catch (IOException ex) { - throw new FileStorageException( - "Erro ao salvar a foto do peladeiro com id: " + id, ex.getCause()); - } + @Async("defaultExecutor") + public CompletableFuture verifyPeladeiroSaveFile(Long id, MultipartFile file) { + return verifyPeladeiroExist(id) // retorna CompletableFuture + .thenAccept( + peladeiroModel -> { + try { + String url = uploadLocalService.upload(file, "peladeiro"); + peladeiroModel.setFotoUrl(url); + repository.save(peladeiroModel); + log.info("Foto salva!"); + } catch (IOException ex) { + throw new FileStorageException( + "Erro ao salvar a foto do peladeiro com id: " + id, ex); + } + }); } } From 8531f9e210e7eceb3a7d6c6fe9e2d887bec88ba4 Mon Sep 17 00:00:00 2001 From: Marcio Costa Date: Mon, 5 Jan 2026 22:03:53 -0300 Subject: [PATCH 13/13] =?UTF-8?q?refactore(transicao=20para=20assicrono):?= =?UTF-8?q?=20-=20transformei=20todas=20as=20classes=20de=20services,=20qu?= =?UTF-8?q?eryService,=20controllers=20e=20mappers=20em=20assincrono(async?= =?UTF-8?q?);=20-=20gerei=20uma=20imagem=20na=20pasta=20de=20fluxograma=20?= =?UTF-8?q?da=20logica=20de=20funcionamento=20do=20metodo=20criarPartidasL?= =?UTF-8?q?ist=20(async);=20-=20adaptei=20todos=20os=20testes=20feito=20at?= =?UTF-8?q?=C3=A9=20aqui=20para=20async;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Fluxograma_BPMN/criarListPartida.png | Bin 0 -> 73550 bytes .../configs/OAuth2LoginSuccessHandler.java | 37 +- .../vemprofut/controllers/FutController.java | 83 ++-- .../vemprofut/mappers/IMappersDefault.java | 10 +- .../services/IHistoricoPeladeiroService.java | 11 +- .../services/implementacao/FutService.java | 398 ++++++++++-------- .../HistoricoPeladeiroService.java | 42 +- .../implementacao/PeladeiroService.java | 15 +- .../services/query/IFutQueryService.java | 22 +- .../query/implementacao/FutQueryService.java | 110 +++-- .../unit/services/FutServiceTest.java | 36 +- .../unit/services/PeladeiroServiceTest.java | 43 +- 12 files changed, 477 insertions(+), 330 deletions(-) create mode 100644 Diagramas/Fluxograma_BPMN/criarListPartida.png diff --git a/Diagramas/Fluxograma_BPMN/criarListPartida.png b/Diagramas/Fluxograma_BPMN/criarListPartida.png new file mode 100644 index 0000000000000000000000000000000000000000..9a2f0f9af53fffa34b7a3556f7f30a23a345d595 GIT binary patch literal 73550 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%0DC=uq%+4@GE_|9RiO9|iy_ zMeEh{9t{;;up@!cSnWYAkn&mX%LX&VlLV&U3i6eN_IOFJk`ezMG^?biTEydGz3U+B z!dqzWgCV%oIS__MlxG6qXmo&1Z#giqsM?ySh@So zAUdPKx9sx&SwntQtY>Eo_Jf-WpvsE!o7NjaJ7Vzb*Br2k=qgoqO}tLgpTk%&a4~u| z>G%IZLq=wKh8gk5BA*9D1O)y6{dUgY82l(2S0Z>`r6 zdbaJC=e?&cu;{{8buve8^~vxJm$8=vSVzH-fVAr2jUoegoYHtM{taRtw!UOwpjc8~ zX!0S{pYK~EgG?Scs_WJ!65y36Q0nv@J^gtUXhDg;PMykFr)U1sG_2{kvzk!5xoL0Q ztvp@vYV^A6%o+6Bc&zvObQb8;@NmVuZxgEdh1WHmu9{3HrfUz5AwJ^qq2AAROHrQ~ zX1eA_o6ih`TU+su(~&S9?hgrh{kfzP{vUYyZ{GkyF#|2-pe0+Xq-g z9n^X{Kq_zL|KPBw!!tXb(-guqh!||1Jlb_Eh%hlSvO^g7_|2tUU_9pg)EEcuT0?MH z*nd8kl$3GA7+HrLzf$K)a|t~wG8Mn#BZa&=4D^uHq4zVsBvQ%a27hTT-3kpwC8Z5j zG~DLU<=@`y@2RP$Plg|pNt3Zu!MD`aL`T0BfTq-8LS8hK5H~XpOF18Ngp!fkBRK1h8@D5+t+xaXOBF#rPw9DeI4%{E1{YWVXa-(~#82wx5?+p} zoI6Z08SpLBoC2R4f;ki075-CkhO)s78V_&W@(weyBUpZbBdiS49|S{@)ei)^uD0%> zZqTPGTf1aJqn0rgaOdvNvu8yMDk0v0oTxjLRKGz3DyxLR|JY_5czt2Z<|E>W@?PZc z20wXyENK=*W;YAC;Xa^A6w!*?{@d244&R~4eMc60aaF}Num>AMm0DlOA>!k}q=p1VGf}}nj*!}(2;nw8?_3Xh__LGj(mqZavI_#v6LC|nr0}tpSS7EB zLa4n~2=UJU4{gSEcz;S*=DGaHIa0j{IDwhp@g&IC6gLgoSaBu;=A}7*0Tf^tS0*~KnufcuUg$n3g5-L_EQm4L(sIPaaV%)=cZUY?s6(%#w6qDPP71gNr= zlY5BpwR-N*SwR)S9tBZ;-~yiMP)E@ger&xjP44bPeqhM#|b_>h)^ zGfkJ)i%C@1EC{rOe6Ey@Gc#(Lb_qy;fio$Cp5ZL0T1y22=OPnatuGKWBMP+c6wg0_ zXG6}fwJMPxWpv5+a^O)JE5kqhnG_UFKY=nl2g*W7P6kBsAw<`u>MKZg4R72f38~`$ z-6gCMB?Y!i{@Pz*NKb`tqTkI|HhF!!hbd{dhFb7Vg^nvOEWe&K>GB~>hLT~@4K{Y0 zUrf^fcx_Z!jB79q42T83w7k`bVtayc7(lM|ERg5_lgwz)Ey=~0FN(_1uAgKp{v`!r z6y#-$prc$35kEI~yh6xA752%BW8+YE2!Y;wr2}%e;P_zs_^(A>W2d$LNTvnB!@z0_dg&^k*s^2p}k^f zu4EMQ+L!>+JLtFNq2)+7&)9(Y6z)9htBpa9W5sCQ=QOKn#;yRxX}Rd)xk^YNAXNlN zcoA?tA9982u!=<<*iI8RXtaRU&&G0fan2XkOzvZ3N5nlTCIlFqbDPgab`#_V;kdWm z?>vSSZ+I^HQDv#**Nc?z9YWNxfVKoK)=UvfG}PNa7CxI}l=XiHsDZ&+^;Z9hFCj;{ zYKl^=2ptd-0+yjP!WUfsULo*JPbx<5|7y(r%`1v%tTg}1d{`cZuhJ4H3}`|?bew~E zEp0J4s14zi60J!$KDQkMvWxV35vT$pF{SZ*$0?(|HGi!ATGyQAW(Cr=3sB6B2z(zT zmk~Uq!~6BlKql67D66MI6EwnD0D#KPVONlAAYdM&#Vxs2QwM9ScElRSXI+BaS6PAe z6p}T3Hl-D0Ckn|z*Y)KGGLCCUkW3<2`9HtB@?puoHL;h4+r~l9CO0=HG^jnlw7a>a z3P3O7D5@&M$@3Rz1Vfkm;$n;klj19P7X|;yR^+e$ZNG;<`F}M@M%wH+ldw_qrk7N$ z3H1#vq4|*6Yl2U*(II}5CuLE756wZGspQhYbt~?0gKzA+r znV7+8Iy#@n?BgetL&v4@w_WwWPE!NOPg#x(_LW+HgcOshc8iQfN1)g0^15;uH{iqI zYy8(~f9v(~^W^I{W_H^x>P`9jOQRyY+6M&-wo;ew#<{zf6c_jWb!T+{LUsQ_ZNDV3 zE2S9rJ?N^SjOfJvw&k5hmsSvquxo%#HpX1XLbKTv6wZ4<1qb~^G;0yS8 z{neU;K&&zc-i4~}qqmJJnVD2YcolXBIj}=G?yQMCwczOR^y0KNfda@Kmz0!_e-`Am znL3W}wB!^1^-qF;&L&FUQ$jrbk3p;La>;0bgxJ|e?)@<12Y#mRWj3*Qf1XK?rY8bPKzbe znK=v$3}~FGy4U;nSgjIk>l70)IoCu`F^BMC-HN=x5V!RYAd zAfJ4H{6e6CbKPeKe73eA;(DGrbEdJm86SSfz!vn32Y0QltvPrmsQ&rq$^})uz(2heK;5#{>r`Kc`dO`;QQ0X+{an!a|cvG!tdTNS; zhv!>cyVi7TY*?=;_qQ7c2GhD;T9lrnjVMM~GXIe@sECV;2aGDaxVYr)tS&Ft^Y4}1 zje@wWJYi?5IWpCaw3BYukJBi#U!Yxx3<^@p{E~66$QXJ|yKLClCRR5$S&_SBp+l&S3$a!SEFhGzl|wY77EKXHk1xt?+3zbx(TRgxt`U_mAy?AJPegZkoadOEt4uV2sW z!(;5p(}Ng9&BGNe1h)Q*@AI>8#bbz3F+W3}8bM1Z3lEJpBWEuqf+U#q9IM_iHa3<3 zTMa|Ghhr_NRGXWdWdN^@#$(acra=J# z3eVxiw{C5{PEJXgTOO?ufXLOu75ahCU9i^nXO%~Ulz$a%mtc2r-HIyRGI;qi31m3| zu8^Lg-S%{O@PK0UoM{VO7p|#PXpE;a4GIlanW#Aks?FfQz=X%%CuiH@AH#c@omGEn zNf;fq5|)L!jTSTVVzgLGN=hL5ym>cDz?ALdw{#7ho(nr zYN~N}c2-}Z34PBEcJ}o8FPvOlGAk=9K+EkOs&EEl(At{Q)*1_s5izgk>;ra(AZCTu zudiRff=Ps(e{C1hHCb9(#&@{9ef##GR0^##phb3cbWE3x7TB(|$W!y7wFd1dCpWh& zSf;*w138bnwarZw&jiH#6*{TZAib@Pz7J}7DCFtt>QZTYPJtlBGWTzkw-Paj6G{RJye5P9eXVAQbj@!{%^1o~c*4S{pGnw1C!jpO?Dca~HSK0<*D z@{G((OicI;Rn#yaWDAcSJ0|vCD?B{h=i^5OY3Wb)3w`L3+lLMvqIRV+e)jC2tMu>% z5PPQD4AXP$UsVRyz;GtMjm);{KrCD+?GbgH%jvPLG?t<)X(qW2uU$`D*WxyxE z=f0?c>qSxODA$L;nGP}(F>!I@ew^76Mid)c8r0i);SN)En>4Ep{Yq3s?#co4~8y7Yf-o`T~MUN0DfajlpUBM)Oqc9)k9tYd~ z2qh)fYW&wW?lv$0KYnCihr;i43*^?G!g=8H;X_*}mnM~v4zg~mw2qxT845c&CiMhG z@ppKK)t|MFr&2lVVvLQUZE%%H0}&VE^o!C{JOueNmQa2^h&lur?NjRGx@KFGAR zv`0@*#|k>gf>{%ASd0QSkm|_FCvm-itE%$yr(tdUJ9F;r*#v+DPxb$L$-|DgEnhgVU3+vRsA0##?jaBW zvaYH=^lc4eR}zPWVK*W80EIiW<^4OC!8sIc{*05-ix)2>B_%oj`dMP{m4K1iz5R(| zj~<=|lG1B<`R6E;OI){(vkX9=70u1Fai+Zqd3trmKR=woann4w3J=$p)*AXTz?xWF zD}qLb+U1)r_zLGvpMFq(go1qL0Noo+4JRiju)^Rc!Xe{>4@*roi1(*Fb>c*bw|AYL z2r~u$5Ayz|s7yGI7^q;{76;4FW%gzu@OfZvZjO+j!9JO4UwcDlVk0nF1jL^bSz!|qsZ{&-c$5Xnjt;!8=OCaEN(WYT(@P$S z(&Y;QaO6$Ly6eQcZiM?~9I{LVjT#)Lusi4kCra;KEQ_-%fei$9#k@NFz#9_e>g2D1 zs{xAv=3vlfpd1`Fuhp=lP&ot_aSF)@-^5&Q-F4bGdvG1LYu6Ist#EK))vkv5`bx!o z;jG%F6BQMG#QiVd+Me~!osNsLwCg`BdwP0wiD;za;H5M$bD&re+&X1=+nFX4w|1zi z@rY4XsYhSV&dvsF2FJ$0E(MzvEO|Bco1p}^J3%2K=$H3s|Jx7ae|5x535e7KaR-Xc z1?=XMz>R2t90{BzdT@J$8{j!2C(HWBy1yP)Q*(1x8)gxp>#`7ibrTuq#0MM>7MH*i zm529^!_Xe(dX&>fX47ixUG*KP@!vUB4?%iR1F!LtS{wB4(z3Pkr zUR6x?ft$0d`AkhACLt;L2O_m#K$=HIT?SmfH&71Xcay=TZ3J;x*W}1WIJD7{l8ySC zR`g3N5IzNihn86hXDd~CSlL#ruWyt7rtQa%A0q_pu&_N~NhAULAwwZiT*&K8F;%iu zm`XU0erMv-Q{aMs@0!$7Y2Dd!UZ$-j!DG55{ zz_Tn8ka$WKE(hHZASVzdHnbUqTgxD#n*Ch~O|d)mGlmFROFVI$#rnTiGAvHg1lnzSc zFwb_jQz(o3{iCi}F}eopQO56vch7PUJc2G9=@hA@qc$jKS1VFg!ujLxU6zjO2mDbC zg1iP7jKQ!EfFfB`RCLaC`T>4g$Zu!iAT{Aa_vM>DLSSv2hw&?P1EaBQY=lya}hG(bA0Yw#8u|o*IjnPTP zdPFLT8)I=f>hZj51}2;Dc_Z#wB`~_KD`&$`ToYioR)9X&>y(=ds z#)Ayr8?CL@I^rPAU!|r}K*r!4^{(I4mDeZTc@k486nddHC?YuRjBtO`V3X$3{(mYJ zXDNCHLTRPeS31b!BD6)HGC!?TA5iUdtc~%nzho;GqbmKR_>WTH+PvOf0pl)@d_L@X zwq7vuDWBXP`?|*@K*)u)7>4%g*@)8FgPGjgrE^-30*7`T8TpSqH~`=7?(9MUl4>}c zKz6*IZ}I$#zy?!hS?p_Bz=!f>o@N=0wRvUygMc#r5vzIb^jT!^MKyQ%?(%bJkGGEX z(HyF5zC`SQm)BRNpJ*2e-3%Pv*%@VwPRd-RLq;`oZvw%on(^D+;pptvYzjj`zi~IB zi7O5TSFg~C$RWJ9vpaB(!pm}VwVfIxjVN;SD#{A%znhwE>3NJLG9wqqs^r8+*v^2e zS>Y|I&^Ex_WZ)B&x%NY&PN%wR^3Ayqq#lgenv(Jna@Gh`PF3eDT(T(wiJ<3fK{fAl zklU0RU}PN0c3`nV+1uL1qJ4)S{RXTnN4WG2UIM~hp_mXLYyHV-SJvbN&spA+|f4>2Izho$WL8?KQ@ICEf>{R-3p- zG)%hE%PL(Bl2o!ZlFODV`(bAze<4_Bo6(P+2Rxu0&1MBD<<4>f1CIL;zsLH-b?B>` zWYX$_J9#ts?6W_-?Jdm;Iy2=Qu}g?Nu>krL_3vJUOXv|4v(qEz)Qf>YV{MmT_4M?i zBfTX~CBKeedP(5PZUhAmK({M+9rjX@PFp#qTYZ%DFAel11GNSQOtp^53vKMEyFktt zf#-uV25zSNf&Hr#Ev*tcv4$7^v3`2*Py7jGq0TlQ2B8sxyskAao9i};5VHP>W;mLA z6l_m6Ag6_e;kjuk@#FuwDRGwnNNxbM#shIyIeMT>Q~koM_2x!9Xc*HwMvf{s*4!C( zy=XUs6=x{b=@5Xt>H2g|hwX9>QBvYln5K~fIuk9&aCA5adVF$9hoPA)9rW}s@9J#z z+{<;W(22UM-LJ~s-jm9xoWEF+jb|UZ@%@xh?h)thn=R5(__^K=yOA6_q`faOKOf`k5iy^hE{%tg)=1;7#7$nomcllxHqzX^ z88>*?d;I*PRrmQZZ;tdl_}QQb-}_wzR32Mhwksr^9g!YyfZ6NNU>qgo+*h)o0djR- z4~Qbmo1!DCji!7}&u?kboqm~|YBI(thj0&1Rq~1-h@T8O%ajVx7-InL&H(CHLB5ug zIuGeSR8lIK@ID5G7z>j(yffPhfk|BzODcpR{?>A13YIWeS2|G^s5wB3t_Cz>vcV{x zvaamTOu#JyVq0UNecD)#h9dhzg$tjgB7Et)6Ujx_C7_i?c4;^xR^+ud@WC@cH>PS= zehFHoVG2O~_tBJy`oqEJ535!4=7M?)Z329R*E3cG9J~O8!T>EnsF2G{oK+*=5QU-B zNoPrDgHrYt+^O$q#r$V5l0@L=#l94MQCSt!CttOsN-{t$gP)A?zAgbs#RTG-8`|$z zGeE8{SKKoyE#F(qPRz4kJPsXpMgR!4wyv~f9-E!cDPwfhfbLl6l2nziZA6yro`p#F zN7u5KwDjp06?6cDanCaCqRx1L9)-Ec#%k}D1r$`Uofb(jA7W-?>O>c) z&T7{R-4;>sU6mggI@(MBw6-Fl$T7im8JqpsI~}TZfk`#}`+pFDg9yhXZu8{M z+{gR~Q}HcoL==h^`-vch1z;uyW>z`$I>QI zH^+Y7mT@GAq0_scT}5NHxA`e3UV6vYYA+VNR}I_QT>lQU2omOh9|uJIxeI>c%?#v^ zZaX7(!Yd8@j(x}jx#S+3$SSfeuCwnUkmr!3P?-L!lc@l zfX2PK1APBc2T_D}3XeAwk)LL#RFESghI^l$s@nY+I6k%xf=Ohy!@|z6jk;fo7BZ8A zxYJ4veqtt2c#UTLgKWtJCM>8W3P5xw<4ooizOyKZbWi@48$xPBh;oP)hO)6+R3xqP znxxy?&}ARpr@r=lMAQe`6|ZlHaj9l*{EJIkYH~X?K0lWdp!6yOut-Qc2^#bfy(#c$ z)%stiBqIt#SdmkLHwB@Bd!RNgv6ZHE_B+2Qvb#W7pRUrOBH++qMU|lxh|AH+1O*1d z@IjhH@RTyjiB>j9YyNekrobb{c!D{I-+FOXt0c8PCH4V-qTKkDO&~Hu~MMeZ)fs_s_ zy(+ZLqqBz9VBFo;WvU19^RxLY!~aLsxYY7gZ&Fh<0_21yMQE{80n}>O5HW+hADMPR z>@*01mB$Ij18;-}_0Z=g(CA7&ifUmL0k{lh6Exd``A$=)Gw*VRc@fv3xxsArWN2bg zzKiEue9G|i1+BvJHySpb1^r%_vzMTcAG6|(%uA3gBs9R+hAA$J^0gd&MKAo2uG*jw zmgy>{z7c5MMoI65FH;p$LNn0bQ+jtju$;d5A=X=I)tmO&I@!&>)10?&Xwch<)tT!`f4^+C zK#t-_erl_@1jwYbPMTLPI~x9Xb^OH%hp}tSwub#T%)XK{?3j?wnN?a8fs>t2!`+2k z*ur=p1YpV%bf7>3u^kl3b?ikzY>|bj`zs&jZZ*iSy#{T2^lD{t)uOJoPsOmhSx$~a zn_M)RAbN$)YUS?un!DSD2DBQ5@3H*m;JcJz_}(6`4uM9KeP-YR^8mhUZr;doXPdsa zGVUrK8N0zW?AJXKHlz$ar~2TM|H){bFw(oJdk+V#1^@=oet80hTD)%@R(Xn&jo=12 zH!@AJ$!!OxW=A)-#DoK>D$rh?JmmTi!5N|^0YG;W(g;2n749^af6Qy>?tup{V14$8 z-cxRWk-BE;4Eh=g#ec_Ge4!R)ryU#iqz4U`Ei}Wm+CV0W*U zyITo-0x<>Hdqg@1$^_w!mDeHZ@)Ae)HQdE`DXNpS7c2<=Z;c;g3(0R&@?Vuc5a3wt^Y^sKgyc(bkA4msEV z{F){d8y(iwnP7l{S%ngf&Z2}_!scdWdzTr|`?_*m|4$S*d9Acfz;m^PUWCb=R!T4jc4lfgc6i!g&833cLk6 ze0hb}{|h$exPPW9czoFRM;&yJgZw^ukJW%Y}01FN>7(`hIP2pdc zbZeD{9mOQ_=-tlyBJKB}a1VYl!SUutZxg(qy9-@X(Cys?WO#u8+3GHF`?=1=3qcFP z{~sV)!J=lx3#E{a)jxshLBYTk1bkcb`88aqY@}AEK%{Mj67VMe%mP3Q+E*4h!`KAo zGeJI)1{wjv8tbBJPhJglgd=Vr$nORS2fIPf>|qioz3t<)n*R~zkluAA{m^TamO+G7 zVEN-V9EQV|g3|W0WSaouq3`)ig!Bb8vYnu!1a@Ka)F<+jrW9A0xgv)8XMSQV;i>43 zB!DP0L2jX9t(B<-#Vr`AD!e^=czcg@${@BbrXw(&rqFuo6!bhLzOUSffli-Km9JM1 zR0!yA=#$t|2}_ARIgVy(C2C%?dl zln5UJX!D(8Eh1fo8BU`6nFu)>roiS#MQfuAAC5Q0P~DI)+c}A}^8FGer+wq$nV4ky zYreiT3Q-(-FbeA^?;e)dGy&usDMw(iES$eC2R%Ch(6AQm+Ow~9d%h2AV*{p_}D{b{Hw#~;j`e81b{_5Nai7zPmnxwQXT9nYx}%^P`F zHTC&O4&cn+%L%M4Z*$(BQf_)w|P@GZmXUPtFAoouTH?byS%5=^?S(AHMXK z&FAf#XDG$PPo4-ovxpB8f40;+xanUV!F%bSe|oRGh<4YC@dz4Bh4(h}HbjTt8Me4T zGp`60McPY_vWWux-AzjF%0VZ!qoCvU+%f7@*YsFcxz0~nQK<(h%*&lMySfND&pahQm<$7lNYna$3xn$3 zIE%^@Tx1xk)0x@(oZ%2gWcPv1jdB|q!gtZnOM_Xel(tGh_sy4TP}Qeo2ULmiK<73vM08RFIDbGPJWHqn9JALuDP3shz#tc3D%=h1)`Ff$aIUeabJE*LO2X%M4)jD8|8m088?k>4Iy1Q@BA8ID= zC7Kv(o-MvtMqvO*O&YvupCYK99PHVTbh)qFkR2~fw??-|sCU@SFEN0~D!_r=9g&C> z9WPO>JmAqC-)OQ{_oJi_XPiE!m%#G4Memm&ktqcRDCun4Ag}qYxJlgwD?cru%5H8~dnHv&;?JpH)+kEDDw#*R(Rmo!EwqO{b^sJGub)6M6&6Mh@CUXlqTuVCvAOY%Zj?%Ih%B;^-{1no^bI{nXca$qv$ zOWF=vsCv6S?j%)u!>mO0@V+X?M7Nz~_lw(A6wp<;T-xnYLA(AnbiRlv-di^75H0L9 zlOk}terb9}YwL{61wnBxkYdpUr8JZUSHU7{3(OJin4lLYtiYYlE$WOA+n73-==Im@ z*6UgxR6VOw3vNgKLD3xv+r2+0Ja?>cuKj5snEiOJD(ZP5$BTp zEjQN{%Z3gWu6i3GtOAeEwSh@n8*$x+YpY_|-U6t5oD98gwr%&Zi?o+mdi-KG`c75I zeNHJEi`i_(P0?$K?Q9u%EN^~_NmCGhrB#qr=(yP3bI6!HBQFooTDA zI@c@k;xQyfXYqY~>n^*wHaA$x%(dcCXtf0=tX6Af&`H3vNu|2Rf9+tMR4x6au{o1y zUvU)h^M;G8<1kz~{Y7g<%7RHKaCLjJ_{_q6Egrd6H)5j8At{rED5iq(B3elaY58Z# z9HndV?O3%$+;yXNRT}W{sRymdi8$hR7<-0?o=zJ)K9gDprMp|{c~rysN=kLY$^7p7 z=xp=*RUcYli$e0j`GeDHB&dPnuB@t=~&q^PDRGThuge)m$7 zsHbUFQa%1C2HI`*@S70^^X!vVTE1eZt75Xiq@>&X@?mXP4p$rUq@y>7RyR0VEUFX( zZEH72DgqvN&TS0!l^Ulbrr%SOe|E$9X}Ws%BF@f@TxGR-;<$3+$)X-JjOkDHX7U&_ z%m91~V%>{@3N>j`#6)4y9 zb=f1*?mwh?)#Cx8Ic!pO9@$3-0dFC z?pPSUz%uo<64-^*5zFqIM$1YlJXAQD4uNzI5 zqG1Z>d$3}YN{twX-BU^g!JQs1=ACs3nXRhb^}TLU_AB?ED7JWy2ySkuE_v5M9GktP zH>yP{nV^Rm-VIgKx#nfgNeifY@_w)DPSXX)Ijwd46TG4Bu>ZSvXuL3rB271^ zpo(lRqgfY*-a?t=$DC}SA4QgU760B1*f&1BgtW~Xy>rUhE-@SQ*IJFfhtb&XEW2I! zbOQlcxH7p)#)!bvJfykb`OTtAS=aOvfN_|a$P{H8VFX|6s;UeFw~RYaPc;m7z*r}X z&PY)*7K&9_Tjo9+W_@v>=Y)+Y<;v*zhwcaOlD&7RTx@30(G7S{l&Wj?bk6*P=hc86 zu8ODhGaHW7k_HeFdZJ<6$!+eY@bZ*5(ur!HO+qWS=;t}}Wp|`G+?boZK-~%G0N34} zHQ4V>`SX**vMQ3g64Vu| zYT&DKVOfTqlkFy=T=+p$W%>yyO0_bXFql6b^HM7*E+Y_on?Um{?#`tdXOv#A7f z9TR_S&KGFPfgIjcH13GwDSL}8nNyY90XrfapOE|pDUzI95efkoa~Te+;H|l8+A7CX znfxsTI}^f0KRXmQ+|_siLsN2+&Zx;ov-Ar^!d}VEA zhYx_+9vv`VM=4qID3YTU_+}I7wmU0$!hPjLg@DONfm7w0cZL|lY}lQ$ca93JycI9s zh#CBLRFoJ9K}`zfg1~H+V@BtCXg{hY1K(N35dGIcPl&w2Atrb*oa=pS=Wdp&eE^_ z^$!02I!b4Ft+NT5n|LnRY&1v2R;qS3j91c^ zewbt+*!PriX9vRQ3Jr0L-{zdwZRrjjNAaooD7UtnCe3)f@!ZVff@&d3@@*s_BO~+e z_6IJ&4dg|}K$kSP=TZO|%|_LXbd6@sev!+3C`=k0b8sdffpZ6I*18Y)=B{`%$D^m_ zcYBmBuFREzeGiEAPKCJI;Wm3%%VpuDcWbxjgba(d1_x+W%3`47-oT^`%yUSVnwXy; z^uW}L0iq78zd6(g2U3lhgq(zmccrJkwN(HJNpWZ?Joc#t4C&=i<##At5>(^P&K4-P z3*vqf6O`nFioB^^nE(k4>buKhp_s5Vh~uzBY2lx41c78B?frs9dyAIcPS`P(tXEl3 zm_jGJ>A@sBEkhf$cwWXOvA1+nyTE8#m1UB z!iGY~B-LN6LC^r)65r4B11WGmNcvptAIc9L_5(}hMwzrO^fy>!f!o9Tn!rDp!3Q7yI;v1yGhcKBr4?kSq zAI*WO2$jA;500urh;etv@FlnWW=y;jS4fV^+Bc@f!0cjCG8gBa$7NkPT@8nv(K#~S z2bWr2YjG@Zte}HOLi=*N-ZZf=!g)^+N<+n1dZrrvFd44}K zi-{ex;?uuYeThlSig8D8XMAAB-*@S@>oHQE@KchNWo>R#cBET?+im`x+A(Ko%%dv9 zCGnV4fxv)J?C>-hp@rg2yCee(9BZ%HU2yT=dn?(5AJiUPbd%4cuRF6_bS9@V!$Yq8 zimHQy<8Fvz^9?T3x^6IAlVgk7T@LFZz8U?soV6&(YRw$_%$WvoPU!+C z&ZmILePU`fUDMl7{*mb?S;ihc6B@;Ip9HyRwzf((33IIGKOKRAFAFs$Cp8<*OTCNy zT_m^rD>qR}Dyb(IgRa+#$8;p*h_nOs$}9&^{k3&ZCe_{6(}OhlNK#_&n9#4BBEX+F zlOO-ta~_6S^mE@2e(8`thD059ud5(NYvyY<4rx?Qo3PXb;L+shBuQ>J?OE9=H}GHK zHE4hEQMOEuqPd#k`SQMj@cpoS&EK!QbEfUDjzyg`>rZlZ&n_}?pFOK6v5KP6-R*mR zD@|hBysyx$P-DP86+V#(E15$z*{Q|;_3q4K*w7!Z`hOS(3uWpy7rMS#uoAlAD1Zur zVhIPHYMpWGC^^3+xAers=t35=U=bG3bbUoeo)8^HrRJBNc9exNoA!s~Fc)vx4X2!d z?2D6)np_zlUA{WFY^xh0yE4K7iTibJ9$D^l{zu4K z0%nC8Fva&PFto zydkpkhC?|gnXYKl_TDhVrYM$a*jYxfD^tGtRaR$ON7j+b!+X}5DjC0fd*SS=n0IL| zweKDh?Kau_Z~!=%UdBbHk!xW+zIF3V)YHb@J9x>`)0`Rjg!#;y;S56YX;9PMSx8+9 zjtFLxW5O&9?|C~3ufMzA5EYUPo_5FgtjOTY?%%s^YzBSicDSswlcD2AdS*WAzEnOY zg!o)tg=sADh8Mj7b2|W+%++D+F=x5ev57D zH@kQG{&s%qv{m`Xw&B9y zdKuK8LsMm*Kf7NzKO_agr6O45luyozfKq7FMwRQtZC=^jhiN@n_m$OoIHL$7I2kyF zoKNnW9z@7Xt%TP6hX-n4%L~T9>VlX{oEOC#=NFYrKBP6&b;BaJ%R)K z7E2YZz9ZYYHy7*H@y|Z_1vE?~!0ClFXhnJ(#5h8cBAdP{JmWD`ale~I^4iUxC%zt0 zC@e`_LT7X3IL21y2rjxmsXVZKN`wH0_36;AH0!Cwj(D@k0;j2>zHhn#j84-u3mQd| zm*`tN5RV-ZH?|vgmYq6oUmP{!^MXU^w)9clUON0Qge~aiSNEBM3DeD;F!RN3sc9)q zD`f7QM%Kmd?tOc&CI)Xv3N}2y%Q4q^%$rotf+SNC*n_x>q~_b1`r}1geE07?RS-_e z`PDqc6qar|$*IQDj@qXt=|T?YK5|^}ns2zRI7O%G1UjGIbT2!1T6lFR6<2+?Xn-W; z1ljHLWUR*EkQ6fjZ@{=~5Xbp(npQclU1;NAO;y*0U9GY(XXM9oy^E==L?A&jk_C}5 zF@d^e!AgN+txwZCYfrVsS;zbQwwq13?#&bfp0JPaXXQG#$EFhQKR>!Jl8DsD;idWv z3Xj6w<6IfepT(vr)tR~L2PP#ItCe+bTfcqu$ZmiBLjUF~n-<{)kIBZS)Zi)@jFNhS zditJenNb5!(>fmB2=dk9KKa3Mc3(q6@*cTWc=_Sgc>6GG9Ve2tJ1wVDWddR<1~0jF zj50);g`EKexA97+91_|M?k(!*K!tR8Lxgr+xIPQEy+DCk{%2CfV@4ej+;@H(>f`LG zzYFfN)YkT;ERYGO0M4%IG5H8;7TMGR6OO800~~QuRne>FBP$QD*LyZM_t%I9%>gQBHADsT-7iOt4wB?Ehg%$N~$S6T5 z}AL&cg1NN^joPFp#}S|6d8PGL^?TQVEmy5laOtZVmc5TnlBj(97cWL*iOQaQ1pZ?q0^6PaA*;_B! zcf@vT2WIHW3Im^K-zH$n-KNuzjE*iaqcBwm88DD%}@#Sss&k6p-X&%EQ6|w`1`KrXm*H-1?Xdq+aZVKwbc7J!=XFx zC~==jcI`$*PEURQoZHglKx+XTC>_!4?uTASVwCn&xt^@)2@m1E%2!@+G6~STJ;4a= zeuWs=vUew9z`6|ScKMQPyb3ObV#~4W61}H)9exPH=YHC&uYCtjH=I=1VO$5g;%JE% z-t3BJLmF}I753q7%Sk4bMz*9K5=Upa?Ez`W$VccxcC4r?23ah}GO)RQ#KH+eYjEJ# zI2colq-vT;FUzw?b&w6F*(Kv#fwX79&|4w{Wg(TCljNxL1?3e*8>U)d^Z4O85_*OO ziAch7YD~DZ`$zl!&9p#kY5JcQ`Bh<<=U*;5l%gvbowPLFv;z&6e}a?jy0vV%pjD>$ z$_9rV{@i;XAUP!why(*nG+CWVKbuZo*#&3t-kvI;?TGGUB~2px&MU!kyzrHg-Zi{2 zdg7xcWaPGwgs7zd(LZN(O$#)jlI5oX-@7Z1_}8Ojtt(Y9c1yi(bMcP{*9L1^vSe6$ zI80dz56Fgy$q@hB0^Aof!YQhR?#FU7zXLoY$lFgPKllZ})xmGy*&t5q^MotMi<^aT zXd{3kRV_NwZIYGwdR^@<1gAUW+$X0S{Qk==b(N++Od2B!%W-{x!wZI7Go5ptN`iNZ z-f6_|aDpm)FJMVrdMwt0J)4;5)@SaEOwY7c&qCB78N(c2mltm(jxIBb;iL z{zA!<9jqqAObdK&4Oi)Oa?v{8SI+PUc`y|@;hc4LV(Hwr^Z2-T)In>mrN~xwSHzR5Ks(EPDf!)3Hdo=6FN8f#yku&B>RnT$@zt?$=gg(wID$r8%Xj_qB1Q}agpNu;$W!Qeu$U*!{rAPSB9 zBYG&e3r61wiwh-qVPxeMwK5OBecU-6z)J3@AZ#zRzGY*tD|jjT`H}exyNhYlj}wV? zKVpgQOp`(ZtZ7<~b~F4>s$4ca7-MhY`EoMK%D~SJMUq}eFm5-4G9r-xcIcFUz;)g^ z+YH!(ss2o5s)?feb6DA*5J6}VXA;4k)V8l=WM=I8(GWW`{&Wcq<_tENNqq;ZsZR7k zR_Uh7R*bgOrU%SczLX#WZ@uH`Nkb@StWy7W&JeTox&_fN$L)=B;EBJ89C947Y<41_ zAT2Fca%iT1gD?LOwsHbXhe*>}&+uMwma;l|v;8HBZ`T=F=8|%MX{nw+Mqw_BOULz<6Qir*jHXr4wCci>exufk>!uTs-jyMAM!Yt}ibI z@d@*aO@C(Sias?l>>jL4Nhs7|(m3q8HR|m|$|_nXg?etc&^YpF#fp-0VAP4Pn+hS~Q{LGxWc1PXy896E@^7$G8IKmAh-(ucBl1GlIyvJpH-~y+tNjYC+FPOE z6&~~TZu2N@RM|MqtK@a;)iMh479O z6U!S!C2@_5)9Iku## zucfe*m=w70FFGwmOJSL~Fc`I_-jSxj%&Kbb8d6M=VO(guV^gaOf7>|;l?*g^ZX6FC zP9}aZ?x+ak16oq%Z)xOC#2$qR5NQ11~mhuxP$ord@&Nlb20Z_ZWApg`+l;a{pby1Qw({<=lUYOTKUcW23Xo$XZ5 zi)aC-XQ}>|yJi#bKMbigwePu+q^MsZY!kmH6~;bc>@mg;iP`?H>_5{e;q82J`OrTdb~dCcn}yO*i$DTwPw}wmNp*vhZ8}x8T#xIxBg% zeMFhEw$|=rSnKL0`+q_Hb06$D);1UZnmp3l@;b-QMup1@{of?|yK_RnKUW^4%R=WX zC^_zKHyGZ%Uv_t-wEme~$FE6WqrS#*7Y!uT`qG6IFH3U79|#Rz6W=Fo4o3(&WQTHVr@)}<%u{#lkAb<0N@%8vNsFk|89^5Q373|#uiP8a zH};p`xN8Z#C%Y~h=BaV_gE(yRzQf(~#}A{c-A5KCvp^0#*CM#o20C;+NNmsU-a+dg zx=GRfR8rFC;XZWq zq%z1d3t!YwBDSC#4Y%|8^8EzT?q+{9HKT}&8qgF@_HXU%^hJ}k1z2xlDqTL3k}p0O zWmE7hK2*F9l23fOqYbac(15uUsMczp6DAshKv=H~dVROu1^zO=$;J?y^QPOoyYypp z0LZvf1d2PDhxU81d{I$8BqU@q*Doh0XQ7;Xb6eYsiuNBY#lUG$180Bq_xIN*FpQ_; znvYEg4i28U)dTv!;A_{e0XZU`*QBeb@YsnH%{Hq_IXc1!C*c-9P(tFKG_|zYpI3%? zer&^WjdgW(FXYjAx?*oRD7<_qsH^Q@j9>;xZ?XDKq4pg#G&Bs9nyGTJ2-CrKao|44 zLmRVYwpSIClhYl*$VP zz#4_d$)J+ym64N{{(UF=N2Tj7LeJ~30wH2wVY+<$+nVQyl>P5LR;L5Wpzk$DzK?D}Qoo;|ZXZwfQ~WDQ!P+JO_YPROZ(E2`xs zz!kf@i~e45q!bpXfZN{dn6(6up-o*<2AL?depp$uV zNls1nHgqevquZqNrGOg z$b^QDE|j$gtCV{3J2GZ19$YKgsC%Qg(g5ye)AOsQh8bQLuoG74C?1SLi=h0q!-o&2 z!B95<1Vj(6Q`2+bJB~|8NU&XAlQ^x%Rg(PGJ*2Hn}^5S1y;^*i0euy9~j$tXP4?qhM ze{x*h6}y?XhsZF=NVokxNAK?{pw$1&B$^GF5R-;L7Top;N>8kEx~{{RI?T$1NBIw>1^b8~aI z(y%I77a!76XdR`@z#H4ypP`V~5_|fzU)I)k@Xe(UjC2(H zo_|3fD+9{u5k;{*BxJkBN?9xXswyyc>x*k^{T z=Nq(6ZJQ2NxmN+mf&m8zhX(#MkacrSz$ZZ0TW$C1)_G+>@`P|{suT@a`EyO~Ulm_V*=~fX@kPw*EBn� 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 { + PeladeiroModel usuario2 = new PeladeiroModel(); + usuario2.setEmail(email); + usuario2.setNome(name); + usuario2.setApelido(name); + usuario2.setDescricao("Usuário criado via OAuth2"); + usuario2.setWhatsapp("000000000"); + usuario2.setPeDominante(PeDominante.DESTRO); // escolha padrão + usuario2.setAuthProvider(provider); + usuario2.setFotoUrl(picture); + usuario2.setHistoricoPeladeiro( + historicoPeladeiroMapper.toModel(historicoPeladeiroDTO)); + peladeiroRepository.save(usuario2); + log.info("Usuário salvo no banco com ID {}", usuario2.getId()); + }) + .exceptionally( + ex -> { + log.error("Erro ao criar histórico do peladeiro", ex); + return null; + }); } else { log.info("Usuario encontrado! ID: {}", usuario.getId()); } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/FutController.java b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/FutController.java index 59cf606..4cc135f 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/controllers/FutController.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/controllers/FutController.java @@ -2,7 +2,6 @@ import br.com.vemprofut.controllers.request.*; import br.com.vemprofut.controllers.response.*; -import br.com.vemprofut.models.FutModel; import br.com.vemprofut.services.IFutService; import io.swagger.v3.oas.annotations.Operation; import jakarta.validation.Valid; @@ -38,27 +37,26 @@ public CompletableFuture> create( @Operation( summary = "Busca um Fut pelo ID", tags = {"FutController - CRUD Básico"}) - public ResponseEntity findByFut(@PathVariable final Long id) { - return ResponseEntity.ok(futService.findById(id)); + public CompletableFuture> findByFut( + @PathVariable final Long id) { + return futService.findById(id).thenApply(v -> ResponseEntity.ok().build()); } @PutMapping("{id}") @Operation( summary = "Altera um Fut já cadastrado, informando o id", tags = {"FutController - CRUD Básico"}) - public ResponseEntity update( + public CompletableFuture> update( @PathVariable final Long id, UpdateFutRequestDTO dto) { - var obj = futService.update(id, dto); - return ResponseEntity.ok(obj); + return futService.update(id, dto).thenApply(obj -> ResponseEntity.ok(obj)); } @DeleteMapping("{id}") @Operation( summary = "Apaga um Fut por meio do id, cuidado! ", tags = {"FutController - CRUD Básico"}) - public ResponseEntity delete(@PathVariable final Long id) { - futService.delete(id); - return ResponseEntity.noContent().build(); + public CompletableFuture> delete(@PathVariable final Long id) { + return futService.delete(id).thenApply(v -> ResponseEntity.noContent().build()); } // =================== acoes partidas ======================= @@ -67,20 +65,26 @@ public ResponseEntity delete(@PathVariable final Long id) { @Operation( summary = "Criar uma nova partida...", tags = {"FutController - Ações Partidas"}) - public ResponseEntity criarPartida( + public CompletableFuture> criarPartida( @RequestBody SavePartidaRequestDTO requestDTO) { - FutModel futModel = futService.findByIdModel(requestDTO.futId()); - return ResponseEntity.status(HttpStatus.CREATED) - .body(futService.criarPartida(requestDTO, futModel)); + return futService + .findByIdModel(requestDTO.futId()) + .thenCompose( + futModel -> + futService + .criarPartida(requestDTO, futModel) + .thenApply(obj -> ResponseEntity.status(HttpStatus.CREATED).body(obj))); } @PostMapping("partidaslist") @Operation( summary = "Cria varias partidas de uma só vez, todas com resultados preenchidos", tags = {"FutController - Ações Partidas"}) - public ResponseEntity> criarPartidasLista( + public CompletableFuture>> criarPartidasLista( @RequestBody List requestDTO) { - return ResponseEntity.status(HttpStatus.CREATED).body(futService.criarPartidasList(requestDTO)); + return futService + .criarPartidasList(requestDTO) + .thenApply(obj -> ResponseEntity.status(HttpStatus.CREATED).body(obj)); } // ================ lista peladeiro ===================== @@ -89,19 +93,22 @@ public ResponseEntity> criarPartidasLista( @Operation( summary = "Adiciona um peladeiro a lista de peladeiros cadastrado no fut", tags = {"FutController - Lista de Peladeiros"}) - public ResponseEntity adicionarPeladeiroLista( + public CompletableFuture> adicionarPeladeiroLista( @RequestBody AddPeladeiroInFutListRequestDTO addPeladeiroRequestDTO) { - futService.addPeladeiro(addPeladeiroRequestDTO); - return ResponseEntity.noContent().build(); + return futService + .addPeladeiro(addPeladeiroRequestDTO) + .thenApply(v -> ResponseEntity.noContent().build()); } @GetMapping("lista-peladeiros/{idFut}") @Operation( summary = "busca a lista de todos peldadeiros cadastrados no fut", tags = {"FutController - Lista de Peladeiros"}) - public ResponseEntity> listarPeladeirosCadastrados( + public CompletableFuture>> listarPeladeirosCadastrados( @PathVariable final Long idFut) { - return ResponseEntity.ok().body(futService.listarPeladeiroCadastradosFut(idFut)); + return futService + .listarPeladeiroCadastradosFut(idFut) + .thenApply(obj -> ResponseEntity.ok().body(obj)); } // =============== lista Editores =========================== @@ -110,20 +117,20 @@ public ResponseEntity> listarPeladeirosCadastrados( @Operation( summary = "Adiciona um Editor a lista de editores de um fut em especifico", tags = {"FutController - Lista de Editores do Fut"}) - public ResponseEntity adicionarEditorLista( + public CompletableFuture> adicionarEditorLista( @RequestBody AddEditorInFutListResquestDTO editor) { - futService.addEditor(editor); - return ResponseEntity.noContent().build(); + return futService.addEditor(editor).thenApply(v -> ResponseEntity.noContent().build()); } @GetMapping("lista-editores/{idFut}") @Operation( summary = "busca a lista de editores de um fut em especifico", tags = {"FutController - Lista de Editores do Fut"}) - public ResponseEntity> listarEditoresFut( + public CompletableFuture>> listarEditoresFut( @PathVariable final Long idFut) { - - return ResponseEntity.ok().body(futService.listarEditoresCadastradosFut(idFut)); // TODO:testar + return futService + .listarEditoresCadastradosFut(idFut) + .thenApply(obj -> ResponseEntity.ok().body(obj)); } // ================= upload arquivos fotos ================== @@ -132,10 +139,11 @@ public ResponseEntity> listarEditoresFut( @Operation( summary = "Para enviar a foto de capa do Fut", tags = {"FutController - Upload de Imagens"}) - public ResponseEntity uploadFotoFut( + public CompletableFuture> uploadFotoFut( @PathVariable Long id, @RequestPart("file") MultipartFile file) { - futService.atualizarFotoCapa(id, file); - return ResponseEntity.ok("Foto de capa Salva!"); + return futService + .atualizarFotoCapa(id, file) + .thenApply(obj -> ResponseEntity.ok("Foto de capa Salva!")); } // ======================== Banimentos ======================= @@ -144,27 +152,28 @@ public ResponseEntity uploadFotoFut( @Operation( summary = "Adicionando um peladeiro da lista para o banimento", tags = {"FutController - Banimento no Fut"}) - public ResponseEntity adicionarBanimento(@RequestBody SaveBanimentoRequestDTO dto) { - futService.addBanimentoList(dto); - return ResponseEntity.noContent().build(); + public CompletableFuture> adicionarBanimento( + @RequestBody SaveBanimentoRequestDTO dto) { + return futService.addBanimentoList(dto).thenApply(v -> ResponseEntity.noContent().build()); } @GetMapping("lista-banidos/{idFut}") @Operation( summary = "Busca a lista de Banidos do Fut em questao", tags = {"FutController - Banimento no Fut"}) - public ResponseEntity> buscarListaBanidos( + public CompletableFuture>> buscarListaBanidos( @PathVariable Long idFut) { - return ResponseEntity.ok().body(futService.findAllBanidos(idFut)); + return futService.findAllBanidos(idFut).thenApply(obj -> ResponseEntity.ok().body(obj)); } @DeleteMapping("delete-banimento/{idFut}/{idPeladeiro}") @Operation( summary = "retira um peladeiro da lista de banidos", tags = {"FutController - Banimento no Fut"}) - public ResponseEntity retirandoBanimento( + public CompletableFuture> retirandoBanimento( @PathVariable Long idFut, @PathVariable Long idPeladeiro) { - futService.removeBanido(idPeladeiro, idFut); - return ResponseEntity.noContent().build(); + return futService + .removeBanido(idPeladeiro, idFut) + .thenApply(v -> ResponseEntity.noContent().build()); } } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/mappers/IMappersDefault.java b/VemProFutApi/src/main/java/br/com/vemprofut/mappers/IMappersDefault.java index b848a69..c0e268b 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/mappers/IMappersDefault.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/mappers/IMappersDefault.java @@ -87,7 +87,7 @@ default Long mapIdGols(EditorModel editor) { // Converte ID tipo Long para EditorModel default EditorModel mapEditor(Long id) { IEditorService service = new EditorService(); - return service.findByIdModel(id); + return service.findByIdModel(id).join(); } // ======================FUT====================== @@ -100,7 +100,7 @@ default Long mapIdFut(FutModel fut) { // Converte ID tipo Long para FutModel default FutModel mapFut(Long id) { IFutService service = new FutService(); - return service.findByIdModel(id); + return service.findByIdModel(id).join(); } // ===========================GOLS================= @@ -139,7 +139,7 @@ default Long mapIdHistoricoPeladeiro(HistoricoPeladeiroModel historico) { // Converte ID tipo Long para HistoricoPeladeiroModel default HistoricoPeladeiroModel mapHistoricoPeladeiro(Long id) { IHistoricoPeladeiroService service = new HistoricoPeladeiroService(); - return service.findByIdModel(id); + return service.findByIdModel(id).join(); } // ============================PARTIDAS=========================== @@ -152,7 +152,7 @@ default Long mapIdPartidas(PartidasModel partida) { // Converte ID tipo Long para PartidasModel default PartidasModel mapPartidas(Long id) { IPartidasService service = new PartidasService(); - return service.findByIdModel(id); + return service.findByIdModel(id).join(); } // =========================PELADEIRO========================= @@ -170,7 +170,7 @@ default Long mapIdPartidas(PeladeiroModel peladeiro) { // Converte ID tipo Long para PeladeiroModel default PeladeiroModel mapPeladeiro(Long id) { IPeladeiroService service = new PeladeiroService(); - return service.findByIdModel(id); + return service.findByIdModel(id).join(); } default PeladeiroResponseDTO mapModelToResponse(PeladeiroModel model) { diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/IHistoricoPeladeiroService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/IHistoricoPeladeiroService.java index 10150b8..3c0dd98 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/IHistoricoPeladeiroService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/IHistoricoPeladeiroService.java @@ -2,16 +2,17 @@ import br.com.vemprofut.models.DTOs.HistoricoPeladeiroDTO; import br.com.vemprofut.models.HistoricoPeladeiroModel; +import java.util.concurrent.CompletableFuture; public interface IHistoricoPeladeiroService { - HistoricoPeladeiroDTO create(); + CompletableFuture create(); - HistoricoPeladeiroDTO findById(Long id); + CompletableFuture findById(Long id); - HistoricoPeladeiroModel findByIdModel(Long id); + CompletableFuture findByIdModel(Long id); - HistoricoPeladeiroDTO update(Long id, HistoricoPeladeiroDTO dto); + CompletableFuture update(Long id, HistoricoPeladeiroDTO dto); - void delete(Long id); + CompletableFuture delete(Long id); } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/FutService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/FutService.java index 80272b3..54aea4b 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/FutService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/FutService.java @@ -15,10 +15,8 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -58,53 +56,54 @@ public class FutService implements IFutService { @Autowired private IBanimentoService banidoService; - @Autowired - @Qualifier("partidasExecutor") - private Executor executor; - // ======================== CRUD basico ========================== @Override @Transactional + @Async("defaultExecutor") public CompletableFuture create(SaveFutRequestDTO dto) { queryService.verifyNomeFutExist(dto.nome()); - var peladeiro = peladeiroQueryService.verifyPeladeiroExist(dto.administradorPeladeiro()); - - FutModel saved = repository.save(mapper.saveRequestToModel(dto)); - - HistoricoFutModel historico = historicoFutService.create(); - saved.setHistoricoFutId(historico); - saved.setAdministradorPeladeiro(peladeiro); - SaveFutResponseDTO response = mapper.toSaveResponse(repository.save(saved)); - return CompletableFuture.completedFuture(response); + return peladeiroQueryService + .verifyPeladeiroExist(dto.administradorPeladeiro()) + .thenApply( + peladeiroModel -> { + FutModel saved = repository.save(mapper.saveRequestToModel(dto)); + + HistoricoFutModel historico = historicoFutService.create(); + saved.setHistoricoFutId(historico); + saved.setAdministradorPeladeiro(peladeiroModel); + return mapper.toSaveResponse(repository.save(saved)); + }); } @Override @Transactional(readOnly = true) - @Async + @Async("defaultExecutor") public CompletableFuture findById(Long id) { - var futModel = queryService.verifyFutExistRetorn(id); - FutDetailsResponse response = mapper.modelToDetailsResponse(futModel); - return CompletableFuture.completedFuture(response); + return queryService + .verifyFutExistRetorn(id) + .thenApply(futModel -> mapper.modelToDetailsResponse(futModel)); } @Override @Transactional(readOnly = true) - @Async + @Async("defaultExecutor") public CompletableFuture findByIdModel(Long id) { - return CompletableFuture.completedFuture(queryService.verifyFutExistRetorn(id)); + return queryService.verifyFutExistRetorn(id); } @Override @Transactional(readOnly = true) + @Async("defaultExecutor") public CompletableFuture findByNome(String nome) { - var futModel = queryService.verifyNomeFutExistRetorn(nome); - return CompletableFuture.completedFuture(mapper.toDTO(futModel)); + return queryService + .verifyNomeFutExistRetorn(nome) + .thenApply(futModel -> mapper.toDTO(futModel)); } @Override @Transactional(readOnly = true) - @Async + @Async("defaultExecutor") public CompletableFuture> findAll() { return CompletableFuture.completedFuture( repository.findAll().stream().map(mapper::toDTO).toList()); @@ -112,19 +111,23 @@ public CompletableFuture> findAll() { @Override @Transactional + @Async("defaultExecutor") public CompletableFuture update(Long id, UpdateFutRequestDTO dto) { - var retorno = queryService.verifyFutExistRetorn(id); - - retorno.setJogadoresPorTime(dto.jogadoresPorTime()); - retorno.setTempoMaxPartida(dto.tempoMaxPartida()); - retorno.setMaxGolsVitoria(dto.maxGolsVitoria()); - - return CompletableFuture.completedFuture( - mapper.modelToUpdateResponse(repository.save(retorno))); + return queryService + .verifyFutExistRetorn(id) + .thenApply( + futModel -> { + futModel.setJogadoresPorTime(dto.jogadoresPorTime()); + futModel.setTempoMaxPartida(dto.tempoMaxPartida()); + futModel.setMaxGolsVitoria(dto.maxGolsVitoria()); + + return mapper.modelToUpdateResponse(repository.save(futModel)); + }); } @Override @Transactional + @Async("defaultExecutor") public CompletableFuture delete(Long id) { queryService.verifyFutExistRetorn(id); repository.deleteById(id); @@ -133,204 +136,271 @@ public CompletableFuture delete(Long id) { // ======================== acoes partidas ========================== - /*@Override + @Override @Transactional - public CompletableFuture criarPartida(SavePartidaRequestDTO requestDTO, FutModel futModel) { - return CompletableFuture.completedFuture(partidasService.create(requestDTO, futModel)); + @Async("defaultExecutor") + public CompletableFuture criarPartida( + SavePartidaRequestDTO requestDTO, FutModel futModel) { + return partidasService.create(requestDTO, futModel); } @Override - public CompletableFuture> criarPartidasList(List requestDTOS) { + @Async("defaultExecutor") + public CompletableFuture> criarPartidasList( + List requestDTOS) { // dispara cada DTO em paralelo - List> futures = requestDTOS.stream() - .map(dto -> CompletableFuture.supplyAsync(() -> criarPartida(dto), executor)) - .toList(); //TODO: --------------------------------------------- - - // espera todos terminarem e junta os resultados - - for (SavePartidaRequestDTO dto : requestDTOS) { - // 1 - criando partida - log.info("criando Partida..."); - PartidasModel partida = new PartidasModel(); - FutModel futModel = queryService.verifyFutExistRetorn(dto.futId()); - partida.setReservas(dto.reservas()); - partida.setFutId(futModel); - - // salvando partida para pegar o id. - partidasRepository.save(partida); - log.info("Partida criada, adicionando Cartao..."); - - // 2 - criando cartoes - if (dto.cartoes() != null) { - for (CartoesRequestDTO c : dto.cartoes()) { - CartoesModel cartoesModel = new CartoesModel(); - cartoesModel.setFut(queryService.verifyFutExistRetorn(dto.futId())); - cartoesModel.setTipoCartao(c.tipoCartao()); - cartoesModel.setPeladeiro(peladeiroQueryService.verifyPeladeiroExist(c.peladeiroId())); - cartoesModel.setPartida(partida); - - cartoesRepository.save(cartoesModel); - partida.getCartoes().add(cartoesModel); // pegar a lista para adicionar nela. - log.info("Cartao adicionado, Adicionando Gol "); - } - } - - // 3 - criando Gols - if (dto.gols() != null) { - for (GolsPartidaRequestDTO g : dto.gols()) { - GolsPartidaModel golsPartida = new GolsPartidaModel(); - golsPartida.setPeladeiro(peladeiroQueryService.verifyPeladeiroExist(g.peladeiro())); - golsPartida.setPartida(partida); - - golsRepository.save(golsPartida); - partida.getGolsPartida().add(golsPartida); - log.info("Gol adicionado, Adicionando Peladeiro"); - } - } - - // 4 - criando peladeiro - if (dto.peladeiros() != null) { - for (PeladeiroRequestDTO p : dto.peladeiros()) { - PeladeiroModel peladeiroModel = peladeiroQueryService.verifyPeladeiroExist(p.id()); - - // Para sincronizar a tabela intermédiaria da relacao @ManyToMany. - partida.getPeladeiros().add(peladeiroModel); - peladeiroModel - .getPartidas() - .add(partida); // Esse adiciona pa tabela: "esta_peladeiro_partidas". - - // para garantir que vai ser salvo. - peladeiroRepository.save( - peladeiroModel); // Esse salva em PeladeiroModel (que é onde a table intermediaria foi - // criada) - - log.info("Peladeiro adicionado"); - } - } - partidaList.add(partida); - } - return CompletableFuture.completedFuture(partidasMapper.toResponseList(partidaList)); - } */ + List> futeres = + requestDTOS.stream() + .map(this::criarPartidasAsync) // cada DTO vira um future de PartidasModel + .toList(); + + return CompletableFuture.allOf(futeres.toArray(new CompletableFuture[0])) + .thenApply( + v -> + futeres.stream() + .map(CompletableFuture::join) // pega cada PartidasModel + .map(partidasMapper::toResponse) // transforma em DTO + .toList()); + } + + private CompletableFuture criarPartidasAsync(SavePartidaRequestDTO dto) { + PartidasModel partida = new PartidasModel(); + + return queryService + .verifyFutExistRetorn(dto.futId()) // <-- precisa retornar aqui + .thenCompose( + futModel -> { + partida.setReservas(dto.reservas()); + partida.setFutId(futModel); + partidasRepository.save(partida); + + // Cartões async + CompletableFuture cartoesFuture = + CompletableFuture.allOf( + dto.cartoes() == null + ? new CompletableFuture[0] + : dto.cartoes().stream() + .map( + c -> + peladeiroQueryService + .verifyPeladeiroExist(c.peladeiroId()) + .thenAccept( + peladeiroModel -> { + CartoesModel cartoesModel = new CartoesModel(); + cartoesModel.setFut(futModel); + cartoesModel.setTipoCartao(c.tipoCartao()); + cartoesModel.setPeladeiro(peladeiroModel); + cartoesModel.setPartida(partida); + cartoesRepository.save(cartoesModel); + partida.getCartoes().add(cartoesModel); + })) + .toArray(CompletableFuture[]::new)); + + // Gols async + CompletableFuture golsFuture = + CompletableFuture.allOf( + dto.gols() == null + ? new CompletableFuture[0] + : dto.gols().stream() + .map( + g -> + peladeiroQueryService + .verifyPeladeiroExist(g.peladeiro()) + .thenAccept( + peladeiroModel -> { + GolsPartidaModel golsPartidaModel = + new GolsPartidaModel(); + golsPartidaModel.setPeladeiro(peladeiroModel); + golsPartidaModel.setPartida(partida); + golsRepository.save(golsPartidaModel); + partida.getGolsPartida().add(golsPartidaModel); + })) + .toArray(CompletableFuture[]::new)); + + // Peladeiros async + CompletableFuture peladeirosFuture = + CompletableFuture.allOf( + dto.peladeiros() == null + ? new CompletableFuture[0] + : dto.peladeiros().stream() + .map( + p -> + peladeiroQueryService + .verifyPeladeiroExist(p.id()) + .thenAccept( + peladeiroModel -> { + peladeiroModel.addPartida(partida); + peladeiroRepository.save(peladeiroModel); + })) + .toArray(CompletableFuture[]::new)); + + // Espera todos os subtarefas terminarem + return CompletableFuture.allOf(cartoesFuture, golsFuture, peladeirosFuture) + .thenApply(v -> partida); + }); + } // ======================== lista peladeiro ======================== @Override @Transactional + @Async("defaultExecutor") public CompletableFuture addPeladeiro(AddPeladeiroInFutListRequestDTO requestDTO) { - PeladeiroModel peladeiroModel = - peladeiroQueryService.verifyPeladeiroExist(requestDTO.peladeiroId()); - FutModel futModel = queryService.verifyFutExistRetorn(requestDTO.futId()); - queryService.verityPeladeiroInList(futModel, peladeiroModel); - log.info("Verificacao do Peladeiro e do Fut realizadas com sucesso! Salvando dados..."); - - futModel - .getPeladeiros() - .add(peladeiroModel); // Esse adiciona a tabela: "participa_peladeiro_fut". - repository.save( - futModel); // Esse salva em FutModel (que é onde a table intermediaria foi criada) - return CompletableFuture.completedFuture(null); + return peladeiroQueryService + .verifyPeladeiroExist(requestDTO.peladeiroId()) + .thenCompose( + peladeiroModel -> + queryService + .verifyFutExistRetorn(requestDTO.futId()) + .thenCompose( + futModel -> + queryService + .verityPeladeiroInList(futModel, peladeiroModel) + .thenRun( + () -> { + log.info( + "Verificacao do Peladeiro e do Fut realizadas com sucesso! Salvando dados..."); + + futModel + .getPeladeiros() + .add(peladeiroModel); // Esse adiciona a tabela: + // "participa_peladeiro_fut". + repository.save( + futModel); // Esse salva em FutModel (que é onde a table + // intermediaria foi criada) + }))); } @Override @Transactional + @Async("defaultExecutor") public CompletableFuture> listarPeladeiroCadastradosFut(Long futId) { - FutModel futModel = queryService.verifyFutExistRetornListPeladeiro(futId); /* + return queryService + .verifyFutExistRetornListPeladeiro(futId) /* Esse metodo retorna futModel já com peladeiros carregado... resolvendo o problema abaixo. @ManyToMany é LAZY, Ou seja: - O Hibernate carrega os objetos da lista somente quando necessário. - / - MAS… apenas carrega os campos que estão presentes na tabela Many-to-Many(id , nome, email...). + - MAS… apenas carrega os campos que estão presentes na tabela Many-to-Many(id , nome, email...). - A tabela intermediária NÃO contém o ID do Peladeiro (Ex. id: null nome: null ...). - Portanto o Hibernate cria um “proxy”(Pense em um proxy como um “representante” de um objeto real.) com ID NULL até realmente precisar buscar do banco. */ - log.info("Verificacao de existencia de Fut realizada com sucesso!"); + .thenApply( + futModel -> { + log.info("Verificacao de existencia de Fut realizada com sucesso!"); - List listResponce = new ArrayList<>(); + List listResponce = new ArrayList<>(); - for (PeladeiroModel p : futModel.getPeladeiros()) { - listResponce.add(peladeiroMapper.modelToPeladeiroResponse(p)); - } - return CompletableFuture.completedFuture(listResponce); + for (PeladeiroModel p : futModel.getPeladeiros()) { + listResponce.add(peladeiroMapper.modelToPeladeiroResponse(p)); + } + return listResponce; + }); } // ===================== lista Editores ============================= @Override @Transactional + @Async("defaultExecutor") public CompletableFuture addEditor(AddEditorInFutListResquestDTO resquestDTO) { EditorModel editorModel = new EditorModel(); - // Verificacoes - FutModel futModel = queryService.verifyFutExistRetorn(resquestDTO.fut()); - PeladeiroModel peladeiroModel = - peladeiroQueryService.verifyPeladeiroExist(resquestDTO.peladeiro()); - queryService.verifyPeladeiroExistInListOrAdm(futModel, peladeiroModel); - - editorModel.setFut(futModel); - editorModel.setPeladeiro(peladeiroModel); - // Salvando Editor - editorService.create(editorModel); - // Adicionando Editor na lista de Editores de Fut. - futModel.getEditores().add(editorModel); - repository.save(futModel); - // Adicionado Editor na lista de Editores de Peladeiro. - peladeiroModel.getEditores().add(editorModel); - peladeiroRepository.save(peladeiroModel); + return queryService + .verifyFutExistRetorn(resquestDTO.fut()) + .thenCompose( + futModel -> + peladeiroQueryService + .verifyPeladeiroExist(resquestDTO.peladeiro()) + .thenAccept( + peladeiroModel -> { + queryService.verifyPeladeiroExistInListOrAdm(futModel, peladeiroModel); + + editorModel.setFut(futModel); + editorModel.setPeladeiro(peladeiroModel); + // Salvando Editor + editorService.create(editorModel); + // Adicionando Editor na lista de Editores de Fut. + futModel.getEditores().add(editorModel); + repository.save(futModel); + // Adicionado Editor na lista de Editores de Peladeiro. + peladeiroModel.getEditores().add(editorModel); + peladeiroRepository.save(peladeiroModel); + })); } @Override @Transactional + @Async("defaultExecutor") public CompletableFuture> listarEditoresCadastradosFut( Long idFut) { - FutModel futModel = queryService.verifyFutExistRetornListEditores(idFut); - log.info("Verificacao de existencia de Fut realizada com sucesso!"); + return queryService + .verifyFutExistRetornListEditores(idFut) + .thenApply( + futModel -> { + log.info("Verificacao de existencia de Fut realizada com sucesso!"); - List listResponse = new ArrayList<>(); + List listResponse = new ArrayList<>(); - for (EditorModel e : futModel.getEditores()) { - listResponse.add(editorMapper.toResponseNameId(e)); - } + for (EditorModel e : futModel.getEditores()) { + listResponse.add(editorMapper.toResponseNameId(e)); + } - return CompletableFuture.completedFuture(listResponse); + return listResponse; + }); } // ===================== upload arquivos fotos ====================== @Override @Transactional + @Async("defaultExecutor") public CompletableFuture atualizarFotoCapa(Long id, MultipartFile file) { queryService.verifyFutSaveFile(id, file); + return CompletableFuture.completedFuture(null); } // ============================= Banimentos ============================ @Override @Transactional + @Async("defaultExecutor") public CompletableFuture addBanimentoList(SaveBanimentoRequestDTO dto) { - FutModel futModel = queryService.verifyFutExistRetorn(dto.fut()); - PeladeiroModel peladeiroModel = peladeiroQueryService.verifyPeladeiroExist(dto.peladeiro()); - - // Verificacao de existe o peladeiro em questao na lista de peladeiro... - queryService.verifyBanidoListPeladeiros(futModel, peladeiroModel); - - return CompletableFuture.completedFuture(banidoService.create(dto)); + return queryService + .verifyFutExistRetorn(dto.fut()) + .thenCompose( + futModel -> + peladeiroQueryService + .verifyPeladeiroExist(dto.peladeiro()) + .thenCompose( + peladeiroModel -> { + // Verificacao de existe o peladeiro em questao na lista de peladeiro... + queryService.verifyBanidoListPeladeiros(futModel, peladeiroModel); + + return banidoService.create(dto); + })); } @Override @Transactional - @Async + @Async("defaultExecutor") public CompletableFuture> findAllBanidos(Long idFut) { - return CompletableFuture.completedFuture(banidoService.findAll(idFut)); + return banidoService.findAll(idFut); } @Override @Transactional + @Async("defaultExecutor") public CompletableFuture removeBanido(Long idPeladeiro, Long idFut) { - // TODO: retirar um Banido da lista - queryService.verifyBanidoListPeladeiros( - queryService.verifyFutExistRetorn(idFut), - peladeiroQueryService.verifyPeladeiroExist(idPeladeiro)); - - banidoService.delete(idPeladeiro); - return CompletableFuture.completedFuture(null); + return queryService + .verifyFutExistRetorn(idFut) + .thenCompose( + futModel -> + peladeiroQueryService + .verifyPeladeiroExist(idPeladeiro) + .thenCompose( + peladeiroModel -> { + queryService.verifyBanidoListPeladeiros(futModel, peladeiroModel); + return banidoService.delete(idPeladeiro); + })); } } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/HistoricoPeladeiroService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/HistoricoPeladeiroService.java index aab5702..8557274 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/HistoricoPeladeiroService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/HistoricoPeladeiroService.java @@ -6,7 +6,9 @@ import br.com.vemprofut.repositories.HistoricoPeladeiroRepository; import br.com.vemprofut.services.IHistoricoPeladeiroService; import br.com.vemprofut.services.query.IHistoricoPeladeiroQueryService; +import java.util.concurrent.CompletableFuture; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -21,41 +23,53 @@ public class HistoricoPeladeiroService implements IHistoricoPeladeiroService { @Override @Transactional - public HistoricoPeladeiroDTO create() { + @Async("defaultExecutor") + public CompletableFuture create() { HistoricoPeladeiroModel historico = new HistoricoPeladeiroModel(); - return mapper.toDTO(repository.save(historico)); + var response = mapper.toDTO(repository.save(historico)); + return CompletableFuture.completedFuture(response); + + // Se mudar futuramente repository para async tem que compor o metodo com thenApply. } @Override @Transactional(readOnly = true) - public HistoricoPeladeiroDTO findById(Long id) { + @Async("defaultExecutor") + public CompletableFuture findById(Long id) { - return mapper.toDTO(queryService.verityHistoricoPeladeiroExistReturn(id)); + return queryService.verityHistoricoPeladeiroExistReturn(id).thenApply(mapper::toDTO); } @Override @Transactional(readOnly = true) - public HistoricoPeladeiroModel findByIdModel(Long id) { + @Async("defaultExecutor") + public CompletableFuture findByIdModel(Long id) { return queryService.verityHistoricoPeladeiroExistReturn(id); } @Override - public HistoricoPeladeiroDTO update(Long id, HistoricoPeladeiroDTO dto) { - HistoricoPeladeiroModel historico = queryService.verityHistoricoPeladeiroExistReturn(id); - - historico.setGolsDoPeladeiro(dto.golsDoPeladeiro()); - historico.setNotaPeladeiro(dto.notaPeladeiro()); - historico.setPartidasJogadas(dto.partidasJogadas()); - historico.setPartidasGanhas(dto.partidasGanhas()); + @Async("defaultExecutor") + public CompletableFuture update(Long id, HistoricoPeladeiroDTO dto) { + return queryService + .verityHistoricoPeladeiroExistReturn(id) + .thenApply( + historicoPeladeiroModel -> { + historicoPeladeiroModel.setGolsDoPeladeiro(dto.golsDoPeladeiro()); + historicoPeladeiroModel.setNotaPeladeiro(dto.notaPeladeiro()); + historicoPeladeiroModel.setPartidasJogadas(dto.partidasJogadas()); + historicoPeladeiroModel.setPartidasGanhas(dto.partidasGanhas()); - return mapper.toDTO(repository.save(historico)); + return mapper.toDTO(repository.save(historicoPeladeiroModel)); + }); } @Override - public void delete(Long id) { + @Async("defaultExecutor") + public CompletableFuture delete(Long id) { queryService.verityHistoricoPeladeiroExist(id); repository.deleteById(id); + return CompletableFuture.completedFuture(null); } // TODO: implementar a somatoria de gols ao historico peladeiro diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/PeladeiroService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/PeladeiroService.java index 5057376..61b4a23 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/PeladeiroService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/implementacao/PeladeiroService.java @@ -8,7 +8,6 @@ import br.com.vemprofut.controllers.response.UpdatePeladeiroResponseDTO; import br.com.vemprofut.mappers.IHistoricoPeladeiroMapper; import br.com.vemprofut.mappers.IPeladeiroMapper; -import br.com.vemprofut.models.DTOs.HistoricoPeladeiroDTO; import br.com.vemprofut.models.PeladeiroModel; import br.com.vemprofut.repositories.PeladeiroRepository; import br.com.vemprofut.services.ICartoesService; @@ -48,12 +47,16 @@ public CompletableFuture create(SavePeladeiroRequestDT PeladeiroModel peladeiroModel = peladeiroMapper.saveRequestToModel(dto); PeladeiroModel peladeiroSalvo = repository.save(peladeiroModel); - HistoricoPeladeiroDTO historico = historicoPeladeiroService.create(); - peladeiroSalvo.setHistoricoPeladeiro(historicoMapper.toModel(historico)); + return historicoPeladeiroService + .create() + .thenApply( + historico -> { + peladeiroSalvo.setHistoricoPeladeiro(historicoMapper.toModel(historico)); + PeladeiroModel salvo = repository.save(peladeiroSalvo); - log.info("Peladeiro cadastrado com sucesso!"); - var response = peladeiroMapper.modelToSaveResponse(repository.save(peladeiroSalvo)); - return CompletableFuture.completedFuture(response); + log.info("Peladeiro cadastrado com sucesso!"); + return peladeiroMapper.modelToSaveResponse(salvo); + }); } @Override diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/IFutQueryService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/IFutQueryService.java index 238927e..111f544 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/IFutQueryService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/IFutQueryService.java @@ -2,27 +2,29 @@ import br.com.vemprofut.models.FutModel; import br.com.vemprofut.models.PeladeiroModel; +import java.util.concurrent.CompletableFuture; import org.springframework.web.multipart.MultipartFile; public interface IFutQueryService { - void verifyFutExist(Long dto); + CompletableFuture verifyFutExist(Long dto); - FutModel verifyFutExistRetorn(Long fut); + CompletableFuture verifyFutExistRetorn(Long fut); - FutModel verifyFutExistRetornListPeladeiro(Long id); + CompletableFuture verifyFutExistRetornListPeladeiro(Long id); - FutModel verifyFutExistRetornListEditores(Long id); + CompletableFuture verifyFutExistRetornListEditores(Long id); - void verifyNomeFutExist(String nome); + CompletableFuture verifyNomeFutExist(String nome); - FutModel verifyNomeFutExistRetorn(String nome); + CompletableFuture verifyNomeFutExistRetorn(String nome); - void verifyPeladeiroExistInListOrAdm(FutModel futModel, PeladeiroModel model); + CompletableFuture verifyPeladeiroExistInListOrAdm(FutModel futModel, PeladeiroModel model); - void verifyFutSaveFile(Long id, MultipartFile file); + CompletableFuture verifyFutSaveFile(Long id, MultipartFile file); - void verityPeladeiroInList(FutModel futModel, PeladeiroModel peladeiroModel); + CompletableFuture verityPeladeiroInList(FutModel futModel, PeladeiroModel peladeiroModel); - void verifyBanidoListPeladeiros(FutModel futModel, PeladeiroModel peladeiroModel); + CompletableFuture verifyBanidoListPeladeiros( + FutModel futModel, PeladeiroModel peladeiroModel); } diff --git a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/FutQueryService.java b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/FutQueryService.java index 95c01ef..4ab8534 100644 --- a/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/FutQueryService.java +++ b/VemProFutApi/src/main/java/br/com/vemprofut/services/query/implementacao/FutQueryService.java @@ -4,13 +4,14 @@ import br.com.vemprofut.models.FutModel; import br.com.vemprofut.models.PeladeiroModel; import br.com.vemprofut.repositories.FutRepository; -import br.com.vemprofut.repositories.PeladeiroRepository; import br.com.vemprofut.services.IUploadLocalService; import br.com.vemprofut.services.query.IFutQueryService; import jakarta.persistence.EntityNotFoundException; import java.io.IOException; +import java.util.concurrent.CompletableFuture; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @@ -21,91 +22,128 @@ public class FutQueryService implements IFutQueryService { private final FutRepository repository; private final IUploadLocalService uploadLocalService; - private final PeladeiroRepository peladeiroRepository; @Override - public void verifyFutExist(Long dto) { + @Async("defaultExecutor") + public CompletableFuture verifyFutExist(Long dto) { if (repository.existsById(dto)) { - throw new FutInUseException("Fut já cadastrado!"); + return CompletableFuture.failedFuture(new FutInUseException("Fut já cadastrado!")); } + return CompletableFuture.completedFuture(null); } @Override - public FutModel verifyFutExistRetorn(Long fut) { + @Async("defaultExecutor") + public CompletableFuture verifyFutExistRetorn(Long fut) { return repository .findById(fut) - .orElseThrow(() -> new NotFoundException("Não foi encontrado o Fut de id " + fut)); + .map(CompletableFuture::completedFuture) + .orElseGet( + () -> + CompletableFuture.failedFuture( + new NotFoundException("Não foi encontrado o Fut de id " + fut))); } @Override - public FutModel verifyFutExistRetornListPeladeiro(Long id) { + @Async("defaultExecutor") + public CompletableFuture verifyFutExistRetornListPeladeiro(Long id) { return repository .buscarFutComListPeladeiros(id) - .orElseThrow( - () -> new NotFoundException("Não foi encontrado o Fut de id ou lista de Peladeiro")); + .map(CompletableFuture::completedFuture) + .orElseGet( + () -> + CompletableFuture.failedFuture( + new NotFoundException("Não foi encontrado o Fut de id ou lista de Peladeiro"))); } @Override - public FutModel verifyFutExistRetornListEditores(Long id) { + @Async("defaultExecutor") + public CompletableFuture verifyFutExistRetornListEditores(Long id) { return repository .buscarFutComListEditores(id) - .orElseThrow( - () -> new NotFoundException("Não foi encontrado o Fut de id ou lista de Editores")); + .map(CompletableFuture::completedFuture) + .orElseGet( + () -> + CompletableFuture.failedFuture( + new NotFoundException("Não foi encontrado o Fut de id ou lista de Editores"))); } @Override - public void verifyNomeFutExist(String nome) { + @Async("defaultExecutor") + public CompletableFuture verifyNomeFutExist(String nome) { log.info("Iniciando a verificacao do nome..."); - boolean exists = repository.existsByNomeStartingWith(nome); - if (exists) { + if (repository.existsByNomeStartingWith(nome)) { log.warn("Nome já existente: " + nome); - throw new NomeInUseException("O nome '" + nome + "' já está cadastrado!"); + return CompletableFuture.failedFuture( + new NomeInUseException("O nome '" + nome + "' já está cadastrado!")); } log.info("Nome disponível."); + return CompletableFuture.completedFuture(null); } @Override - public FutModel verifyNomeFutExistRetorn(String nome) { + @Async("defaultExecutor") + public CompletableFuture verifyNomeFutExistRetorn(String nome) { return repository .findByNome(nome) - .orElseThrow( - () -> new EntityNotFoundException("Futebol com nome '" + nome + "' não encontrado")); + .map(CompletableFuture::completedFuture) + .orElseGet( + () -> + CompletableFuture.failedFuture( + new EntityNotFoundException("Futebol com nome '" + nome + "' não encontrado"))); } @Override - public void verifyPeladeiroExistInListOrAdm(FutModel futModel, PeladeiroModel model) { + @Async("defaultExecutor") + public CompletableFuture verifyPeladeiroExistInListOrAdm( + FutModel futModel, PeladeiroModel model) { if (!(futModel.getPeladeiros().contains(model))) { - throw new NotFoundException("Editor nao cadastrado na lista de peladeiros do fut!"); + return CompletableFuture.failedFuture( + new NotFoundException("Editor nao cadastrado na lista de peladeiros do fut!")); } if (futModel.getAdministradorPeladeiro().equals(model)) { - throw new NotFoundException("O Editor selecionado já é o Administrador do Fut!"); + return CompletableFuture.failedFuture( + new NotFoundException("O Editor selecionado já é o Administrador do Fut!")); } + return CompletableFuture.completedFuture(null); } @Override - public void verifyFutSaveFile(Long id, MultipartFile file) { - FutModel futModel = verifyFutExistRetorn(id); - try { - String url = uploadLocalService.upload(file, "fut"); - futModel.setFoto_url(url); - repository.save(futModel); - } catch (IOException ex) { - throw new FileStorageException( - "Erro ao salvar a foto do peladeiro com id: " + id, ex.getCause()); - } + @Async("defaultExecutor") + public CompletableFuture verifyFutSaveFile(Long id, MultipartFile file) { + return verifyFutExistRetorn(id) + .thenAccept( + futModel -> { + try { + String url = uploadLocalService.upload(file, "fut"); + futModel.setFoto_url(url); + repository.save(futModel); + } catch (IOException ex) { + throw new FileStorageException( + "Erro ao salvar a foto do peladeiro com id: " + id, ex.getCause()); + } + }); } @Override - public void verityPeladeiroInList(FutModel futModel, PeladeiroModel peladeiroModel) { + @Async("defaultExecutor") + public CompletableFuture verityPeladeiroInList( + FutModel futModel, PeladeiroModel peladeiroModel) { if (futModel.getPeladeiros().contains(peladeiroModel)) { - throw new PeladeiroNotExistException("Peladeiro já cadastrado na lista de peladeiros!"); + return CompletableFuture.failedFuture( + new PeladeiroNotExistException("Peladeiro já cadastrado na lista de peladeiros!")); } + return CompletableFuture.completedFuture(null); } @Override - public void verifyBanidoListPeladeiros(FutModel futModel, PeladeiroModel peladeiroModel) { + @Async("defaultExecutor") + public CompletableFuture verifyBanidoListPeladeiros( + FutModel futModel, PeladeiroModel peladeiroModel) { if (!(futModel.getPeladeiros().contains(peladeiroModel))) { - throw new NotFoundException("Nao consta esse Peladeiro na lista de Peladeiros"); + return CompletableFuture.failedFuture( + new NotFoundException("Nao consta esse Peladeiro na lista de Banidos")); } + return CompletableFuture.completedFuture(null); } } diff --git a/VemProFutApi/src/test/java/br/com/vemprofut/unit/services/FutServiceTest.java b/VemProFutApi/src/test/java/br/com/vemprofut/unit/services/FutServiceTest.java index 0d6d866..abf2aaa 100644 --- a/VemProFutApi/src/test/java/br/com/vemprofut/unit/services/FutServiceTest.java +++ b/VemProFutApi/src/test/java/br/com/vemprofut/unit/services/FutServiceTest.java @@ -9,23 +9,18 @@ import br.com.vemprofut.controllers.response.SaveFutResponseDTO; import br.com.vemprofut.exceptions.NomeInUseException; import br.com.vemprofut.exceptions.NotFoundException; -import br.com.vemprofut.mappers.IEditorMapper; import br.com.vemprofut.mappers.IFutMapper; -import br.com.vemprofut.mappers.IPartidasMapper; -import br.com.vemprofut.mappers.IPeladeiroMapper; import br.com.vemprofut.models.DTOs.FutDTO; import br.com.vemprofut.models.FutModel; import br.com.vemprofut.models.HistoricoFutModel; import br.com.vemprofut.models.PeladeiroModel; import br.com.vemprofut.repositories.*; -import br.com.vemprofut.services.IBanimentoService; -import br.com.vemprofut.services.IEditorService; import br.com.vemprofut.services.IHistoricoFutService; -import br.com.vemprofut.services.IPartidasService; import br.com.vemprofut.services.implementacao.FutService; import br.com.vemprofut.services.query.IFutQueryService; import br.com.vemprofut.services.query.IPeladeiroQueryService; import java.util.List; +import java.util.concurrent.CompletableFuture; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -40,19 +35,9 @@ public class FutServiceTest { @Mock private IFutQueryService queryService; @Mock private IFutMapper mapper; - @Mock private IPartidasMapper partidasMapper; @Mock private FutRepository repository; - @Mock private IPartidasService partidasService; - @Mock private PartidasRepository partidasRepository; @Mock private IPeladeiroQueryService peladeiroQueryService; - @Mock private PeladeiroRepository peladeiroRepository; - @Mock private IPeladeiroMapper peladeiroMapper; @Mock private IHistoricoFutService historicoFutService; - @Mock private CartoesRepository cartoesRepository; - @Mock private GolsPartidaRepository golsRepository; - @Mock private IEditorService editorService; - @Mock private IEditorMapper editorMapper; - @Mock private IBanimentoService banidoService; @InjectMocks private FutService futService; @@ -88,16 +73,17 @@ void create_quandoFutValido_retornaFutSalvo() { new SaveFutResponseDTO( 1L, "Fut teste", 4, 10, 2, historicoFutModel.getId(), peladeiroModel.getId()); - doNothing().when(queryService).verifyNomeFutExist(futModel.getNome()); + when(queryService.verifyNomeFutExist(futModel.getNome())) + .thenReturn(CompletableFuture.completedFuture(null)); when(peladeiroQueryService.verifyPeladeiroExist(saveFutRequestDTO.administradorPeladeiro())) - .thenReturn(peladeiroModel); + .thenReturn(CompletableFuture.completedFuture(peladeiroModel)); when(mapper.saveRequestToModel(saveFutRequestDTO)).thenReturn(futModel); when(repository.save(futModel)).thenReturn(futModel); when(historicoFutService.create()).thenReturn(historicoFutModel); when(mapper.toSaveResponse(futModel)).thenReturn(saveFutResponseDTO); // Act: - SaveFutResponseDTO response = futService.create(saveFutRequestDTO); + SaveFutResponseDTO response = futService.create(saveFutRequestDTO).join(); // Assert: assertThat(response.id()).isNotNull().isEqualTo(1L); @@ -140,11 +126,12 @@ void findById_quandoFutExiste_retornaFutDetailsResponse() { FutDetailsResponse futDetailsResponse = new FutDetailsResponse(1L, "Fut teste", 4, 10, 2, 1L, 1L); - when(queryService.verifyFutExistRetorn(1L)).thenReturn(futModel); + when(queryService.verifyFutExistRetorn(1L)) + .thenReturn(CompletableFuture.completedFuture(futModel)); when(mapper.modelToDetailsResponse(futModel)).thenReturn(futDetailsResponse); // Act: - FutDetailsResponse response = futService.findById(1L); + FutDetailsResponse response = futService.findById(1L).join(); // Assert assertThat(response.id()).isNotNull().isEqualTo(1L); @@ -159,8 +146,6 @@ void findById_quandoFutExiste_retornaFutDetailsResponse() { void findById_quandoFutInexistente_retornaNotFoundException() { // Arrange: - FutDetailsResponse futDetailsResponse = - new FutDetailsResponse(1L, "Fut teste", 4, 10, 2, 1L, 1L); when(queryService.verifyFutExistRetorn(99L)) .thenThrow(new NotFoundException("Não foi encontrado o Fut de id " + 99L)); @@ -181,11 +166,12 @@ void findByNome_quandoFutExiste_retornaFutDTO() { List cartoes = List.of(1L, 2L, 3L, 5L); FutDTO futDTO = new FutDTO(1L, "Fut Test", 4, 10, 2, 1L, 1L, editores, peladeiros, cartoes); - when(queryService.verifyNomeFutExistRetorn("Fut Test")).thenReturn(futModel); + when(queryService.verifyNomeFutExistRetorn("Fut Test")) + .thenReturn(CompletableFuture.completedFuture(futModel)); when(mapper.toDTO(futModel)).thenReturn(futDTO); // Act - FutDTO response = futService.findByNome("Fut Test"); + FutDTO response = futService.findByNome("Fut Test").join(); // Assert assertThat(response).isNotNull(); diff --git a/VemProFutApi/src/test/java/br/com/vemprofut/unit/services/PeladeiroServiceTest.java b/VemProFutApi/src/test/java/br/com/vemprofut/unit/services/PeladeiroServiceTest.java index bddeffb..322e43c 100644 --- a/VemProFutApi/src/test/java/br/com/vemprofut/unit/services/PeladeiroServiceTest.java +++ b/VemProFutApi/src/test/java/br/com/vemprofut/unit/services/PeladeiroServiceTest.java @@ -14,6 +14,8 @@ import br.com.vemprofut.exceptions.NotFoundException; import br.com.vemprofut.mappers.IHistoricoPeladeiroMapper; import br.com.vemprofut.mappers.IPeladeiroMapper; +import br.com.vemprofut.models.DTOs.HistoricoPeladeiroDTO; +import br.com.vemprofut.models.HistoricoPeladeiroModel; import br.com.vemprofut.models.PeladeiroModel; import br.com.vemprofut.models.enuns.PeDominante; import br.com.vemprofut.repositories.PeladeiroRepository; @@ -22,6 +24,7 @@ import br.com.vemprofut.services.implementacao.PeladeiroService; import br.com.vemprofut.services.query.IPeladeiroQueryService; import java.util.List; +import java.util.concurrent.CompletableFuture; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -38,9 +41,9 @@ public class PeladeiroServiceTest { @Mock private IPeladeiroQueryService queryService; @Mock private PeladeiroRepository repository; @Mock private IPeladeiroMapper peladeiroMapper; - @Mock private IHistoricoPeladeiroMapper historicoMapper; - @Mock private IHistoricoPeladeiroService historicoPeladeiroService; @Mock private ICartoesService cartoesService; + @Mock private IHistoricoPeladeiroService historicoPeladeiroService; + @Mock private IHistoricoPeladeiroMapper historicoPeladeiroMapper; @InjectMocks private PeladeiroService peladeiroService; @@ -68,12 +71,21 @@ void create_quandoPeladeiroValido() { PeDominante.DESTRO, "81999999999"); + HistoricoPeladeiroDTO historicoPeladeiroDTO = new HistoricoPeladeiroDTO(1L, 10, 9.5, 5, 3); + + HistoricoPeladeiroModel historicoPeladeiroModel = new HistoricoPeladeiroModel(); + // cria listas simuladas List partidas = List.of(10L, 20L); List futs = List.of(30L); List cartoes = List.of(40L, 50L); - doNothing().when(queryService).verifyEmail(savePeladeiroRequestDTO.email()); + when(queryService.verifyEmail(savePeladeiroRequestDTO.email())) + .thenReturn(CompletableFuture.completedFuture(null)); + when(historicoPeladeiroService.create()) + .thenReturn(CompletableFuture.completedFuture(historicoPeladeiroDTO)); + when(historicoPeladeiroMapper.toModel(historicoPeladeiroDTO)) + .thenReturn(historicoPeladeiroModel); when(peladeiroMapper.saveRequestToModel(savePeladeiroRequestDTO)).thenReturn(peladeiroModel); when(repository.save(peladeiroModel)).thenReturn(peladeiroModel); when(peladeiroMapper.modelToSaveResponse(peladeiroModel)) @@ -98,6 +110,8 @@ void create_quandoPeladeiroValido() { assertThat(response.nome()).isEqualTo("Marcio"); verify(queryService).verifyEmail(savePeladeiroRequestDTO.email()); + verify(historicoPeladeiroService).create(); + verify(historicoPeladeiroMapper).toModel(historicoPeladeiroDTO); verify(repository, times(2)).save(peladeiroModel); verify(peladeiroMapper).saveRequestToModel(savePeladeiroRequestDTO); verify(peladeiroMapper).modelToSaveResponse(peladeiroModel); @@ -116,11 +130,6 @@ void create_quandoEmailJaCadastrado_retornaEmailInUseException() { PeDominante.DESTRO, "81999999999"); - // cria listas simuladas - List partidas = List.of(10L, 20L); - List futs = List.of(30L); - List cartoes = List.of(40L, 50L); - doThrow( new EmailInUseException( "O e-mail " + savePeladeiroRequestDTO.email() + " já está em uso")) @@ -149,7 +158,8 @@ void update_quandoPeladeiroExiste_retornaPeladeiroAlterado() { PeDominante.CANHOTO, "81999993332"); - when(queryService.verifyPeladeiroExist(1L)).thenReturn(peladeiroModel); + when(queryService.verifyPeladeiroExist(1L)) + .thenReturn(CompletableFuture.completedFuture(peladeiroModel)); when(repository.save(peladeiroModel)).thenReturn(peladeiroModel); when(peladeiroMapper.modelToUpdateResponse(peladeiroModel)) .thenReturn( @@ -203,8 +213,10 @@ void update_quandoPeladeiroInexistente_retornaNotFoundException() { void findById_quandoPeladeiroExiste_retornaPeladeiroDetailResponse() { // Arrange CartoesResumoResponseDTO cartoesResumoResponseDTO = new CartoesResumoResponseDTO(2, 3, 2); - when(queryService.verifyPeladeiroExist(1L)).thenReturn(peladeiroModel); - when(cartoesService.contarCartoesPeladeiro(1L)).thenReturn(cartoesResumoResponseDTO); + when(queryService.verifyPeladeiroExist(1L)) + .thenReturn(CompletableFuture.completedFuture(peladeiroModel)); + when(cartoesService.contarCartoesPeladeiro(1L)) + .thenReturn(CompletableFuture.completedFuture(cartoesResumoResponseDTO)); // Act PeladeiroDetailResponse peladeiroDetailResponse = peladeiroService.findById(1L).join(); @@ -243,7 +255,8 @@ void findById_quandoPeladeiroInexistente_retornaNotFoundException() { @DisplayName("Deve retornar PeladeiroModel quando ID existe") void findByIdModel_quandoPeladeiroExiste_retornaPeladeiroModel() { // Arrenge - when(queryService.verifyPeladeiroExist(1L)).thenReturn(peladeiroModel); + when(queryService.verifyPeladeiroExist(1L)) + .thenReturn(CompletableFuture.completedFuture(peladeiroModel)); // Act PeladeiroModel response = peladeiroService.findByIdModel(1L).join(); @@ -275,7 +288,8 @@ void findByIdModel_quandoPeladeiroInexistente_retornaNotFoundException() { @DisplayName("Deve apagar o Peladeiro indicado pelo numero do ID") void delete_quandoPeladeiroExiste() { // Arrenge - when(queryService.verifyPeladeiroExist(1L)).thenReturn(peladeiroModel); + when(queryService.verifyPeladeiroExist(1L)) + .thenReturn(CompletableFuture.completedFuture(peladeiroModel)); doNothing().when(repository).deleteById(1L); // Act @@ -307,7 +321,8 @@ void delete_quandoPeladeiroInexistente() { void atualizarFoto_quandoPeladeiroExiste() { MultipartFile file = mock(MultipartFile.class); - doNothing().when(queryService).verifyPeladeiroSaveFile(1L, file); + when(queryService.verifyPeladeiroSaveFile(1L, file)) + .thenReturn(CompletableFuture.completedFuture(null)); peladeiroService.atualizarFoto(1L, file).join(); verify(queryService).verifyPeladeiroSaveFile(1L, file);