diff --git a/.github/workflows/deploy-test-toothless.yml b/.github/workflows/deploy-test-toothless.yml new file mode 100644 index 00000000..b9f16104 --- /dev/null +++ b/.github/workflows/deploy-test-toothless.yml @@ -0,0 +1,41 @@ +name: deploy to toothlessdev + +on: + workflow_dispatch: + push: + branches: [ develop ] + +jobs: + deploy: + environment: development + runs-on: toothlessdev + permissions: + checks: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: 17 + + - name: Create .env.dev file + run: | + touch .env.dev + echo "${{ secrets.ENV }}" >> .env.dev + + - name: Cache Gradle packages + uses: gradle/actions/setup-gradle@v3 + with: + cache-read-only: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' }} + + - name: Build with Gradle + run: ./dev.sh + + - name: Publish Unit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: build/test-results/**/*.xml \ No newline at end of file diff --git a/.gitignore b/.gitignore index a4fc9588..241b7e57 100644 --- a/.gitignore +++ b/.gitignore @@ -18,13 +18,13 @@ derby.log # Gradle artifacts .gradle .gradletasknamecache -/build -buildSrc/build -/spring-*/build -/framework-*/build -/integration-tests/build -/src/asciidoc/build -spring-test/test-output/ +**/build +**/buildSrc/build +**/spring-*/build +**/framework-*/build +**/integration-tests/build +**/src/asciidoc/build +**/spring-test/test-output/ # Maven artifacts pom.xml @@ -104,9 +104,9 @@ local.sh .run/ # Static Resources -src/main/resources/static/* -src/test/resources/static/* -./storage +**/src/main/resources/static/docs/* +**/src/main/resources/static/oas/* +storage/* # Yml File local-docker-compose.yml \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 5aa1cd39..cbdb3634 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM openjdk:17-jdk -ARG JAR_FILE=build/libs/*.jar +ARG JAR_FILE=get-p-api/build/libs/*.jar ADD ${JAR_FILE} app.jar ENTRYPOINT ["java", "-Duser.timezone=GMT+9", "-Djava.security.egd=file:/dev/./urandom", "-Dspring.profiles.active=dev", "-jar", "/app.jar"] \ No newline at end of file diff --git a/build.gradle b/build.gradle index 787dd108..98efaa88 100644 --- a/build.gradle +++ b/build.gradle @@ -2,137 +2,76 @@ plugins { id 'java' id 'org.springframework.boot' version '3.1.4' id 'io.spring.dependency-management' version '1.1.3' - id 'org.asciidoctor.jvm.convert' version '4.0.2' + id 'java-library' + id 'java-test-fixtures' + id 'maven-publish' } -group = 'es.princip' -version = '0.0.1-SNAPSHOT' +allprojects { + group = 'es.princip' + version = '0.0.1-SNAPSHOT' -java { - sourceCompatibility = '17' -} - -repositories { - mavenCentral() -} - -configurations { - compileOnly { - extendsFrom annotationProcessor + repositories { + mavenCentral() } - asciidoctorExt } -dependencies { - implementation 'org.springframework.boot:spring-boot-starter-web' - testImplementation 'org.springframework.boot:spring-boot-starter-test' - developmentOnly 'org.springframework.boot:spring-boot-devtools' - - // Validation - implementation 'org.springframework.boot:spring-boot-starter-validation' - implementation 'org.apache.tika:tika-core:2.9.1' - - // Jpa - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - - // Security - implementation 'org.springframework.boot:spring-boot-starter-security' - testImplementation 'org.springframework.security:spring-security-test' - - // Jwt - implementation 'io.jsonwebtoken:jjwt-api:0.11.5' - implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' - implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' - - // Database - runtimeOnly 'com.mysql:mysql-connector-j:9.0.0' - implementation 'org.flywaydb:flyway-core' - implementation 'org.flywaydb:flyway-mysql' - - // Lombok - compileOnly 'org.projectlombok:lombok' - annotationProcessor 'org.projectlombok:lombok' - testCompileOnly 'org.projectlombok:lombok' - testAnnotationProcessor 'org.projectlombok:lombok' - - // WebSocket - implementation 'org.springframework:spring-websocket:6.0.13' - - // QueryDSL - implementation "com.querydsl:querydsl-jpa:5.0.0:jakarta" - annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" - annotationProcessor "jakarta.annotation:jakarta.annotation-api" - annotationProcessor "jakarta.persistence:jakarta.persistence-api" - - // Redis - implementation 'org.springframework.boot:spring-boot-starter-data-redis' - - // Mail - implementation 'com.sun.mail:jakarta.mail:2.0.1' - implementation 'org.springframework.boot:spring-boot-starter-mail:3.2.1' - - // MapStruct - implementation 'org.mapstruct:mapstruct:1.6.0' - annotationProcessor 'org.mapstruct:mapstruct-processor:1.6.0' - - // AOP - implementation 'org.springframework.boot:spring-boot-starter-aop' - - // Test Containers - testImplementation 'org.springframework.boot:spring-boot-testcontainers' - testImplementation 'org.testcontainers:testcontainers:1.19.3' - testImplementation 'org.testcontainers:junit-jupiter:1.19.3' - testImplementation 'org.testcontainers:mysql:1.20.0' +subprojects { + apply plugin: 'java' + apply plugin: 'org.springframework.boot' + apply plugin: 'io.spring.dependency-management' + apply plugin: 'java-library' + apply plugin: 'java-test-fixtures' + apply plugin: 'maven-publish' + + java { + sourceCompatibility = '17' + targetCompatibility = '17' + } - implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.16.1' + configurations { + compileOnly { + extendsFrom annotationProcessor + } + } - // Swagger - implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.4.0' + test { + useJUnitPlatform() + } - // Monitoring - implementation 'org.springframework.boot:spring-boot-starter-actuator' - implementation 'io.micrometer:micrometer-registry-prometheus' + tasks.register('copyTestResults', Copy) { + from 'build/test-results' + into '../build/test-results' + include '**/*.xml' + } - // Spring REST Docs - asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor' - testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' -} + test.finalizedBy(copyTestResults) -ext { - snippetsDir = file('build/generated-snippets') -} + dependencies { + // Lombok 라이브러리 + compileOnly 'org.projectlombok:lombok:1.18.34' + annotationProcessor 'org.projectlombok:lombok:1.18.34' + testCompileOnly 'org.projectlombok:lombok:1.18.34' + testAnnotationProcessor 'org.projectlombok:lombok:1.18.34' -test { - outputs.dir snippetsDir - useJUnitPlatform() -} + // MapStruct 라이브러리 + implementation 'org.mapstruct:mapstruct:1.6.0' + annotationProcessor 'org.mapstruct:mapstruct-processor:1.6.0' -asciidoctor { - inputs.dir snippetsDir - configurations 'asciidoctorExt' - dependsOn test - sources { - include('**/index.adoc') - } - baseDirFollowsSourceFile() -} + // Jackson 라이브러리 + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.16.1' -tasks.register('copyDocument', Copy) { - dependsOn asciidoctor - doFirst { - delete file('src/main/resources/static/docs') + // 테스트 프레임워크 + testImplementation 'org.springframework.boot:spring-boot-starter-test:3.3.5' + testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } - from asciidoctor.outputDir - into file('src/main/resources/static/docs') } -build { - dependsOn copyDocument +bootJar { + enabled = false } -bootJar { - dependsOn copyDocument - from ("${asciidoctor.outputDir}") { - into 'static/docs' - } +jar { + enabled = true } \ No newline at end of file diff --git a/get-p-api/build.gradle b/get-p-api/build.gradle new file mode 100644 index 00000000..0bd333c3 --- /dev/null +++ b/get-p-api/build.gradle @@ -0,0 +1,153 @@ +import org.yaml.snakeyaml.DumperOptions +import org.yaml.snakeyaml.Yaml + +plugins { + id 'org.asciidoctor.jvm.convert' version '4.0.2' + id 'com.epages.restdocs-api-spec' version '0.18.2' +} + +configurations { + asciidoctorExt +} + +dependencies { + // 모듈 의존성 + implementation(project(':get-p-domain')) + testImplementation(testFixtures(project(':get-p-domain'))) + implementation(project(':get-p-application')) + implementation(project(':get-p-persistence')) + implementation(project(':get-p-infrastructure')) + + // Spring 개발 도구 + developmentOnly 'org.springframework.boot:spring-boot-devtools:3.3.5' + + // Spring Web + implementation 'org.springframework.boot:spring-boot-starter-web:3.3.5' + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.3.5' + + // Spring Validation + implementation 'org.springframework.boot:spring-boot-starter-validation:3.3.5' + + // Spring Security + implementation 'org.springframework.boot:spring-boot-starter-security:3.3.5' + testImplementation 'org.springframework.security:spring-security-test:6.3.4' + + // Spring Data + implementation 'org.springframework.data:spring-data-commons' + + // JWT + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' + implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' + + // Spring REST Docs + asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor:3.0.2' + testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc:3.0.2' + testImplementation 'com.epages:restdocs-api-spec-mockmvc:0.18.2' + + // Swagger + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.4.0' + + // 모니터링 + implementation 'org.springframework.boot:spring-boot-starter-actuator:3.3.5' + implementation 'io.micrometer:micrometer-registry-prometheus:1.13.6' +} + +ext { + snippetsDir = file('build/generated-snippets') +} + +test { + outputs.dir snippetsDir +} + +asciidoctor { + inputs.dir snippetsDir + configurations 'asciidoctorExt' + dependsOn test + baseDirFollowsSourceFile() +} + +tasks.register('copyDocument', Copy) { + dependsOn asciidoctor + doFirst { + delete file('src/main/resources/static/docs') + } + from asciidoctor.outputDir + into file('src/main/resources/static/docs') +} + +openapi3 { + servers = [ + { + url = 'https://api.principes.xyz' + description = '개발 서버' + }, + { + url = 'http://localhost:57020' + description = '로컬 서버' + } + ] + title = 'GET-P API' + description = 'GET-P API 명세서' + version = '0.1.0' + format = 'yaml' +} + +tasks.register('addSecurityConfig') { + dependsOn('openapi3') + doFirst { + def path = "${projectDir}/${openapi3.outputDirectory}/openapi3.yaml" + def yaml = new Yaml() + def file = new File(path) + def data = yaml.load(new InputStreamReader(new FileInputStream(new File(path)), 'UTF-8')) + + def components = data?.components ?: [:] + def JWT = [ + type: 'http', + scheme: 'bearer', + bearerFormat: 'JWT' + ] + def security = [[JWT: []]] + def securitySchemas = [JWT: JWT] + + components.securitySchemes = securitySchemas + data.security = security + + def options = new DumperOptions().with { + defaultFlowStyle = DumperOptions.FlowStyle.BLOCK + prettyFlow = true + indent = 2 + it + } + def converted = new Yaml(options) + def writer = new OutputStreamWriter(new FileOutputStream(file), 'UTF-8') + + converted.dump(data, writer) + } +} + +tasks.register('copyOas', Copy) { + dependsOn('addSecurityConfig') + doFirst { + delete file('src/main/resources/static/oas') + } + from openapi3.outputDirectory + into file('src/main/resources/static/oas') +} + +bootJar { + dependsOn copyDocument + dependsOn copyOas + from(asciidoctor.outputDir) { + into 'static/docs' + } + from(openapi3.outputDirectory) { + into 'static/oas' + } +} + +build { + dependsOn copyDocument + dependsOn copyOas +} \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/auth/login.adoc b/get-p-api/src/docs/asciidoc/auth/login.adoc new file mode 100644 index 00000000..78814fad --- /dev/null +++ b/get-p-api/src/docs/asciidoc/auth/login.adoc @@ -0,0 +1,3 @@ +사용자는 로그인을 할 수 있습니다. + +operation::/auth/login[snippets="http-request,request-fields,http-response,response-fields"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/auth/reissue-access-token.adoc b/get-p-api/src/docs/asciidoc/auth/reissue-access-token.adoc new file mode 100644 index 00000000..76033e11 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/auth/reissue-access-token.adoc @@ -0,0 +1,3 @@ +Access Token 및 Refresh Token 재발급 시 기존의 토큰들은 만료 처리가 됩니다. 새로운 토큰을 사용해주세요. + +operation::/auth/reissue-access-token[snippets="http-request,request-headers,http-response,response-fields"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/auth/send-email-verification-code-for-sign-up.adoc b/get-p-api/src/docs/asciidoc/auth/send-email-verification-code-for-sign-up.adoc new file mode 100644 index 00000000..124c4f3e --- /dev/null +++ b/get-p-api/src/docs/asciidoc/auth/send-email-verification-code-for-sign-up.adoc @@ -0,0 +1,3 @@ +사용자는 회원 가입 시 이메일을 인증해야 합니다. + +operation::/auth/send-email-verification-code-for-signup[snippets="http-request,http-response"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/auth/signup.adoc b/get-p-api/src/docs/asciidoc/auth/signup.adoc new file mode 100644 index 00000000..7237cf33 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/auth/signup.adoc @@ -0,0 +1,3 @@ +사용자는 회원 가입을 할 수 있습니다. + +operation::/auth/signup[snippets="http-request,request-fields,http-response"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/client/edit-my-client.adoc b/get-p-api/src/docs/asciidoc/client/edit-my-client.adoc new file mode 100644 index 00000000..444337c4 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/client/edit-my-client.adoc @@ -0,0 +1,3 @@ +의뢰자는 자신의 의뢰자 정보를 수정할 수 있습니다. + +operation::/client/edit-my-client[snippets="http-request,request-headers,request-fields,http-response"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/client/get-my-client.adoc b/get-p-api/src/docs/asciidoc/client/get-my-client.adoc new file mode 100644 index 00000000..71b09a48 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/client/get-my-client.adoc @@ -0,0 +1,3 @@ +의뢰자는 자신의 의뢰자 정보를 조회할 수 있습니다. + +operation::/client/get-my-client[snippets="http-request,request-headers,http-response,response-fields"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/client/register-my-client.adoc b/get-p-api/src/docs/asciidoc/client/register-my-client.adoc new file mode 100644 index 00000000..56e84004 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/client/register-my-client.adoc @@ -0,0 +1,3 @@ +의뢰자는 자신의 의뢰자 정보를 등록할 수 있습니다. + +operation::/client/register-my-client[snippets="http-request,request-headers,request-fields,http-response,response-fields"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/common/query-parameters-description.adoc b/get-p-api/src/docs/asciidoc/common/query-parameters-description.adoc new file mode 100644 index 00000000..b2bdc4f5 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/common/query-parameters-description.adoc @@ -0,0 +1 @@ +다음과 같은 필터 조건을 추가할 수 있어요. 각 조건마다 회원이 사용할 수 있는 권한이 다르니 주의해주세요. 사용 권한이 표기된 조건은 로그인한 경우에만 사용할 수 있으니, 반드시 `Authorization` 헤더를 포함해주세요. 사용 권한이 표시되지 않은 조건만 사용할 경우, `Authorization` 헤더를 포함하지 않아도 괜찮아요. 각 조건은 `?{필터명1}={필터 조건}&{필터명2}={필터 조건}...` 와 같이 `&` 를 사용해 중복으로 사용할 수 있어요. \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/common/sort-parameters-description.adoc b/get-p-api/src/docs/asciidoc/common/sort-parameters-description.adoc new file mode 100644 index 00000000..90c4261c --- /dev/null +++ b/get-p-api/src/docs/asciidoc/common/sort-parameters-description.adoc @@ -0,0 +1 @@ +다음과 같은 정렬 조건을 추가할 수 있어요. 오름차순으로 정렬하는 경우 `asc`, 내림차순으로 정렬하는 경우 `desc` 를 사용해주세요. 정렬 기준과 정렬 순서는 `,` 로 구분됩니다. 정렬 조건은 `?sort={필드명1},asc&sort={필드명2},desc` 와 같이 `&` 를 사용해 중복으로 사용할 수 있어요. 여러 개의 정렬 기준을 사용한 경우, 순서에 따라 우선 순위가 결정돼요. \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/index.adoc b/get-p-api/src/docs/asciidoc/index.adoc new file mode 100644 index 00000000..147c6689 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/index.adoc @@ -0,0 +1,170 @@ += GET-P API 문서 +:doctype: book +:icons: font +:source-highlighter: highlightjs +:toc: left +:toclevels: 4 +:sectnums: +:sectlinks: +:sectanchors: + +== 인증 +=== 회원 가입 + +include::{docdir}/auth/signup.adoc[] + +=== 로그인 + +include::{docdir}/auth/login.adoc[] + +=== 회원 가입 시 이메일 인증 코드 전송 + +include::{docdir}/auth/send-email-verification-code-for-sign-up.adoc[] + +=== Access Token 및 Refresh Token 재발급 + +include::{docdir}/auth/reissue-access-token.adoc[] + +== 사용자 + +=== [회원] 내 회원 정보 조회 + +include::{docdir}/member/get-my-member.adoc[] + +=== [회원] 내 프로필 사진 등록 + +include::{docdir}/member/upload-profile-image.adoc[] + +== 의뢰자 + +=== [의뢰자] 내 의뢰자 정보 등록 + +include::{docdir}/client/register-my-client.adoc[] + +=== [의뢰자] 내 의뢰자 정보 조회 + +include::{docdir}/client/get-my-client.adoc[] + +=== [의뢰자] 내 의뢰자 정보 수정 + +include::{docdir}/client/edit-my-client.adoc[] + +== 피플 + +=== 피플 상세 조회 + +include::{docdir}/people/get-people.adoc[] + +=== 피플 목록 조회 + +include::{docdir}/people/get-people-list.adoc[] + +== 피플 정보 + +=== [피플] 내 피플 정보 등록 + +피플은 자신의 피플 정보를 등록할 수 있습니다. + +include::{docdir}/people/register-my-people.adoc[] + +=== [피플] 내 피플 정보 조회 + +피플은 자신의 피플 정보를 조회할 수 있습니다. + +include::{docdir}/people/get-my-people.adoc[] + +=== [피플] 내 피플 정보 수정 + +피플은 자신의 피플 정보를 수정할 수 있습니다. + +include::{docdir}/people/edit-my-people.adoc[] + +== 피플 프로필 + +=== [피플] 내 피플 프로필 등록 + +include::{docdir}/people/register-my-people-profile.adoc[] + +=== [피플] 내 피플 프로필 조회 + +include::{docdir}/people/get-my-people-profile.adoc[] + +=== [피플] 내 피플 프로필 수정 + +include::{docdir}/people/edit-my-people-profile.adoc[] + +== 좋아요 + +=== [피플] 프로젝트 좋아요 + +include::{docdir}/like/like-project.adoc[] + +=== [피플] 프로젝트 좋아요 취소 + +include::{docdir}/like/unlike-project.adoc[] + +=== [의뢰자] 피플 좋아요 + +include::{docdir}/like/like-people.adoc[] + +=== [의뢰자] 피플 좋아요 취소 + +include::{docdir}/like/unlike-people.adoc[] + +== 프로젝트 + +=== 프로젝트 상세 조회 + +include::{docdir}/project/get-project.adoc[] + +=== 프로젝트 목록 조회 + +include::{docdir}/project/get-projects.adoc[] + +== 프로젝트 의뢰 + +=== [의뢰자] 프로젝트 의뢰 + +include::{docdir}/project/commission-project.adoc[] + +== 프로젝트 지원 + +=== [피플] 프로젝트 팀원 검색 + +include::{docdir}/project/search-teammates.adoc[] + +=== [피플] 프로젝트 지원(개인) + +include::{docdir}/project/apply-project-as-individual.adoc[] + +=== [피플] 프로젝트 지원(팀) + +include::{docdir}/project/apply-project-as-team.adoc[] + +=== [피플] 프로젝트 팀원 승인 + +include::{docdir}/project/approve-teammate.adoc[] + +=== [피플] 프로젝트 지원 내역 조회 + +include::{docdir}/project/get-application-detail.adoc[] + +== 프로젝트 관리 + +=== [의뢰자] 프로젝트 미팅 신청 + +include::{docdir}/project/schedule-meeting.adoc[] + +=== [의뢰자] 프로젝트 지원자 목록 조회 + +include::{docdir}/project/get-applicants.adoc[] + +=== [의뢰자] 프로젝트 지원서 조회 + +include::{docdir}/project/get-application-form.adoc[] + +== 스토리지 + +=== [회원] 파일 업로드 + +include::{docdir}/storage/upload-file.adoc[] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/like/like-people.adoc b/get-p-api/src/docs/asciidoc/like/like-people.adoc new file mode 100644 index 00000000..5ecdb69d --- /dev/null +++ b/get-p-api/src/docs/asciidoc/like/like-people.adoc @@ -0,0 +1 @@ +operation::/like/like-people[snippets="http-request,request-headers,http-response"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/like/like-project.adoc b/get-p-api/src/docs/asciidoc/like/like-project.adoc new file mode 100644 index 00000000..c63a52ff --- /dev/null +++ b/get-p-api/src/docs/asciidoc/like/like-project.adoc @@ -0,0 +1,3 @@ +피플은 마음에 드는 프로젝트에 좋아요를 누를 수 있습니다. + +operation::/like/like-project[snippets="http-request,request-headers,http-response"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/like/unlike-people.adoc b/get-p-api/src/docs/asciidoc/like/unlike-people.adoc new file mode 100644 index 00000000..2ecec8bd --- /dev/null +++ b/get-p-api/src/docs/asciidoc/like/unlike-people.adoc @@ -0,0 +1,2 @@ + +operation::/like/unlike-people[snippets="http-request,request-headers,http-response"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/like/unlike-project.adoc b/get-p-api/src/docs/asciidoc/like/unlike-project.adoc new file mode 100644 index 00000000..dd6ab712 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/like/unlike-project.adoc @@ -0,0 +1,3 @@ +피플은 마음에 드는 프로젝트에 눌렀던 좋아요를 취소할 수 있습니다. + +operation::/like/unlike-project[snippets="http-request,request-headers,http-response"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/member/get-my-member.adoc b/get-p-api/src/docs/asciidoc/member/get-my-member.adoc new file mode 100644 index 00000000..e8dac239 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/member/get-my-member.adoc @@ -0,0 +1,3 @@ +회원은 자신의 회원 정보를 조회할 수 있습니다. + +operation::/member/get-my-member[snippets="http-request,request-headers,http-response,response-fields"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/member/upload-profile-image.adoc b/get-p-api/src/docs/asciidoc/member/upload-profile-image.adoc new file mode 100644 index 00000000..5b260927 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/member/upload-profile-image.adoc @@ -0,0 +1,3 @@ +회원은 자신의 프로필 사진을 등록할 수 있습니다. + +operation::/member/upload-profile-image/[snippets="httpie-request,request-headers,request-parts,http-response,response-fields"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/people/edit-my-people-profile.adoc b/get-p-api/src/docs/asciidoc/people/edit-my-people-profile.adoc new file mode 100644 index 00000000..f55ee8d0 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/people/edit-my-people-profile.adoc @@ -0,0 +1,3 @@ +피플은 자신의 프로필을 수정할 수 있습니다. + +operation::/people/edit-my-people-profile[snippets="http-request,request-headers,request-fields,http-response"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/people/edit-my-people.adoc b/get-p-api/src/docs/asciidoc/people/edit-my-people.adoc new file mode 100644 index 00000000..a61c41f8 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/people/edit-my-people.adoc @@ -0,0 +1 @@ +operation::/people/edit-my-people[snippets="http-request,request-headers,request-fields,http-response"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/people/get-my-people-profile.adoc b/get-p-api/src/docs/asciidoc/people/get-my-people-profile.adoc new file mode 100644 index 00000000..62e9da67 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/people/get-my-people-profile.adoc @@ -0,0 +1,3 @@ +피플은 자신의 프로필을 조회할 수 있습니다. + +operation::/people/get-my-people-profile[snippets="http-request,request-headers,http-response,response-fields"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/people/get-my-people.adoc b/get-p-api/src/docs/asciidoc/people/get-my-people.adoc new file mode 100644 index 00000000..54034616 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/people/get-my-people.adoc @@ -0,0 +1 @@ +operation::/people/get-my-people[snippets="http-request,request-headers,http-response,response-fields"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/people/get-people-list.adoc b/get-p-api/src/docs/asciidoc/people/get-people-list.adoc new file mode 100644 index 00000000..88c73c19 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/people/get-people-list.adoc @@ -0,0 +1,30 @@ +플랫폼에 등록된 피플의 목록을 페이지 별로 조회할 수 있는 기능이에요. + +==== HTTP request +include::{snippets}/people/get-people-list/http-request.adoc[] + +==== Request headers +include::{snippets}/people/get-people-list/request-headers.adoc[] + +==== Query parameters + +include::../common/query-parameters-description.adoc[] +include::{snippets}/people/get-people-list/query-parameters.adoc[] + +==== Sort parameters + +include::../common/sort-parameters-description.adoc[] + +[cols=2*] +|=== +|정렬 조건|필드명 + +|피플 ID|`peopleId` +|좋아요 개수|`likesCount` +|=== + +==== HTTP response +include::{snippets}/people/get-people-list/http-response.adoc[] + +==== Response fields +include::{snippets}/people/get-people-list/response-fields.adoc[] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/people/get-people.adoc b/get-p-api/src/docs/asciidoc/people/get-people.adoc new file mode 100644 index 00000000..e71663a6 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/people/get-people.adoc @@ -0,0 +1,3 @@ +사용자는 피플의 상세 정보를 조회할 수 있습니다. 로그인을 하지 않은 경우, 특정 필드가 모자이크되어 반환됩니다. + +operation::/people/get-people[snippets="http-request,request-headers,http-response,response-fields"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/people/register-my-people-profile.adoc b/get-p-api/src/docs/asciidoc/people/register-my-people-profile.adoc new file mode 100644 index 00000000..0aa32ce8 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/people/register-my-people-profile.adoc @@ -0,0 +1,3 @@ +피플은 자신의 프로필을 등록할 수 있습니다. + +operation::/people/register-my-people-profile[snippets="http-request,request-headers,request-fields,http-response"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/people/register-my-people.adoc b/get-p-api/src/docs/asciidoc/people/register-my-people.adoc new file mode 100644 index 00000000..edf6ff10 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/people/register-my-people.adoc @@ -0,0 +1 @@ +operation::/people/register-my-people[snippets="http-request,request-headers,request-fields,http-response"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/project/apply-project-as-individual.adoc b/get-p-api/src/docs/asciidoc/project/apply-project-as-individual.adoc new file mode 100644 index 00000000..dd379aaa --- /dev/null +++ b/get-p-api/src/docs/asciidoc/project/apply-project-as-individual.adoc @@ -0,0 +1,3 @@ +피플은 프로젝트에 개인으로 지원할 수 있습니다. + +operation::/project/apply-project-as-individual[snippets="http-request,request-headers,path-parameters,request-fields,http-response,response-fields"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/project/apply-project-as-team.adoc b/get-p-api/src/docs/asciidoc/project/apply-project-as-team.adoc new file mode 100644 index 00000000..90353fec --- /dev/null +++ b/get-p-api/src/docs/asciidoc/project/apply-project-as-team.adoc @@ -0,0 +1,3 @@ +피플이 프로젝트에 팀으로 지원하는 기능이에요. 해당 API를 호출하게 되면 팀원들에게 프로젝트 팀원 승인 신청 메일이 발송돼요. + +operation::/project/apply-project-as-team[snippets="http-request,request-headers,path-parameters,request-fields,http-response,response-fields"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/project/approve-teammate.adoc b/get-p-api/src/docs/asciidoc/project/approve-teammate.adoc new file mode 100644 index 00000000..87930fc8 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/project/approve-teammate.adoc @@ -0,0 +1,3 @@ +피플이 팀원 신청을 승인하는 기능이에요. + +operation::/project/approve-teammate[snippets="http-request,request-headers,query-parameters,http-response"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/project/commission-project.adoc b/get-p-api/src/docs/asciidoc/project/commission-project.adoc new file mode 100644 index 00000000..aa9a7584 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/project/commission-project.adoc @@ -0,0 +1,3 @@ +의뢰자는 프로젝트를 의뢰할 수 있습니다. + +operation::/project/commission-project[snippets="http-request,request-headers,request-fields,http-response,response-fields"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/project/get-applicants.adoc b/get-p-api/src/docs/asciidoc/project/get-applicants.adoc new file mode 100644 index 00000000..54bb36cd --- /dev/null +++ b/get-p-api/src/docs/asciidoc/project/get-applicants.adoc @@ -0,0 +1,22 @@ +의뢰자는 자신이 의뢰한 프로젝트의 지원자 목록을 조회할 수 있습니다. 지원자가 팀원을 포함한 경우, 팀원 목록도 함께 조회됩니다. 이때 팀원이 모두 팀원 신청을 승인하지 않아 지원 상태가 팀원 승인 대기인 경우, 해당 팀은 조회되지 않습니다. 자세한 설명은 link:https://get-p.atlassian.net/wiki/spaces/GETP/pages/5637342/CLI-04[요구 사항 문서]의 "프로젝트 지원자 목록 조회"를 참고하세요. + +==== HTTP request +include::{snippets}/project/get-applicants/http-request.adoc[] + +==== Request headers +include::{snippets}/project/get-applicants/request-headers.adoc[] + +==== Path parameters + +include::{snippets}/project/get-applicants/path-parameters.adoc[] + +==== Query parameters + +include::../common/query-parameters-description.adoc[] +include::{snippets}/project/get-applicants/query-parameters.adoc[] + +==== HTTP response +include::{snippets}/project/get-applicants/http-response.adoc[] + +==== Response fields +include::{snippets}/project/get-applicants/response-fields.adoc[] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/project/get-application-detail.adoc b/get-p-api/src/docs/asciidoc/project/get-application-detail.adoc new file mode 100644 index 00000000..18ccc1d6 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/project/get-application-detail.adoc @@ -0,0 +1,17 @@ +피플은 자신의 프로젝트 지원 내역을 조회할 수 있습니다. 팀원과 함께 지원한 경우, 팀원 목록도 함께 조회됩니다. + +==== HTTP request +include::{snippets}/project/get-application-detail/http-request.adoc[] + +==== Request headers +include::{snippets}/project/get-application-detail/request-headers.adoc[] + +==== Path parameters + +include::{snippets}/project/get-application-detail/path-parameters.adoc[] + +==== HTTP response +include::{snippets}/project/get-application-detail/http-response.adoc[] + +==== Response fields +include::{snippets}/project/get-application-detail/response-fields.adoc[] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/project/get-application-form.adoc b/get-p-api/src/docs/asciidoc/project/get-application-form.adoc new file mode 100644 index 00000000..7026fc9b --- /dev/null +++ b/get-p-api/src/docs/asciidoc/project/get-application-form.adoc @@ -0,0 +1,17 @@ +의뢰자는 프로젝트 지원자의 지원서를 조회할 수 있습니다. 팀원과 함께 지원한 경우, 팀원 목록도 함께 조회됩니다. + +==== HTTP request +include::{snippets}/project/get-application-form/http-request.adoc[] + +==== Request headers +include::{snippets}/project/get-application-form/request-headers.adoc[] + +==== Path parameters + +include::{snippets}/project/get-application-form/path-parameters.adoc[] + +==== HTTP response +include::{snippets}/project/get-application-form/http-response.adoc[] + +==== Response fields +include::{snippets}/project/get-application-form/response-fields.adoc[] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/project/get-project.adoc b/get-p-api/src/docs/asciidoc/project/get-project.adoc new file mode 100644 index 00000000..96176613 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/project/get-project.adoc @@ -0,0 +1,3 @@ +사용자는 프로젝트의 상세 정보를 조회할 수 있습니다. + +operation::/project/get-project[snippets="http-request,request-headers,path-parameters,http-response,response-fields"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/project/get-projects.adoc b/get-p-api/src/docs/asciidoc/project/get-projects.adoc new file mode 100644 index 00000000..e76de5c0 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/project/get-projects.adoc @@ -0,0 +1,32 @@ +플랫폼에 등록된 프로젝트의 목록을 페이지 별로 조회할 수 있는 기능이에요. + +==== HTTP request +include::{snippets}/project/get-projects/http-request.adoc[] + +==== Request headers +include::{snippets}/project/get-projects/request-headers.adoc[] + +==== Query parameters + +include::../common/query-parameters-description.adoc[] +include::{snippets}/project/get-projects/query-parameters.adoc[] + +==== Sort parameters + +include::../common/sort-parameters-description.adoc[] + +[cols=2*] +|=== +|정렬 조건|필드명 + +|프로젝트 |`projectId` +|등록 날짜|`createdAt` +|성공 보수|`payment` +|지원자 모집 기간|`applicationDuration` +|=== + +==== HTTP response +include::{snippets}/project/get-projects/http-response.adoc[] + +==== Response fields +include::{snippets}/project/get-projects/response-fields.adoc[] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/project/schedule-meeting.adoc b/get-p-api/src/docs/asciidoc/project/schedule-meeting.adoc new file mode 100644 index 00000000..817d42d4 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/project/schedule-meeting.adoc @@ -0,0 +1,3 @@ +의뢰자는 프로젝트 지원자에게 미팅을 신청할 수 있습니다. + +operation::/project/schedule-meeting[snippets="http-request,request-headers,request-fields,http-response,response-fields"] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/project/search-teammates.adoc b/get-p-api/src/docs/asciidoc/project/search-teammates.adoc new file mode 100644 index 00000000..31e39979 --- /dev/null +++ b/get-p-api/src/docs/asciidoc/project/search-teammates.adoc @@ -0,0 +1,24 @@ +프로젝트 지원 시 함께 지원할 팀원을 검색하는 기능이에요. + +==== HTTP request +include::{snippets}/project/search-teammates/http-request.adoc[] + +==== Request headers +include::{snippets}/project/search-teammates/request-headers.adoc[] + +==== Path parameters + +해당 프로젝트에 이미 지원한 피플은 검색 결과에서 제외하기 위해 프로젝트 ID가 필요해요. + +include::{snippets}/project/search-teammates/path-parameters.adoc[] + +==== Query parameters + +include::../common/query-parameters-description.adoc[] +include::{snippets}/project/search-teammates/query-parameters.adoc[] + +==== HTTP response +include::{snippets}/project/search-teammates/http-response.adoc[] + +==== Response fields +include::{snippets}/project/search-teammates/response-fields.adoc[] \ No newline at end of file diff --git a/get-p-api/src/docs/asciidoc/storage/upload-file.adoc b/get-p-api/src/docs/asciidoc/storage/upload-file.adoc new file mode 100644 index 00000000..4f5dcc6b --- /dev/null +++ b/get-p-api/src/docs/asciidoc/storage/upload-file.adoc @@ -0,0 +1,3 @@ +회원은 파일을 업로드할 수 있습니다. 업로드할 수 있는 파일 확장자는 `pdf`, `zip`, `ppt`, `docx`, `hwp`, `jpg`, `png` 입니다. + +operation::/storage/upload-file/[snippets="httpie-request,request-headers,request-parts,http-response,response-fields"] \ No newline at end of file diff --git a/src/main/java/es/princip/getp/GetpServerApplication.java b/get-p-api/src/main/java/es/princip/getp/GetpServerApplication.java similarity index 100% rename from src/main/java/es/princip/getp/GetpServerApplication.java rename to get-p-api/src/main/java/es/princip/getp/GetpServerApplication.java diff --git a/src/main/java/es/princip/getp/api/controller/auth/AuthController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/auth/AuthController.java similarity index 90% rename from src/main/java/es/princip/getp/api/controller/auth/AuthController.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/auth/AuthController.java index 30f62139..8ba287a0 100644 --- a/src/main/java/es/princip/getp/api/controller/auth/AuthController.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/auth/AuthController.java @@ -1,7 +1,7 @@ package es.princip.getp.api.controller.auth; import es.princip.getp.api.controller.auth.dto.request.LoginRequest; -import es.princip.getp.api.controller.auth.dto.response.Token; +import es.princip.getp.application.auth.dto.response.Token; import es.princip.getp.api.support.dto.ApiResponse; import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; import es.princip.getp.application.auth.service.AuthService; @@ -31,7 +31,9 @@ public class AuthController { */ @PostMapping("/login") public ResponseEntity> login(@RequestBody @Valid final LoginRequest request) { - final Token token = authService.login(request); + final String email = request.email(); + final String password = request.password(); + final Token token = authService.login(email, password); final String authorization = token.grantType() + " " + token.accessToken(); return ResponseEntity.status(HttpStatus.CREATED) .header(HttpHeaders.AUTHORIZATION, authorization) diff --git a/src/main/java/es/princip/getp/api/controller/auth/SignUpCommandMapper.java b/get-p-api/src/main/java/es/princip/getp/api/controller/auth/SignUpCommandMapper.java similarity index 82% rename from src/main/java/es/princip/getp/api/controller/auth/SignUpCommandMapper.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/auth/SignUpCommandMapper.java index e36d22d2..57205b6f 100644 --- a/src/main/java/es/princip/getp/api/controller/auth/SignUpCommandMapper.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/auth/SignUpCommandMapper.java @@ -2,13 +2,11 @@ import es.princip.getp.api.controller.auth.dto.request.ServiceTermAgreementRequest; import es.princip.getp.api.controller.auth.dto.request.SignUpRequest; -import es.princip.getp.api.controller.common.mapper.CommandMapper; -import es.princip.getp.application.auth.command.SignUpCommand; +import es.princip.getp.application.auth.dto.command.SignUpCommand; import es.princip.getp.domain.member.model.ServiceTermAgreementData; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -@CommandMapper @Mapper(componentModel = "spring") interface SignUpCommandMapper { diff --git a/src/main/java/es/princip/getp/api/controller/auth/SignUpController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/auth/SignUpController.java similarity index 100% rename from src/main/java/es/princip/getp/api/controller/auth/SignUpController.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/auth/SignUpController.java diff --git a/src/main/java/es/princip/getp/api/controller/auth/dto/request/EmailVerificationCodeRequest.java b/get-p-api/src/main/java/es/princip/getp/api/controller/auth/dto/request/EmailVerificationCodeRequest.java similarity index 100% rename from src/main/java/es/princip/getp/api/controller/auth/dto/request/EmailVerificationCodeRequest.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/auth/dto/request/EmailVerificationCodeRequest.java diff --git a/src/main/java/es/princip/getp/api/controller/auth/dto/request/LoginRequest.java b/get-p-api/src/main/java/es/princip/getp/api/controller/auth/dto/request/LoginRequest.java similarity index 100% rename from src/main/java/es/princip/getp/api/controller/auth/dto/request/LoginRequest.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/auth/dto/request/LoginRequest.java diff --git a/src/main/java/es/princip/getp/api/controller/auth/dto/request/ServiceTermAgreementRequest.java b/get-p-api/src/main/java/es/princip/getp/api/controller/auth/dto/request/ServiceTermAgreementRequest.java similarity index 100% rename from src/main/java/es/princip/getp/api/controller/auth/dto/request/ServiceTermAgreementRequest.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/auth/dto/request/ServiceTermAgreementRequest.java diff --git a/src/main/java/es/princip/getp/api/controller/auth/dto/request/SignUpRequest.java b/get-p-api/src/main/java/es/princip/getp/api/controller/auth/dto/request/SignUpRequest.java similarity index 100% rename from src/main/java/es/princip/getp/api/controller/auth/dto/request/SignUpRequest.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/auth/dto/request/SignUpRequest.java diff --git a/src/main/java/es/princip/getp/api/controller/client/command/MyClientController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/client/command/MyClientController.java similarity index 85% rename from src/main/java/es/princip/getp/api/controller/client/command/MyClientController.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/client/command/MyClientController.java index 923abd5e..ee63a0a3 100644 --- a/src/main/java/es/princip/getp/api/controller/client/command/MyClientController.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/client/command/MyClientController.java @@ -2,15 +2,16 @@ import es.princip.getp.api.controller.client.command.dto.request.EditMyClientRequest; import es.princip.getp.api.controller.client.command.dto.request.RegisterMyClientRequest; -import es.princip.getp.api.controller.client.command.dto.response.RegisterMyClientResponse; +import es.princip.getp.application.client.dto.response.RegisterMyClientResponse; +import es.princip.getp.application.auth.service.PrincipalDetails; import es.princip.getp.api.support.dto.ApiResponse; import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; -import es.princip.getp.api.security.details.PrincipalDetails; -import es.princip.getp.application.client.command.EditClientCommand; -import es.princip.getp.application.client.command.RegisterClientCommand; +import es.princip.getp.application.client.dto.command.EditClientCommand; +import es.princip.getp.application.client.dto.command.RegisterClientCommand; import es.princip.getp.application.client.port.in.EditClientUseCase; import es.princip.getp.application.client.port.in.RegisterClientUseCase; import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @@ -40,7 +41,7 @@ public ResponseEntity> registerMyClie ) { final Member member = principalDetails.getMember(); final RegisterClientCommand command = request.toCommand(member); - final Long clientId = registerClientUseCase.register(command); + final Long clientId = registerClientUseCase.register(command).getValue(); final RegisterMyClientResponse response = new RegisterMyClientResponse(clientId); return ApiResponse.success(HttpStatus.CREATED, response); } @@ -56,7 +57,7 @@ public ResponseEntity> editMyClient( @RequestBody @Valid final EditMyClientRequest request, @AuthenticationPrincipal final PrincipalDetails principalDetails ) { - final Long memberId = principalDetails.getMember().getMemberId(); + final MemberId memberId = principalDetails.getMember().getId(); final EditClientCommand command = request.toCommand(memberId); editClientUseCase.edit(command); return ApiResponse.success(HttpStatus.OK); diff --git a/src/main/java/es/princip/getp/api/controller/client/command/dto/request/EditMyClientRequest.java b/get-p-api/src/main/java/es/princip/getp/api/controller/client/command/dto/request/EditMyClientRequest.java similarity index 69% rename from src/main/java/es/princip/getp/api/controller/client/command/dto/request/EditMyClientRequest.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/client/command/dto/request/EditMyClientRequest.java index 70ce17d1..5905f3c7 100644 --- a/src/main/java/es/princip/getp/api/controller/client/command/dto/request/EditMyClientRequest.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/client/command/dto/request/EditMyClientRequest.java @@ -1,34 +1,31 @@ package es.princip.getp.api.controller.client.command.dto.request; -import es.princip.getp.application.client.command.EditClientCommand; +import es.princip.getp.application.client.dto.command.EditClientCommand; import es.princip.getp.domain.client.model.Address; -import es.princip.getp.domain.client.model.BankAccount; import es.princip.getp.domain.common.model.Email; import es.princip.getp.domain.common.model.EmailPattern; import es.princip.getp.domain.common.model.PhoneNumber; import es.princip.getp.domain.common.model.PhoneNumberPattern; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.member.model.Nickname; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; -// 갱신 명령은 기존 필드를 모두 가지고 있어야 한다. public record EditMyClientRequest( @NotBlank String nickname, @NotNull @EmailPattern String email, @NotNull @PhoneNumberPattern String phoneNumber, - @NotNull @Valid Address address, - @NotNull @Valid BankAccount bankAccount + @Valid Address address ) { - public EditClientCommand toCommand(final Long memberId) { + public EditClientCommand toCommand(final MemberId memberId) { return new EditClientCommand( memberId, Nickname.from(nickname()), Email.from(email()), PhoneNumber.from(phoneNumber()), - address, - bankAccount + address ); } } \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/client/command/dto/request/RegisterMyClientRequest.java b/get-p-api/src/main/java/es/princip/getp/api/controller/client/command/dto/request/RegisterMyClientRequest.java similarity index 77% rename from src/main/java/es/princip/getp/api/controller/client/command/dto/request/RegisterMyClientRequest.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/client/command/dto/request/RegisterMyClientRequest.java index 820b962d..ad7be439 100644 --- a/src/main/java/es/princip/getp/api/controller/client/command/dto/request/RegisterMyClientRequest.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/client/command/dto/request/RegisterMyClientRequest.java @@ -1,8 +1,7 @@ package es.princip.getp.api.controller.client.command.dto.request; -import es.princip.getp.application.client.command.RegisterClientCommand; +import es.princip.getp.application.client.dto.command.RegisterClientCommand; import es.princip.getp.domain.client.model.Address; -import es.princip.getp.domain.client.model.BankAccount; import es.princip.getp.domain.common.model.Email; import es.princip.getp.domain.common.model.EmailPattern; import es.princip.getp.domain.common.model.PhoneNumber; @@ -17,18 +16,16 @@ public record RegisterMyClientRequest( @NotBlank String nickname, // 필수 @EmailPattern String email, // 선택, 미입력 시 회원 가입 시 작성한 이메일 주소가 기본값 @NotNull @PhoneNumberPattern String phoneNumber, // 필수 - @Valid Address address, // 선택 - @Valid BankAccount bankAccount // 선택 + @Valid Address address // 선택 ) { public RegisterClientCommand toCommand(final Member member) { return new RegisterClientCommand( member, Nickname.from(nickname()), - email() == null ? null : Email.from(email()), + email() == null ? member.getEmail() : Email.from(email()), PhoneNumber.from(phoneNumber()), - address, - bankAccount + address ); } } \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/client/query/ClientQueryController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/client/query/ClientQueryController.java similarity index 88% rename from src/main/java/es/princip/getp/api/controller/client/query/ClientQueryController.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/client/query/ClientQueryController.java index 0d3f4665..349e0a7c 100644 --- a/src/main/java/es/princip/getp/api/controller/client/query/ClientQueryController.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/client/query/ClientQueryController.java @@ -1,9 +1,10 @@ package es.princip.getp.api.controller.client.query; -import es.princip.getp.api.controller.client.query.dto.ClientResponse; +import es.princip.getp.application.client.dto.response.ClientResponse; import es.princip.getp.api.support.dto.ApiResponse; import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; import es.princip.getp.application.client.port.out.ClientQuery; +import es.princip.getp.domain.client.model.ClientId; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -29,7 +30,8 @@ public class ClientQueryController { @GetMapping("/{clientId}") @PreAuthorize("(hasRole('ADMIN') or hasRole('MANAGER')) and isAuthenticated()") public ResponseEntity> getClient(@PathVariable final Long clientId) { - final ClientResponse response = clientQuery.findClientById(clientId); + final ClientId id = new ClientId(clientId); + final ClientResponse response = clientQuery.findClientBy(id); return ApiResponse.success(HttpStatus.OK, response); } } \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/client/query/MyClientQueryController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/client/query/MyClientQueryController.java similarity index 83% rename from src/main/java/es/princip/getp/api/controller/client/query/MyClientQueryController.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/client/query/MyClientQueryController.java index e0b6094c..22a76249 100644 --- a/src/main/java/es/princip/getp/api/controller/client/query/MyClientQueryController.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/client/query/MyClientQueryController.java @@ -1,10 +1,11 @@ package es.princip.getp.api.controller.client.query; -import es.princip.getp.api.controller.client.query.dto.ClientResponse; +import es.princip.getp.application.client.dto.response.ClientResponse; +import es.princip.getp.application.auth.service.PrincipalDetails; import es.princip.getp.api.support.dto.ApiResponse; import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; -import es.princip.getp.api.security.details.PrincipalDetails; import es.princip.getp.application.client.port.out.ClientQuery; +import es.princip.getp.domain.member.model.MemberId; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -30,8 +31,8 @@ public class MyClientQueryController { @PreAuthorize("hasRole('CLIENT') and isAuthenticated()") public ResponseEntity> getMyClient( @AuthenticationPrincipal final PrincipalDetails principalDetails) { - final Long memberId = principalDetails.getMember().getMemberId(); - final ClientResponse response = clientQuery.findClientByMemberId(memberId); + final MemberId memberId = principalDetails.getMember().getId(); + final ClientResponse response = clientQuery.findClientBy(memberId); return ApiResponse.success(HttpStatus.OK, response); } } \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/common/mapper/HashtagMapper.java b/get-p-api/src/main/java/es/princip/getp/api/controller/common/mapper/HashtagMapper.java similarity index 94% rename from src/main/java/es/princip/getp/api/controller/common/mapper/HashtagMapper.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/common/mapper/HashtagMapper.java index 20fcf569..01de6511 100644 --- a/src/main/java/es/princip/getp/api/controller/common/mapper/HashtagMapper.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/common/mapper/HashtagMapper.java @@ -3,7 +3,6 @@ import es.princip.getp.domain.common.model.Hashtag; import org.mapstruct.Mapper; -@CommandMapper @Mapper(componentModel = "spring") public interface HashtagMapper { diff --git a/src/main/java/es/princip/getp/api/controller/common/mapper/PhoneNumberMapper.java b/get-p-api/src/main/java/es/princip/getp/api/controller/common/mapper/PhoneNumberMapper.java similarity index 94% rename from src/main/java/es/princip/getp/api/controller/common/mapper/PhoneNumberMapper.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/common/mapper/PhoneNumberMapper.java index 35aaba03..0b80a276 100644 --- a/src/main/java/es/princip/getp/api/controller/common/mapper/PhoneNumberMapper.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/common/mapper/PhoneNumberMapper.java @@ -3,7 +3,6 @@ import es.princip.getp.domain.common.model.PhoneNumber; import org.mapstruct.Mapper; -@CommandMapper @Mapper(componentModel = "spring") public interface PhoneNumberMapper { diff --git a/src/main/java/es/princip/getp/api/controller/common/mapper/TechStackMapper.java b/get-p-api/src/main/java/es/princip/getp/api/controller/common/mapper/TechStackMapper.java similarity index 94% rename from src/main/java/es/princip/getp/api/controller/common/mapper/TechStackMapper.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/common/mapper/TechStackMapper.java index 30937a7f..a880a7e7 100644 --- a/src/main/java/es/princip/getp/api/controller/common/mapper/TechStackMapper.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/common/mapper/TechStackMapper.java @@ -3,7 +3,6 @@ import es.princip.getp.domain.common.model.TechStack; import org.mapstruct.Mapper; -@CommandMapper @Mapper(componentModel = "spring") public interface TechStackMapper { diff --git a/src/main/java/es/princip/getp/api/controller/common/mapper/URLMapper.java b/get-p-api/src/main/java/es/princip/getp/api/controller/common/mapper/URLMapper.java similarity index 93% rename from src/main/java/es/princip/getp/api/controller/common/mapper/URLMapper.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/common/mapper/URLMapper.java index bb35cddf..69ee14a0 100644 --- a/src/main/java/es/princip/getp/api/controller/common/mapper/URLMapper.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/common/mapper/URLMapper.java @@ -3,7 +3,6 @@ import es.princip.getp.domain.common.model.URL; import org.mapstruct.Mapper; -@CommandMapper @Mapper(componentModel = "spring") public interface URLMapper { diff --git a/src/main/java/es/princip/getp/api/controller/like/command/PeopleLikeController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/like/command/PeopleLikeController.java similarity index 73% rename from src/main/java/es/princip/getp/api/controller/like/command/PeopleLikeController.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/like/command/PeopleLikeController.java index bdcac361..dbe3a0bc 100644 --- a/src/main/java/es/princip/getp/api/controller/like/command/PeopleLikeController.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/like/command/PeopleLikeController.java @@ -1,10 +1,12 @@ package es.princip.getp.api.controller.like.command; +import es.princip.getp.application.auth.service.PrincipalDetails; import es.princip.getp.api.support.dto.ApiResponse; import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; -import es.princip.getp.api.security.details.PrincipalDetails; import es.princip.getp.application.like.people.port.in.LikePeopleUseCase; import es.princip.getp.application.like.people.port.in.UnlikePeopleUseCase; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.model.PeopleId; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -28,12 +30,13 @@ public class PeopleLikeController { */ @PostMapping("/{peopleId}/likes") @PreAuthorize("hasRole('CLIENT') and isAuthenticated()") - public ResponseEntity> like( + public ResponseEntity> likePeople( @PathVariable final Long peopleId, @AuthenticationPrincipal final PrincipalDetails principalDetails ) { - final Long memberId = principalDetails.getMember().getMemberId(); - likePeopleUseCase.like(memberId, peopleId); + final MemberId memberId = principalDetails.getMember().getId(); + final PeopleId pid = new PeopleId(peopleId); + likePeopleUseCase.like(memberId, pid); return ApiResponse.success(HttpStatus.CREATED); } @@ -45,12 +48,13 @@ public ResponseEntity> like( */ @DeleteMapping("/{peopleId}/likes") @PreAuthorize("hasRole('CLIENT') and isAuthenticated()") - public ResponseEntity> unlike( + public ResponseEntity> unlikePeople( @PathVariable final Long peopleId, @AuthenticationPrincipal final PrincipalDetails principalDetails ) { - final Long memberId = principalDetails.getMember().getMemberId(); - unlikePeopleUseCase.unlike(memberId, peopleId); + final MemberId memberId = principalDetails.getMember().getId(); + final PeopleId pid = new PeopleId(peopleId); + unlikePeopleUseCase.unlike(memberId, pid); return ApiResponse.success(HttpStatus.NO_CONTENT); } } diff --git a/src/main/java/es/princip/getp/api/controller/like/command/ProjectLikeController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/like/command/ProjectLikeController.java similarity index 78% rename from src/main/java/es/princip/getp/api/controller/like/command/ProjectLikeController.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/like/command/ProjectLikeController.java index 1e2256a9..516a70b7 100644 --- a/src/main/java/es/princip/getp/api/controller/like/command/ProjectLikeController.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/like/command/ProjectLikeController.java @@ -1,10 +1,12 @@ package es.princip.getp.api.controller.like.command; +import es.princip.getp.application.auth.service.PrincipalDetails; import es.princip.getp.api.support.dto.ApiResponse; import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; -import es.princip.getp.api.security.details.PrincipalDetails; import es.princip.getp.application.like.project.port.in.LikeProjectUseCase; import es.princip.getp.application.like.project.port.in.UnlikeProjectUseCase; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.project.commission.model.ProjectId; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -32,8 +34,9 @@ public ResponseEntity> likeProject( @PathVariable final Long projectId, @AuthenticationPrincipal final PrincipalDetails principalDetails ) { - final Long memberId = principalDetails.getMember().getMemberId(); - likeProjectUseCase.like(projectId, memberId); + final MemberId memberId = principalDetails.getMember().getId(); + final ProjectId pid = new ProjectId(projectId); + likeProjectUseCase.like(memberId, pid); return ApiResponse.success(HttpStatus.CREATED); } @@ -49,8 +52,9 @@ public ResponseEntity> unlikeProject( @PathVariable final Long projectId, @AuthenticationPrincipal final PrincipalDetails principalDetails ) { - final Long memberId = principalDetails.getMember().getMemberId(); - unlikeProjectUseCase.unlike(projectId, memberId); + final MemberId memberId = principalDetails.getMember().getId(); + final ProjectId pid = new ProjectId(projectId); + unlikeProjectUseCase.unlike(memberId, pid); return ApiResponse.success(HttpStatus.NO_CONTENT); } } diff --git a/src/main/java/es/princip/getp/api/controller/member/command/MyMemberController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/member/command/MyMemberController.java similarity index 84% rename from src/main/java/es/princip/getp/api/controller/member/command/MyMemberController.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/member/command/MyMemberController.java index 5f3fae61..0a0fe763 100644 --- a/src/main/java/es/princip/getp/api/controller/member/command/MyMemberController.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/member/command/MyMemberController.java @@ -1,11 +1,12 @@ package es.princip.getp.api.controller.member.command; +import es.princip.getp.application.member.dto.response.ProfileImageResponse; +import es.princip.getp.application.auth.service.PrincipalDetails; import es.princip.getp.api.support.dto.ApiResponse; import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; -import es.princip.getp.api.controller.member.command.dto.response.ProfileImageResponse; -import es.princip.getp.api.security.details.PrincipalDetails; -import es.princip.getp.application.member.command.RegisterProfileImageCommand; +import es.princip.getp.application.member.dto.command.RegisterProfileImageCommand; import es.princip.getp.application.member.port.in.ProfileImageUseCase; +import es.princip.getp.domain.member.model.MemberId; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -37,7 +38,7 @@ public ResponseEntity> uploadProfileImage @AuthenticationPrincipal final PrincipalDetails principalDetails, @RequestPart final MultipartFile image ) { - final Long memberId = principalDetails.getMember().getMemberId(); + final MemberId memberId = principalDetails.getMember().getId(); final RegisterProfileImageCommand command = new RegisterProfileImageCommand(memberId, image); final String profileImageUri = profileImageUseCase.registerProfileImage(command); final ProfileImageResponse response = new ProfileImageResponse(profileImageUri); diff --git a/src/main/java/es/princip/getp/api/controller/member/query/MyMemberQueryController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/member/query/MyMemberQueryController.java similarity index 88% rename from src/main/java/es/princip/getp/api/controller/member/query/MyMemberQueryController.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/member/query/MyMemberQueryController.java index 8085cd8f..cef2e57f 100644 --- a/src/main/java/es/princip/getp/api/controller/member/query/MyMemberQueryController.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/member/query/MyMemberQueryController.java @@ -1,8 +1,8 @@ package es.princip.getp.api.controller.member.query; import es.princip.getp.api.support.dto.ApiResponse; -import es.princip.getp.api.controller.member.query.dto.response.MemberResponse; -import es.princip.getp.api.security.details.PrincipalDetails; +import es.princip.getp.application.member.dto.response.MemberResponse; +import es.princip.getp.application.auth.service.PrincipalDetails; import es.princip.getp.domain.member.model.Member; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; diff --git a/src/main/java/es/princip/getp/api/controller/people/command/MyPeopleController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/people/command/MyPeopleController.java similarity index 77% rename from src/main/java/es/princip/getp/api/controller/people/command/MyPeopleController.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/people/command/MyPeopleController.java index d1a64e31..9cb953b2 100644 --- a/src/main/java/es/princip/getp/api/controller/people/command/MyPeopleController.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/people/command/MyPeopleController.java @@ -1,15 +1,17 @@ package es.princip.getp.api.controller.people.command; -import es.princip.getp.api.support.dto.ApiResponse; -import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; import es.princip.getp.api.controller.people.command.dto.request.EditPeopleRequest; import es.princip.getp.api.controller.people.command.dto.request.RegisterPeopleRequest; -import es.princip.getp.api.controller.people.command.dto.response.RegisterPeopleResponse; -import es.princip.getp.api.security.details.PrincipalDetails; -import es.princip.getp.application.people.command.EditPeopleCommand; -import es.princip.getp.application.people.command.RegisterPeopleCommand; +import es.princip.getp.application.people.dto.response.people.RegisterPeopleResponse; +import es.princip.getp.application.auth.service.PrincipalDetails; +import es.princip.getp.api.support.dto.ApiResponse; +import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; +import es.princip.getp.application.people.dto.command.EditPeopleCommand; +import es.princip.getp.application.people.dto.command.RegisterPeopleCommand; import es.princip.getp.application.people.port.in.EditPeopleUseCase; import es.princip.getp.application.people.port.in.RegisterPeopleUseCase; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @@ -33,12 +35,12 @@ public class MyPeopleController { */ @PostMapping @PreAuthorize("hasRole('PEOPLE') and isAuthenticated()") - public ResponseEntity> createMyPeople( + public ResponseEntity> registerMyPeople( @RequestBody @Valid final RegisterPeopleRequest request, @AuthenticationPrincipal final PrincipalDetails principalDetails) { - final Long memberId = principalDetails.getMember().getMemberId(); - final RegisterPeopleCommand command = request.toCommand(memberId); - final Long peopleId = registerPeopleUseCase.register(command); + final Member member = principalDetails.getMember(); + final RegisterPeopleCommand command = request.toCommand(member); + final Long peopleId = registerPeopleUseCase.register(command).getValue(); final RegisterPeopleResponse response = new RegisterPeopleResponse(peopleId); return ApiResponse.success(HttpStatus.CREATED, response); } @@ -50,10 +52,10 @@ public ResponseEntity> createMyPeople( */ @PutMapping @PreAuthorize("hasRole('PEOPLE') and isAuthenticated()") - public ResponseEntity> updateMyPeople( + public ResponseEntity> editMyPeople( @RequestBody @Valid final EditPeopleRequest request, @AuthenticationPrincipal final PrincipalDetails principalDetails) { - final Long memberId = principalDetails.getMember().getMemberId(); + final MemberId memberId = principalDetails.getMember().getId(); final EditPeopleCommand command = request.toCommand(memberId); editPeopleUseCase.edit(command); return ApiResponse.success(HttpStatus.CREATED); diff --git a/src/main/java/es/princip/getp/api/controller/people/command/MyPeopleProfileController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/people/command/MyPeopleProfileController.java similarity index 81% rename from src/main/java/es/princip/getp/api/controller/people/command/MyPeopleProfileController.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/people/command/MyPeopleProfileController.java index b200441b..b5e03a62 100644 --- a/src/main/java/es/princip/getp/api/controller/people/command/MyPeopleProfileController.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/people/command/MyPeopleProfileController.java @@ -1,14 +1,15 @@ package es.princip.getp.api.controller.people.command; -import es.princip.getp.api.support.dto.ApiResponse; -import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; import es.princip.getp.api.controller.people.command.dto.request.EditPeopleProfileRequest; import es.princip.getp.api.controller.people.command.dto.request.RegisterPeopleProfileRequest; -import es.princip.getp.api.security.details.PrincipalDetails; -import es.princip.getp.application.people.command.EditPeopleProfileCommand; -import es.princip.getp.application.people.command.RegisterPeopleProfileCommand; +import es.princip.getp.application.auth.service.PrincipalDetails; +import es.princip.getp.api.support.dto.ApiResponse; +import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; +import es.princip.getp.application.people.dto.command.EditPeopleProfileCommand; +import es.princip.getp.application.people.dto.command.RegisterPeopleProfileCommand; import es.princip.getp.application.people.port.in.EditPeopleProfileUseCase; import es.princip.getp.application.people.port.in.RegisterPeopleProfileUseCase; +import es.princip.getp.domain.member.model.MemberId; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @@ -35,11 +36,11 @@ public class MyPeopleProfileController { */ @PostMapping @PreAuthorize("hasRole('PEOPLE') and isAuthenticated()") - public ResponseEntity> createMyPeopleProfile( + public ResponseEntity> registerMyPeopleProfile( @RequestBody @Valid final RegisterPeopleProfileRequest request, @AuthenticationPrincipal final PrincipalDetails principalDetails ) { - final Long memberId = principalDetails.getMember().getMemberId(); + final MemberId memberId = principalDetails.getMember().getId(); final RegisterPeopleProfileCommand command = peopleCommandMapper.mapToCommand(memberId, request); registerPeopleProfileUseCase.register(command); return ApiResponse.success(HttpStatus.CREATED); @@ -53,11 +54,11 @@ public ResponseEntity> createMyPeopleProfile( */ @PutMapping @PreAuthorize("hasRole('PEOPLE') and isAuthenticated()") - public ResponseEntity> updateMyPeopleProfile( + public ResponseEntity> editMyPeopleProfile( @RequestBody @Valid final EditPeopleProfileRequest request, @AuthenticationPrincipal final PrincipalDetails principalDetails ) { - final Long memberId = principalDetails.getMember().getMemberId(); + final MemberId memberId = principalDetails.getMember().getId(); final EditPeopleProfileCommand command = peopleCommandMapper.mapToCommand(memberId, request); editPeopleProfileUseCase.edit(command); return ApiResponse.success(HttpStatus.OK); diff --git a/src/main/java/es/princip/getp/api/controller/people/command/PeopleCommandMapper.java b/get-p-api/src/main/java/es/princip/getp/api/controller/people/command/PeopleCommandMapper.java similarity index 59% rename from src/main/java/es/princip/getp/api/controller/people/command/PeopleCommandMapper.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/people/command/PeopleCommandMapper.java index 2292e1fe..2398e447 100644 --- a/src/main/java/es/princip/getp/api/controller/people/command/PeopleCommandMapper.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/people/command/PeopleCommandMapper.java @@ -1,24 +1,28 @@ package es.princip.getp.api.controller.people.command; -import es.princip.getp.api.controller.common.mapper.CommandMapper; import es.princip.getp.api.controller.common.mapper.HashtagMapper; import es.princip.getp.api.controller.common.mapper.TechStackMapper; import es.princip.getp.api.controller.common.mapper.URLMapper; import es.princip.getp.api.controller.people.command.dto.request.EditPeopleProfileRequest; import es.princip.getp.api.controller.people.command.dto.request.PortfolioRequest; import es.princip.getp.api.controller.people.command.dto.request.RegisterPeopleProfileRequest; -import es.princip.getp.application.people.command.EditPeopleProfileCommand; -import es.princip.getp.application.people.command.RegisterPeopleProfileCommand; +import es.princip.getp.application.people.dto.command.EditPeopleProfileCommand; +import es.princip.getp.application.people.dto.command.RegisterPeopleProfileCommand; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.model.PeopleId; import es.princip.getp.domain.people.model.Portfolio; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; -@CommandMapper @Mapper(componentModel = "spring", uses = {HashtagMapper.class, TechStackMapper.class, URLMapper.class}) public interface PeopleCommandMapper { - RegisterPeopleProfileCommand mapToCommand(Long memberId, RegisterPeopleProfileRequest request); + @Mapping(source = "value", target = "value") + PeopleId mapToPeopleId(Long value); - EditPeopleProfileCommand mapToCommand(Long memberId, EditPeopleProfileRequest request); + RegisterPeopleProfileCommand mapToCommand(MemberId memberId, RegisterPeopleProfileRequest request); + + EditPeopleProfileCommand mapToCommand(MemberId memberId, EditPeopleProfileRequest request); Portfolio mapToPortfolio(PortfolioRequest request); } diff --git a/src/main/java/es/princip/getp/api/controller/people/command/dto/request/EditPeopleProfileRequest.java b/get-p-api/src/main/java/es/princip/getp/api/controller/people/command/dto/request/EditPeopleProfileRequest.java similarity index 68% rename from src/main/java/es/princip/getp/api/controller/people/command/dto/request/EditPeopleProfileRequest.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/people/command/dto/request/EditPeopleProfileRequest.java index 5b198c64..7469db4c 100644 --- a/src/main/java/es/princip/getp/api/controller/people/command/dto/request/EditPeopleProfileRequest.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/people/command/dto/request/EditPeopleProfileRequest.java @@ -10,9 +10,9 @@ public record EditPeopleProfileRequest( @NotNull @Valid Education education, @NotBlank String activityArea, - @NotBlank String introduction, - @NotNull List<@NotBlank String> techStacks, - @NotNull @Valid List portfolios, - @NotNull List<@NotBlank String> hashtags + String introduction, + List<@NotBlank String> techStacks, + List<@Valid PortfolioRequest> portfolios, + List<@NotBlank String> hashtags ) { } \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/people/command/dto/request/EditPeopleRequest.java b/get-p-api/src/main/java/es/princip/getp/api/controller/people/command/dto/request/EditPeopleRequest.java similarity index 65% rename from src/main/java/es/princip/getp/api/controller/people/command/dto/request/EditPeopleRequest.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/people/command/dto/request/EditPeopleRequest.java index 658f0506..2d25ce2d 100644 --- a/src/main/java/es/princip/getp/api/controller/people/command/dto/request/EditPeopleRequest.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/people/command/dto/request/EditPeopleRequest.java @@ -1,30 +1,27 @@ package es.princip.getp.api.controller.people.command.dto.request; -import es.princip.getp.api.validation.Enum; -import es.princip.getp.application.people.command.EditPeopleCommand; +import es.princip.getp.application.people.dto.command.EditPeopleCommand; import es.princip.getp.domain.common.model.Email; import es.princip.getp.domain.common.model.EmailPattern; import es.princip.getp.domain.common.model.PhoneNumber; import es.princip.getp.domain.common.model.PhoneNumberPattern; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.member.model.Nickname; -import es.princip.getp.domain.people.model.PeopleType; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; public record EditPeopleRequest( @NotBlank String nickname, @NotNull @EmailPattern String email, - @NotNull @PhoneNumberPattern String phoneNumber, - @Enum PeopleType peopleType + @NotNull @PhoneNumberPattern String phoneNumber ) { - public EditPeopleCommand toCommand(Long memberId) { + public EditPeopleCommand toCommand(MemberId memberId) { return new EditPeopleCommand( memberId, Nickname.from(nickname), Email.from(email), - PhoneNumber.from(phoneNumber), - peopleType + PhoneNumber.from(phoneNumber) ); } } \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/people/command/dto/request/PortfolioRequest.java b/get-p-api/src/main/java/es/princip/getp/api/controller/people/command/dto/request/PortfolioRequest.java similarity index 100% rename from src/main/java/es/princip/getp/api/controller/people/command/dto/request/PortfolioRequest.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/people/command/dto/request/PortfolioRequest.java diff --git a/src/main/java/es/princip/getp/api/controller/people/command/dto/request/RegisterPeopleProfileRequest.java b/get-p-api/src/main/java/es/princip/getp/api/controller/people/command/dto/request/RegisterPeopleProfileRequest.java similarity index 68% rename from src/main/java/es/princip/getp/api/controller/people/command/dto/request/RegisterPeopleProfileRequest.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/people/command/dto/request/RegisterPeopleProfileRequest.java index 3ae9267b..c5bfd772 100644 --- a/src/main/java/es/princip/getp/api/controller/people/command/dto/request/RegisterPeopleProfileRequest.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/people/command/dto/request/RegisterPeopleProfileRequest.java @@ -10,9 +10,9 @@ public record RegisterPeopleProfileRequest( @NotNull @Valid Education education, @NotBlank String activityArea, - @NotBlank String introduction, - @NotNull List<@NotBlank String> techStacks, - @NotNull @Valid List portfolios, - @NotNull List<@NotBlank String> hashtags + String introduction, + List<@NotBlank String> techStacks, + List<@Valid PortfolioRequest> portfolios, + List<@NotBlank String> hashtags ) { } \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/people/command/dto/request/RegisterPeopleRequest.java b/get-p-api/src/main/java/es/princip/getp/api/controller/people/command/dto/request/RegisterPeopleRequest.java similarity index 56% rename from src/main/java/es/princip/getp/api/controller/people/command/dto/request/RegisterPeopleRequest.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/people/command/dto/request/RegisterPeopleRequest.java index df5a3c21..7948e347 100644 --- a/src/main/java/es/princip/getp/api/controller/people/command/dto/request/RegisterPeopleRequest.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/people/command/dto/request/RegisterPeopleRequest.java @@ -1,30 +1,27 @@ package es.princip.getp.api.controller.people.command.dto.request; -import es.princip.getp.api.validation.Enum; -import es.princip.getp.application.people.command.RegisterPeopleCommand; +import es.princip.getp.application.people.dto.command.RegisterPeopleCommand; import es.princip.getp.domain.common.model.Email; import es.princip.getp.domain.common.model.EmailPattern; import es.princip.getp.domain.common.model.PhoneNumber; import es.princip.getp.domain.common.model.PhoneNumberPattern; +import es.princip.getp.domain.member.model.Member; import es.princip.getp.domain.member.model.Nickname; -import es.princip.getp.domain.people.model.PeopleType; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; public record RegisterPeopleRequest( @NotBlank String nickname, - @NotNull @EmailPattern String email, - @NotNull @PhoneNumberPattern String phoneNumber, - @Enum PeopleType peopleType + @EmailPattern String email, + @NotNull @PhoneNumberPattern String phoneNumber ) { - public RegisterPeopleCommand toCommand(final Long memberId) { + public RegisterPeopleCommand toCommand(final Member member) { return new RegisterPeopleCommand( - memberId, + member.getId(), Nickname.from(nickname), - Email.from(email), - PhoneNumber.from(phoneNumber), - peopleType + email == null ? member.getEmail() : Email.from(email), + PhoneNumber.from(phoneNumber) ); } } \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/people/query/MyPeopleProfileQueryController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/people/query/MyPeopleProfileQueryController.java similarity index 73% rename from src/main/java/es/princip/getp/api/controller/people/query/MyPeopleProfileQueryController.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/people/query/MyPeopleProfileQueryController.java index 8ad54a34..42f8ef95 100644 --- a/src/main/java/es/princip/getp/api/controller/people/query/MyPeopleProfileQueryController.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/people/query/MyPeopleProfileQueryController.java @@ -1,10 +1,11 @@ package es.princip.getp.api.controller.people.query; +import es.princip.getp.application.people.dto.response.peopleProfile.PeopleProfileDetailResponse; +import es.princip.getp.application.auth.service.PrincipalDetails; import es.princip.getp.api.support.dto.ApiResponse; import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; -import es.princip.getp.api.controller.people.query.dto.peopleProfile.DetailPeopleProfileResponse; -import es.princip.getp.api.security.details.PrincipalDetails; import es.princip.getp.application.people.port.in.GetMyPeopleQuery; +import es.princip.getp.domain.member.model.MemberId; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -28,10 +29,10 @@ public class MyPeopleProfileQueryController { */ @GetMapping @PreAuthorize("hasRole('PEOPLE') and isAuthenticated()") - public ResponseEntity> getMyPeopleProfile( + public ResponseEntity> getMyPeopleProfile( @AuthenticationPrincipal final PrincipalDetails principalDetails) { - final Long memberId = principalDetails.getMember().getMemberId(); - final DetailPeopleProfileResponse response = getMyPeopleQuery.getDetailProfileByMemberId(memberId); + final MemberId memberId = principalDetails.getMember().getId(); + final PeopleProfileDetailResponse response = getMyPeopleQuery.getDetailProfileBy(memberId); return ApiResponse.success(HttpStatus.OK, response); } } \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/people/query/MyPeopleQueryController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/people/query/MyPeopleQueryController.java similarity index 83% rename from src/main/java/es/princip/getp/api/controller/people/query/MyPeopleQueryController.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/people/query/MyPeopleQueryController.java index eb2d6e33..913fb7eb 100644 --- a/src/main/java/es/princip/getp/api/controller/people/query/MyPeopleQueryController.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/people/query/MyPeopleQueryController.java @@ -1,10 +1,11 @@ package es.princip.getp.api.controller.people.query; +import es.princip.getp.application.people.dto.response.people.MyPeopleResponse; +import es.princip.getp.application.auth.service.PrincipalDetails; import es.princip.getp.api.support.dto.ApiResponse; import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; -import es.princip.getp.api.controller.people.query.dto.people.MyPeopleResponse; -import es.princip.getp.api.security.details.PrincipalDetails; import es.princip.getp.application.people.port.in.GetMyPeopleQuery; +import es.princip.getp.domain.member.model.MemberId; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -30,8 +31,8 @@ public class MyPeopleQueryController { @PreAuthorize("hasRole('PEOPLE') and isAuthenticated()") public ResponseEntity> getMyPeople( @AuthenticationPrincipal final PrincipalDetails principalDetails) { - final Long memberId = principalDetails.getMember().getMemberId(); - final MyPeopleResponse response = getMyPeopleQuery.getByMemberId(memberId); + final MemberId memberId = principalDetails.getMember().getId(); + final MyPeopleResponse response = getMyPeopleQuery.getBy(memberId); return ApiResponse.success(HttpStatus.OK, response); } } \ No newline at end of file diff --git a/get-p-api/src/main/java/es/princip/getp/api/controller/people/query/PeopleQueryController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/people/query/PeopleQueryController.java new file mode 100644 index 00000000..3438f7bf --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/people/query/PeopleQueryController.java @@ -0,0 +1,71 @@ +package es.princip.getp.api.controller.people.query; + +import es.princip.getp.api.support.ControllerSupport; +import es.princip.getp.api.support.dto.ApiResponse; +import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; +import es.princip.getp.application.auth.service.PrincipalDetails; +import es.princip.getp.application.people.dto.command.GetPeopleCommand; +import es.princip.getp.application.people.dto.command.PeopleSearchFilter; +import es.princip.getp.application.people.dto.response.people.CardPeopleResponse; +import es.princip.getp.application.people.dto.response.people.PeopleDetailResponse; +import es.princip.getp.application.people.port.in.GetPeopleQuery; +import es.princip.getp.application.support.dto.PageResponse; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.people.model.PeopleId; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; + +import java.util.Optional; + +@RestController +@RequestMapping("/people") +@RequiredArgsConstructor +public class PeopleQueryController extends ControllerSupport { + + private final GetPeopleQuery getPeopleQuery; + + /** + * 피플 상세 조회 + * + * @param peopleId 피플 ID + * @return 피플 ID에 해당되는 피플 상세 정보 + */ + @GetMapping("/{peopleId}") + public ResponseEntity> getPeople( + @AuthenticationPrincipal final PrincipalDetails principalDetails, + @PathVariable final Long peopleId + ) { + final PeopleId pid = new PeopleId(peopleId); + final Member member = Optional.ofNullable(principalDetails) + .map(PrincipalDetails::getMember) + .orElse(null); + final PeopleDetailResponse response = getPeopleQuery.getDetailBy(member, pid); + return ApiResponse.success(HttpStatus.OK, response); + } + + /** + * 피플 목록 조회 + * + * @param pageable 정렬 기준 + * @return 정렬 기준에 해당되는 피플 정보 목록 + */ + @GetMapping + public ResponseEntity>> getPeopleList( + @PageableDefault(sort = "peopleId", direction = Sort.Direction.DESC) final Pageable pageable, + @ModelAttribute final PeopleSearchFilter filter, + @AuthenticationPrincipal final PrincipalDetails principalDetails + ) { + final Member member = Optional.ofNullable(principalDetails) + .map(PrincipalDetails::getMember) + .orElse(null); + final GetPeopleCommand command = new GetPeopleCommand(pageable, filter, member); + final PageResponse response = PageResponse.from(getPeopleQuery.getPagedCards(command)); + return ApiResponse.success(HttpStatus.OK, response); + } +} \ No newline at end of file diff --git a/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/ApproveTeammateController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/ApproveTeammateController.java new file mode 100644 index 00000000..ca405bd0 --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/ApproveTeammateController.java @@ -0,0 +1,26 @@ +package es.princip.getp.api.controller.project.command; + +import es.princip.getp.api.support.dto.ApiResponse; +import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; +import es.princip.getp.application.project.apply.port.in.ApproveTeammateUseCase; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/teammates") +public class ApproveTeammateController { + + private final ApproveTeammateUseCase approveTeammateUseCase; + + @GetMapping("/approve") + public ResponseEntity> approveTeammate(@RequestParam("token") final String token) { + approveTeammateUseCase.approve(token); + return ApiResponse.success(HttpStatus.OK); + } +} diff --git a/src/main/java/es/princip/getp/api/controller/project/command/ProjectApplicationController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/ProjectApplicationController.java similarity index 67% rename from src/main/java/es/princip/getp/api/controller/project/command/ProjectApplicationController.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/project/command/ProjectApplicationController.java index 2718ca4f..67901a6d 100644 --- a/src/main/java/es/princip/getp/api/controller/project/command/ProjectApplicationController.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/ProjectApplicationController.java @@ -1,12 +1,15 @@ package es.princip.getp.api.controller.project.command; +import es.princip.getp.api.controller.project.command.dto.request.ApplyProjectRequest; +import es.princip.getp.application.project.apply.dto.response.ApplyProjectResponse; +import es.princip.getp.application.auth.service.PrincipalDetails; import es.princip.getp.api.support.dto.ApiResponse; import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; -import es.princip.getp.api.controller.project.command.dto.request.ApplyProjectRequest; -import es.princip.getp.api.controller.project.command.dto.response.ApplyProjectResponse; -import es.princip.getp.api.security.details.PrincipalDetails; -import es.princip.getp.application.project.apply.command.ApplyProjectCommand; +import es.princip.getp.application.project.apply.dto.command.ApplyProjectCommand; import es.princip.getp.application.project.apply.port.in.ApplyProjectUseCase; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; +import es.princip.getp.domain.project.commission.model.ProjectId; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @@ -23,24 +26,18 @@ public class ProjectApplicationController { private final ApplyProjectUseCase applyProjectUseCase; private final ProjectCommandMapper projectCommandMapper; - /** - * 프로젝트 지원 - * - * @param request 프로젝트 지원 요청 - * @param principalDetails 로그인한 사용자 정보 - * @param projectId 프로젝트 ID - */ @PostMapping("/{projectId}/applications") @PreAuthorize("hasRole('PEOPLE') and isAuthenticated()") - public ResponseEntity> applyForProject( + public ResponseEntity> applyProject( @RequestBody @Valid final ApplyProjectRequest request, @AuthenticationPrincipal final PrincipalDetails principalDetails, @PathVariable Long projectId ) { - final Long memberId = principalDetails.getMember().getMemberId(); - final ApplyProjectCommand command = projectCommandMapper.mapToCommand(memberId, projectId, request); - final Long applicationId = applyProjectUseCase.apply(command); - final ApplyProjectResponse response = new ApplyProjectResponse(applicationId); + final Member member = principalDetails.getMember(); + final ProjectId pid = new ProjectId(projectId); + final ApplyProjectCommand command = projectCommandMapper.mapToCommand(member, pid, request); + final ProjectApplicationId applicationId = applyProjectUseCase.apply(command); + final ApplyProjectResponse response = new ApplyProjectResponse(applicationId.getValue()); return ApiResponse.success(HttpStatus.CREATED, response); } } diff --git a/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/ProjectCommandMapper.java b/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/ProjectCommandMapper.java new file mode 100644 index 00000000..b330f01d --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/ProjectCommandMapper.java @@ -0,0 +1,73 @@ +package es.princip.getp.api.controller.project.command; + +import es.princip.getp.api.controller.common.mapper.HashtagMapper; +import es.princip.getp.api.controller.common.mapper.PhoneNumberMapper; +import es.princip.getp.api.controller.common.mapper.URLMapper; +import es.princip.getp.api.controller.people.command.PeopleCommandMapper; +import es.princip.getp.api.controller.project.command.dto.request.*; +import es.princip.getp.application.project.apply.dto.command.ApplyProjectAsIndividualCommand; +import es.princip.getp.application.project.apply.dto.command.ApplyProjectAsTeamCommand; +import es.princip.getp.application.project.apply.dto.command.ApplyProjectCommand; +import es.princip.getp.application.project.commission.dto.command.CommissionProjectCommand; +import es.princip.getp.application.project.meeting.dto.command.ScheduleMeetingCommand; +import es.princip.getp.domain.common.model.AttachmentFile; +import es.princip.getp.domain.common.model.URL; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.project.commission.model.ProjectId; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper( + componentModel = "spring", + uses = {URLMapper.class, HashtagMapper.class, PhoneNumberMapper.class, PeopleCommandMapper.class} +) +abstract class ProjectCommandMapper { + + ApplyProjectCommand mapToCommand( + Member member, + ProjectId projectId, + ApplyProjectRequest request + ) { + if (request instanceof ApplyProjectAsIndividualRequest req) { + return mapToCommand(member, projectId, req); + } else if (request instanceof ApplyProjectAsTeamRequest req) { + return mapToCommand(member, projectId, req); + } else { + throw new IllegalArgumentException("올바르지 않은 프로젝트 지원 유형: " + request.getClass()); + } + } + + @Mapping(source = "request.expectedDuration", target = "expectedDuration") + @Mapping(source = "request.description", target = "description") + @Mapping(source = "request.attachmentFiles", target = "attachmentFiles") + protected abstract ApplyProjectAsIndividualCommand mapToCommand( + Member member, + ProjectId projectId, + ApplyProjectAsIndividualRequest request + ); + + @Mapping(source = "request.expectedDuration", target = "expectedDuration") + @Mapping(source = "request.description", target = "description") + @Mapping(source = "request.attachmentFiles", target = "attachmentFiles") + @Mapping(source = "request.teammates", target = "teammates") + protected abstract ApplyProjectAsTeamCommand mapToCommand( + Member member, + ProjectId projectId, + ApplyProjectAsTeamRequest request + ); + + protected abstract AttachmentFile mapToAttachmentFile(URL url); + + abstract CommissionProjectCommand mapToCommand( + MemberId memberId, + CommissionProjectRequest request + ); + + @Mapping(source = "request.applicantId", target = "applicantId.value") + abstract ScheduleMeetingCommand mapToCommand( + MemberId memberId, + ProjectId projectId, + ScheduleMeetingRequest request + ); +} diff --git a/src/main/java/es/princip/getp/api/controller/project/command/ProjectCommissionController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/ProjectCommissionController.java similarity index 78% rename from src/main/java/es/princip/getp/api/controller/project/command/ProjectCommissionController.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/project/command/ProjectCommissionController.java index b0cf8d7a..4aa11f27 100644 --- a/src/main/java/es/princip/getp/api/controller/project/command/ProjectCommissionController.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/ProjectCommissionController.java @@ -1,12 +1,14 @@ package es.princip.getp.api.controller.project.command; +import es.princip.getp.api.controller.project.command.dto.request.CommissionProjectRequest; +import es.princip.getp.application.project.commission.dto.response.CommissionProjectResponse; +import es.princip.getp.application.auth.service.PrincipalDetails; import es.princip.getp.api.support.dto.ApiResponse; import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; -import es.princip.getp.api.controller.project.command.dto.request.CommissionProjectRequest; -import es.princip.getp.api.controller.project.command.dto.response.CommissionProjectResponse; -import es.princip.getp.api.security.details.PrincipalDetails; -import es.princip.getp.application.project.commission.command.CommissionProjectCommand; +import es.princip.getp.application.project.commission.dto.command.CommissionProjectCommand; import es.princip.getp.application.project.commission.port.in.CommissionProjectUseCase; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.project.commission.model.ProjectId; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @@ -38,10 +40,10 @@ public ResponseEntity> commissionPro @RequestBody @Valid final CommissionProjectRequest request, @AuthenticationPrincipal final PrincipalDetails principalDetails ) { - final Long memberId = principalDetails.getMember().getMemberId(); + final MemberId memberId = principalDetails.getMember().getId(); final CommissionProjectCommand command = projectCommandMapper.mapToCommand(memberId, request); - final Long projectId = commissionProjectUseCase.commission(command); - final CommissionProjectResponse response = new CommissionProjectResponse(projectId); + final ProjectId projectId = commissionProjectUseCase.commission(command); + final CommissionProjectResponse response = new CommissionProjectResponse(projectId.getValue()); return ApiResponse.success(HttpStatus.CREATED, response); } } diff --git a/src/main/java/es/princip/getp/api/controller/project/command/ProjectMeetingController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/ProjectMeetingController.java similarity index 77% rename from src/main/java/es/princip/getp/api/controller/project/command/ProjectMeetingController.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/project/command/ProjectMeetingController.java index 32d089ad..4f6e3c84 100644 --- a/src/main/java/es/princip/getp/api/controller/project/command/ProjectMeetingController.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/ProjectMeetingController.java @@ -1,12 +1,14 @@ package es.princip.getp.api.controller.project.command; +import es.princip.getp.api.controller.project.command.dto.request.ScheduleMeetingRequest; +import es.princip.getp.application.project.meeting.dto.response.ScheduleMeetingResponse; +import es.princip.getp.application.auth.service.PrincipalDetails; import es.princip.getp.api.support.dto.ApiResponse; import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; -import es.princip.getp.api.controller.project.command.dto.request.ScheduleMeetingRequest; -import es.princip.getp.api.controller.project.command.dto.response.ScheduleMeetingResponse; -import es.princip.getp.api.security.details.PrincipalDetails; import es.princip.getp.application.project.meeting.ProjectMeetingService; -import es.princip.getp.application.project.meeting.command.ScheduleMeetingCommand; +import es.princip.getp.application.project.meeting.dto.command.ScheduleMeetingCommand; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.project.commission.model.ProjectId; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @@ -21,7 +23,6 @@ public class ProjectMeetingController { private final ProjectMeetingService projectMeetingService; - private final ProjectCommandMapper projectCommandMapper; /** @@ -38,8 +39,9 @@ public ResponseEntity> scheduleMeeting @AuthenticationPrincipal final PrincipalDetails principalDetails, @PathVariable Long projectId ) { - final Long memberId = principalDetails.getMember().getMemberId(); - final ScheduleMeetingCommand command = projectCommandMapper.mapToCommand(memberId, projectId, request); + final MemberId memberId = principalDetails.getMember().getId(); + final ProjectId pid = new ProjectId(projectId); + final ScheduleMeetingCommand command = projectCommandMapper.mapToCommand(memberId, pid, request); final Long meetingId = projectMeetingService.scheduleMeeting(command); final ScheduleMeetingResponse response = new ScheduleMeetingResponse(meetingId); return ApiResponse.success(HttpStatus.CREATED, response); diff --git a/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/dto/request/ApplyProjectAsIndividualRequest.java b/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/dto/request/ApplyProjectAsIndividualRequest.java new file mode 100644 index 00000000..8838ef74 --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/dto/request/ApplyProjectAsIndividualRequest.java @@ -0,0 +1,17 @@ +package es.princip.getp.api.controller.project.command.dto.request; + +import es.princip.getp.domain.common.model.Duration; + +import java.util.List; + +public class ApplyProjectAsIndividualRequest extends ApplyProjectRequest { + + public ApplyProjectAsIndividualRequest( + final String type, + final Duration expectedDuration, + final String description, + final List attachmentFiles + ) { + super(type, expectedDuration, description, attachmentFiles); + } +} \ No newline at end of file diff --git a/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/dto/request/ApplyProjectAsTeamRequest.java b/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/dto/request/ApplyProjectAsTeamRequest.java new file mode 100644 index 00000000..1502a312 --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/dto/request/ApplyProjectAsTeamRequest.java @@ -0,0 +1,25 @@ +package es.princip.getp.api.controller.project.command.dto.request; + +import es.princip.getp.domain.common.model.Duration; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; + +import java.util.List; +import java.util.Set; + +@Getter +public class ApplyProjectAsTeamRequest extends ApplyProjectRequest { + + private final @NotNull Set<@NotNull Long> teammates; + + public ApplyProjectAsTeamRequest( + final String type, + final Duration expectedDuration, + final String description, + final List attachmentFiles, + final Set teammates + ) { + super(type, expectedDuration, description, attachmentFiles); + this.teammates = teammates; + } +} \ No newline at end of file diff --git a/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/dto/request/ApplyProjectRequest.java b/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/dto/request/ApplyProjectRequest.java new file mode 100644 index 00000000..fa5bfef1 --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/dto/request/ApplyProjectRequest.java @@ -0,0 +1,38 @@ +package es.princip.getp.api.controller.project.command.dto.request; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import es.princip.getp.domain.common.model.Duration; +import es.princip.getp.domain.project.apply.model.IndividualProjectApplication; +import es.princip.getp.domain.project.apply.model.TeamProjectApplication; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +@Getter +@RequiredArgsConstructor +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.EXISTING_PROPERTY, + property = "type", + visible = true +) +@JsonSubTypes({ + @JsonSubTypes.Type( + value = ApplyProjectAsIndividualRequest.class, + name = IndividualProjectApplication.TYPE + ), + @JsonSubTypes.Type( + value = ApplyProjectAsTeamRequest.class, + name = TeamProjectApplication.TYPE + ) +}) +public abstract class ApplyProjectRequest { + + private final @NotNull String type; + private final @NotNull Duration expectedDuration; + private final @NotNull String description; + private final @NotNull List<@NotNull String> attachmentFiles; +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/project/command/dto/request/CommissionProjectRequest.java b/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/dto/request/CommissionProjectRequest.java similarity index 85% rename from src/main/java/es/princip/getp/api/controller/project/command/dto/request/CommissionProjectRequest.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/project/command/dto/request/CommissionProjectRequest.java index d2734219..d6518546 100644 --- a/src/main/java/es/princip/getp/api/controller/project/command/dto/request/CommissionProjectRequest.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/dto/request/CommissionProjectRequest.java @@ -1,6 +1,5 @@ package es.princip.getp.api.controller.project.command.dto.request; -import es.princip.getp.api.validation.Enum; import es.princip.getp.domain.common.model.Duration; import es.princip.getp.domain.project.commission.model.MeetingType; import es.princip.getp.domain.project.commission.model.ProjectCategory; @@ -12,11 +11,12 @@ public record CommissionProjectRequest( @NotBlank String title, @NotNull Long payment, + @NotNull Long recruitmentCount, @NotNull Duration applicationDuration, @NotNull Duration estimatedDuration, @NotBlank String description, - @Enum MeetingType meetingType, - @Enum ProjectCategory category, + @NotNull MeetingType meetingType, + @NotNull ProjectCategory category, @NotNull List attachmentFiles, @NotNull List hashtags ) { diff --git a/src/main/java/es/princip/getp/api/controller/project/command/dto/request/ScheduleMeetingRequest.java b/get-p-api/src/main/java/es/princip/getp/api/controller/project/command/dto/request/ScheduleMeetingRequest.java similarity index 100% rename from src/main/java/es/princip/getp/api/controller/project/command/dto/request/ScheduleMeetingRequest.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/project/command/dto/request/ScheduleMeetingRequest.java diff --git a/get-p-api/src/main/java/es/princip/getp/api/controller/project/query/ProjectApplicantQueryController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/project/query/ProjectApplicantQueryController.java new file mode 100644 index 00000000..3fcc8760 --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/project/query/ProjectApplicantQueryController.java @@ -0,0 +1,44 @@ +package es.princip.getp.api.controller.project.query; + +import es.princip.getp.application.auth.service.PrincipalDetails; +import es.princip.getp.api.support.CursorDefault; +import es.princip.getp.api.support.dto.ApiResponse; +import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; +import es.princip.getp.application.project.apply.dto.response.ProjectApplicantResponse; +import es.princip.getp.application.project.apply.port.in.GetApplicantQuery; +import es.princip.getp.application.support.Cursor; +import es.princip.getp.application.support.CursorPageable; +import es.princip.getp.application.support.dto.SliceResponse; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.project.commission.model.ProjectId; +import lombok.RequiredArgsConstructor; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/projects") +public class ProjectApplicantQueryController { + + private final GetApplicantQuery getApplicantQuery; + + @PreAuthorize("isAuthenticated() and hasRole('CLIENT')") + @GetMapping("/{projectId}/applicants") + public ResponseEntity>> getApplicants( + @CursorDefault @PageableDefault final CursorPageable pageable, + @AuthenticationPrincipal final PrincipalDetails principalDetails, + @PathVariable final Long projectId + ) { + final Member member = principalDetails.getMember(); + final ProjectId pid = new ProjectId(projectId); + final SliceResponse response = getApplicantQuery.getApplicantsBy(pageable, member, pid); + return ApiResponse.success(HttpStatus.OK, response); + } +} diff --git a/get-p-api/src/main/java/es/princip/getp/api/controller/project/query/ProjectApplicationDetailQueryController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/project/query/ProjectApplicationDetailQueryController.java new file mode 100644 index 00000000..663db504 --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/project/query/ProjectApplicationDetailQueryController.java @@ -0,0 +1,38 @@ +package es.princip.getp.api.controller.project.query; + +import es.princip.getp.application.project.apply.dto.response.ProjectApplicationDetailResponse; +import es.princip.getp.application.auth.service.PrincipalDetails; +import es.princip.getp.api.support.dto.ApiResponse; +import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; +import es.princip.getp.application.project.apply.port.in.GetApplicationDetailQuery; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/applications/me") +public class ProjectApplicationDetailQueryController { + + private final GetApplicationDetailQuery query; + + @PreAuthorize("isAuthenticated()") + @GetMapping("/{applicationId}") + public ResponseEntity> getApplicationForm( + @AuthenticationPrincipal final PrincipalDetails principalDetails, + @PathVariable final Long applicationId + ) { + final Member member = principalDetails.getMember(); + final ProjectApplicationId aid = new ProjectApplicationId(applicationId); + final ProjectApplicationDetailResponse response = query.getApplicationDetailBy(member, aid); + return ApiResponse.success(HttpStatus.OK, response); + } +} diff --git a/get-p-api/src/main/java/es/princip/getp/api/controller/project/query/ProjectApplicationFormQueryController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/project/query/ProjectApplicationFormQueryController.java new file mode 100644 index 00000000..a1bba758 --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/project/query/ProjectApplicationFormQueryController.java @@ -0,0 +1,38 @@ +package es.princip.getp.api.controller.project.query; + +import es.princip.getp.application.project.apply.dto.response.ProjectApplicationFormResponse; +import es.princip.getp.application.auth.service.PrincipalDetails; +import es.princip.getp.api.support.dto.ApiResponse; +import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; +import es.princip.getp.application.project.apply.port.in.GetApplicationFormQuery; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/applications") +public class ProjectApplicationFormQueryController { + + private final GetApplicationFormQuery query; + + @PreAuthorize("isAuthenticated()") + @GetMapping("/{applicationId}") + public ResponseEntity> getApplicationForm( + @AuthenticationPrincipal final PrincipalDetails principalDetails, + @PathVariable final Long applicationId + ) { + final Member member = principalDetails.getMember(); + final ProjectApplicationId aid = new ProjectApplicationId(applicationId); + final ProjectApplicationFormResponse response = query.getApplicationFormBy(member, aid); + return ApiResponse.success(HttpStatus.OK, response); + } +} diff --git a/get-p-api/src/main/java/es/princip/getp/api/controller/project/query/ProjectQueryCommandMapper.java b/get-p-api/src/main/java/es/princip/getp/api/controller/project/query/ProjectQueryCommandMapper.java new file mode 100644 index 00000000..6b8ee3da --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/project/query/ProjectQueryCommandMapper.java @@ -0,0 +1,14 @@ +package es.princip.getp.api.controller.project.query; + +import es.princip.getp.application.people.dto.command.SearchTeammateCommand; +import es.princip.getp.application.support.Cursor; +import es.princip.getp.application.support.CursorPageable; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper(componentModel = "spring") +abstract class ProjectQueryCommandMapper { + + @Mapping(source = "projectId", target = "projectId.value") + abstract SearchTeammateCommand mapToCommand(Long projectId, CursorPageable pageable, String nickname); +} diff --git a/get-p-api/src/main/java/es/princip/getp/api/controller/project/query/ProjectQueryController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/project/query/ProjectQueryController.java new file mode 100644 index 00000000..ff2b1ae2 --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/project/query/ProjectQueryController.java @@ -0,0 +1,70 @@ +package es.princip.getp.api.controller.project.query; + +import es.princip.getp.application.auth.service.PrincipalDetails; +import es.princip.getp.api.support.ControllerSupport; +import es.princip.getp.api.support.dto.ApiResponse; +import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; +import es.princip.getp.application.project.commission.dto.command.GetProjectCommand; +import es.princip.getp.application.project.commission.dto.command.ProjectSearchFilter; +import es.princip.getp.application.project.commission.dto.response.ProjectCardResponse; +import es.princip.getp.application.project.commission.dto.response.ProjectDetailResponse; +import es.princip.getp.application.project.commission.port.in.GetProjectQuery; +import es.princip.getp.application.support.dto.PageResponse; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.project.commission.model.ProjectId; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; + +import java.util.Optional; + +@RestController +@RequestMapping("/projects") +@RequiredArgsConstructor +public class ProjectQueryController extends ControllerSupport{ + + private final GetProjectQuery getProjectQuery; + + /** + * 프로젝트 목록 조회 + * + * @return 프로젝트 목록 + */ + @GetMapping + public ResponseEntity>> getProjects( + @PageableDefault(sort = "projectId", direction = Sort.Direction.DESC) final Pageable pageable, + @ModelAttribute final ProjectSearchFilter filter, + @AuthenticationPrincipal final PrincipalDetails principalDetails + ) { + final Member member = Optional.ofNullable(principalDetails) + .map(PrincipalDetails::getMember) + .orElse(null); + final GetProjectCommand command = new GetProjectCommand(pageable, filter, member); + final PageResponse response = PageResponse.from(getProjectQuery.getPagedCards(command)); + return ApiResponse.success(HttpStatus.OK, response); + } + + /** + * 프로젝트 상세 조회 + * + * @param projectId 프로젝트 ID + * @return 프로젝트 + */ + @GetMapping("/{projectId}") + public ResponseEntity> getProjectByProjectId( + @AuthenticationPrincipal final PrincipalDetails principalDetails, + @PathVariable final Long projectId + ) { + final ProjectId pid = new ProjectId(projectId); + final Member member = Optional.ofNullable(principalDetails) + .map(PrincipalDetails::getMember) + .orElse(null); + final ProjectDetailResponse response = getProjectQuery.getDetailBy(member, pid); + return ApiResponse.success(HttpStatus.OK, response); + } +} diff --git a/get-p-api/src/main/java/es/princip/getp/api/controller/project/query/SearchTeammateController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/project/query/SearchTeammateController.java new file mode 100644 index 00000000..a1e47e92 --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/project/query/SearchTeammateController.java @@ -0,0 +1,37 @@ +package es.princip.getp.api.controller.project.query; + +import es.princip.getp.api.support.ControllerSupport; +import es.princip.getp.api.support.CursorDefault; +import es.princip.getp.api.support.dto.ApiResponse; +import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; +import es.princip.getp.application.people.dto.command.SearchTeammateCommand; +import es.princip.getp.application.project.apply.dto.response.SearchTeammateResponse; +import es.princip.getp.application.project.apply.port.in.SearchTeammateQuery; +import es.princip.getp.application.support.Cursor; +import es.princip.getp.application.support.CursorPageable; +import es.princip.getp.application.support.dto.SliceResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/projects") +@RequiredArgsConstructor +public class SearchTeammateController extends ControllerSupport { + + private final ProjectQueryCommandMapper mapper; + private final SearchTeammateQuery searchTeammateQuery; + + @GetMapping("/{projectId}/teammates") + public ResponseEntity>> searchTeammates( + @PathVariable final Long projectId, + @CursorDefault @PageableDefault final CursorPageable pageable, + @RequestParam(value = "nickname") final String nickname + ) { + final SearchTeammateCommand command = mapper.mapToCommand(projectId, pageable, nickname); + final SliceResponse response = searchTeammateQuery.search(command); + return ApiResponse.success(HttpStatus.OK, response); + } +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/serviceTerm/ServiceTermController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/serviceTerm/ServiceTermController.java similarity index 70% rename from src/main/java/es/princip/getp/api/controller/serviceTerm/ServiceTermController.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/serviceTerm/ServiceTermController.java index 4f8a3655..da91e62c 100644 --- a/src/main/java/es/princip/getp/api/controller/serviceTerm/ServiceTermController.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/serviceTerm/ServiceTermController.java @@ -1,7 +1,8 @@ package es.princip.getp.api.controller.serviceTerm; import es.princip.getp.api.controller.serviceTerm.dto.reqeust.ServiceTermRequest; -import es.princip.getp.api.controller.serviceTerm.dto.response.ServiceTermResponse; +import es.princip.getp.application.serviceTerm.dto.command.ServiceTermCommand; +import es.princip.getp.application.serviceTerm.dto.response.ServiceTermResponse; import es.princip.getp.application.serviceTerm.port.in.RegisterServiceTermUseCase; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; @@ -19,7 +20,12 @@ public class ServiceTermController { @PostMapping // TODO: 관리자만 접근 가능하도록 권한 제어 - public ServiceTermResponse registerServiceTerm(@RequestBody @Valid ServiceTermRequest serviceTermRequest) { - return ServiceTermResponse.from(registerServiceTermUseCase.register(serviceTermRequest)); + public ServiceTermResponse registerServiceTerm(@RequestBody @Valid ServiceTermRequest request) { + final ServiceTermCommand command = new ServiceTermCommand( + request.tag(), + request.required(), + request.revocable() + ); + return ServiceTermResponse.from(registerServiceTermUseCase.register(command)); } } diff --git a/src/main/java/es/princip/getp/api/controller/serviceTerm/dto/reqeust/ServiceTermRequest.java b/get-p-api/src/main/java/es/princip/getp/api/controller/serviceTerm/dto/reqeust/ServiceTermRequest.java similarity index 100% rename from src/main/java/es/princip/getp/api/controller/serviceTerm/dto/reqeust/ServiceTermRequest.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/serviceTerm/dto/reqeust/ServiceTermRequest.java diff --git a/src/main/java/es/princip/getp/api/controller/storage/FileStorageController.java b/get-p-api/src/main/java/es/princip/getp/api/controller/storage/FileStorageController.java similarity index 75% rename from src/main/java/es/princip/getp/api/controller/storage/FileStorageController.java rename to get-p-api/src/main/java/es/princip/getp/api/controller/storage/FileStorageController.java index b02866eb..22957267 100644 --- a/src/main/java/es/princip/getp/api/controller/storage/FileStorageController.java +++ b/get-p-api/src/main/java/es/princip/getp/api/controller/storage/FileStorageController.java @@ -1,10 +1,11 @@ package es.princip.getp.api.controller.storage; +import es.princip.getp.application.auth.service.PrincipalDetails; import es.princip.getp.api.support.dto.ApiResponse; -import es.princip.getp.api.controller.storage.dto.FileUploadResponse; -import es.princip.getp.api.security.details.PrincipalDetails; -import es.princip.getp.application.storage.command.UploadFileCommand; +import es.princip.getp.application.storage.dto.command.UploadFileCommand; +import es.princip.getp.application.storage.dto.response.UploadFileResponse; import es.princip.getp.application.storage.port.in.UploadFileUseCase; +import es.princip.getp.domain.member.model.MemberId; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -29,14 +30,14 @@ public class FileStorageController { @PostMapping("/files") @PreAuthorize("isAuthenticated()") - public ResponseEntity> uploadFile( + public ResponseEntity> uploadFile( @AuthenticationPrincipal final PrincipalDetails principalDetails, @RequestPart final MultipartFile file ) { - final Long memberId = principalDetails.getMember().getMemberId(); + final MemberId memberId = principalDetails.getMember().getId(); final UploadFileCommand command = new UploadFileCommand(memberId, file); final URI uri = uploadFileUseCase.upload(command); - final FileUploadResponse response = new FileUploadResponse(uri); + final UploadFileResponse response = new UploadFileResponse(uri); return ApiResponse.success(HttpStatus.CREATED, response); } } diff --git a/src/main/java/es/princip/getp/api/handler/AccessDeniedExceptionHandler.java b/get-p-api/src/main/java/es/princip/getp/api/handler/AccessDeniedExceptionHandler.java similarity index 100% rename from src/main/java/es/princip/getp/api/handler/AccessDeniedExceptionHandler.java rename to get-p-api/src/main/java/es/princip/getp/api/handler/AccessDeniedExceptionHandler.java diff --git a/src/main/java/es/princip/getp/api/handler/ApplicationLogicExceptionHandler.java b/get-p-api/src/main/java/es/princip/getp/api/handler/ApplicationLogicExceptionHandler.java similarity index 100% rename from src/main/java/es/princip/getp/api/handler/ApplicationLogicExceptionHandler.java rename to get-p-api/src/main/java/es/princip/getp/api/handler/ApplicationLogicExceptionHandler.java diff --git a/get-p-api/src/main/java/es/princip/getp/api/handler/AuthenticationExceptionHandler.java b/get-p-api/src/main/java/es/princip/getp/api/handler/AuthenticationExceptionHandler.java new file mode 100644 index 00000000..73211f79 --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/handler/AuthenticationExceptionHandler.java @@ -0,0 +1,28 @@ +package es.princip.getp.api.handler; + +import es.princip.getp.api.support.dto.ApiErrorResponse; +import es.princip.getp.api.support.dto.ApiErrorResponse.ApiErrorResult; +import es.princip.getp.domain.support.ErrorDescription; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.AuthenticationException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@Slf4j +@Order(1) +@RestControllerAdvice +public class AuthenticationExceptionHandler { + + @ExceptionHandler(AuthenticationException.class) + public ResponseEntity handle(final AuthenticationException exception) { + log.debug(exception.getMessage(), exception); + final ErrorDescription description = ErrorDescription.of( + "AUTHENTICATION_ERROR", + exception.getMessage() + ); + return ApiErrorResponse.error(HttpStatus.UNAUTHORIZED, description); + } +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/handler/DefaultExceptionHandler.java b/get-p-api/src/main/java/es/princip/getp/api/handler/DefaultExceptionHandler.java similarity index 100% rename from src/main/java/es/princip/getp/api/handler/DefaultExceptionHandler.java rename to get-p-api/src/main/java/es/princip/getp/api/handler/DefaultExceptionHandler.java diff --git a/src/main/java/es/princip/getp/api/handler/DomainLogicExceptionHandler.java b/get-p-api/src/main/java/es/princip/getp/api/handler/DomainLogicExceptionHandler.java similarity index 100% rename from src/main/java/es/princip/getp/api/handler/DomainLogicExceptionHandler.java rename to get-p-api/src/main/java/es/princip/getp/api/handler/DomainLogicExceptionHandler.java diff --git a/src/main/java/es/princip/getp/api/handler/ErrorDescriptionExceptionHandler.java b/get-p-api/src/main/java/es/princip/getp/api/handler/ErrorDescriptionExceptionHandler.java similarity index 100% rename from src/main/java/es/princip/getp/api/handler/ErrorDescriptionExceptionHandler.java rename to get-p-api/src/main/java/es/princip/getp/api/handler/ErrorDescriptionExceptionHandler.java diff --git a/src/main/java/es/princip/getp/api/handler/ExternalServerExceptionHandler.java b/get-p-api/src/main/java/es/princip/getp/api/handler/ExternalServerExceptionHandler.java similarity index 92% rename from src/main/java/es/princip/getp/api/handler/ExternalServerExceptionHandler.java rename to get-p-api/src/main/java/es/princip/getp/api/handler/ExternalServerExceptionHandler.java index a1591bf2..e57c36ee 100644 --- a/src/main/java/es/princip/getp/api/handler/ExternalServerExceptionHandler.java +++ b/get-p-api/src/main/java/es/princip/getp/api/handler/ExternalServerExceptionHandler.java @@ -2,7 +2,7 @@ import es.princip.getp.api.support.dto.ApiErrorResponse; import es.princip.getp.api.support.dto.ApiErrorResponse.ApiErrorResult; -import es.princip.getp.infrastructure.support.FileStorageException; +import es.princip.getp.application.storage.exception.FileStorageException; import lombok.extern.slf4j.Slf4j; import org.springframework.core.annotation.Order; import org.springframework.http.HttpStatus; diff --git a/src/main/java/es/princip/getp/api/handler/FileStorageExceptionHandler.java b/get-p-api/src/main/java/es/princip/getp/api/handler/FileStorageExceptionHandler.java similarity index 91% rename from src/main/java/es/princip/getp/api/handler/FileStorageExceptionHandler.java rename to get-p-api/src/main/java/es/princip/getp/api/handler/FileStorageExceptionHandler.java index a8f900b0..a72520d7 100644 --- a/src/main/java/es/princip/getp/api/handler/FileStorageExceptionHandler.java +++ b/get-p-api/src/main/java/es/princip/getp/api/handler/FileStorageExceptionHandler.java @@ -2,7 +2,7 @@ import es.princip.getp.api.support.dto.ApiErrorResponse; import es.princip.getp.api.support.dto.ApiErrorResponse.ApiErrorResult; -import es.princip.getp.infrastructure.support.FileStorageException; +import es.princip.getp.application.storage.exception.FileStorageException; import lombok.extern.slf4j.Slf4j; import org.springframework.core.annotation.Order; import org.springframework.http.HttpStatus; diff --git a/src/main/java/es/princip/getp/api/handler/ForbiddenExceptionHandler.java b/get-p-api/src/main/java/es/princip/getp/api/handler/ForbiddenExceptionHandler.java similarity index 100% rename from src/main/java/es/princip/getp/api/handler/ForbiddenExceptionHandler.java rename to get-p-api/src/main/java/es/princip/getp/api/handler/ForbiddenExceptionHandler.java diff --git a/src/main/java/es/princip/getp/api/handler/HttpRequestMethodNotSupportedExceptionHandler.java b/get-p-api/src/main/java/es/princip/getp/api/handler/HttpRequestMethodNotSupportedExceptionHandler.java similarity index 100% rename from src/main/java/es/princip/getp/api/handler/HttpRequestMethodNotSupportedExceptionHandler.java rename to get-p-api/src/main/java/es/princip/getp/api/handler/HttpRequestMethodNotSupportedExceptionHandler.java diff --git a/get-p-api/src/main/java/es/princip/getp/api/handler/MaxUploadSizeExceededExceptionHandler.java b/get-p-api/src/main/java/es/princip/getp/api/handler/MaxUploadSizeExceededExceptionHandler.java new file mode 100644 index 00000000..157607a8 --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/handler/MaxUploadSizeExceededExceptionHandler.java @@ -0,0 +1,28 @@ +package es.princip.getp.api.handler; + +import es.princip.getp.api.support.dto.ApiErrorResponse; +import es.princip.getp.api.support.dto.ApiErrorResponse.ApiErrorResult; +import es.princip.getp.domain.support.ErrorDescription; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.multipart.MaxUploadSizeExceededException; + +@Slf4j +@Order(1) +@RestControllerAdvice +public class MaxUploadSizeExceededExceptionHandler { + + @ExceptionHandler(MaxUploadSizeExceededException.class) + public ResponseEntity handle(final MaxUploadSizeExceededException exception) { + log.debug(exception.getMessage(), exception); + final ErrorDescription description = ErrorDescription.of( + "MAX_UPLOAD_SIZE_EXCEEDED", + "최대 허용 파일 크기를 초과했습니다. " + ); + return ApiErrorResponse.error(HttpStatus.PAYLOAD_TOO_LARGE, description); + } +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/handler/MethodArgumentNotValidExceptionHandler.java b/get-p-api/src/main/java/es/princip/getp/api/handler/MethodArgumentNotValidExceptionHandler.java similarity index 100% rename from src/main/java/es/princip/getp/api/handler/MethodArgumentNotValidExceptionHandler.java rename to get-p-api/src/main/java/es/princip/getp/api/handler/MethodArgumentNotValidExceptionHandler.java diff --git a/src/main/java/es/princip/getp/api/handler/NotFoundExceptionHandler.java b/get-p-api/src/main/java/es/princip/getp/api/handler/NotFoundExceptionHandler.java similarity index 93% rename from src/main/java/es/princip/getp/api/handler/NotFoundExceptionHandler.java rename to get-p-api/src/main/java/es/princip/getp/api/handler/NotFoundExceptionHandler.java index bbb0ec64..0d3f931d 100644 --- a/src/main/java/es/princip/getp/api/handler/NotFoundExceptionHandler.java +++ b/get-p-api/src/main/java/es/princip/getp/api/handler/NotFoundExceptionHandler.java @@ -2,7 +2,7 @@ import es.princip.getp.api.support.dto.ApiErrorResponse; import es.princip.getp.api.support.dto.ApiErrorResponse.ApiErrorResult; -import es.princip.getp.persistence.support.NotFoundException; +import es.princip.getp.application.support.NotFoundException; import lombok.extern.slf4j.Slf4j; import org.springframework.core.annotation.Order; import org.springframework.http.HttpStatus; diff --git a/src/main/java/es/princip/getp/api/security/filter/AccessTokenAuthorizationFilter.java b/get-p-api/src/main/java/es/princip/getp/api/support/AccessTokenAuthorizationFilter.java similarity index 78% rename from src/main/java/es/princip/getp/api/security/filter/AccessTokenAuthorizationFilter.java rename to get-p-api/src/main/java/es/princip/getp/api/support/AccessTokenAuthorizationFilter.java index 8be01a12..b37af6a1 100644 --- a/src/main/java/es/princip/getp/api/security/filter/AccessTokenAuthorizationFilter.java +++ b/get-p-api/src/main/java/es/princip/getp/api/support/AccessTokenAuthorizationFilter.java @@ -1,7 +1,5 @@ -package es.princip.getp.api.security.filter; +package es.princip.getp.api.support; -import es.princip.getp.api.security.exception.ExpiredTokenException; -import es.princip.getp.api.security.exception.InvalidTokenException; import es.princip.getp.application.auth.service.AccessTokenService; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -27,14 +25,14 @@ public class AccessTokenAuthorizationFilter extends OncePerRequestFilter { * Access Token을 검증한 뒤, 유효한 경우 SecurityContext에 인증 정보를 저장한다. * Access Token이 없거나 OPTIONS 요청일 경우 필터를 통과한다. * - * @throws ExpiredTokenException Access Token이 만료된 경우 - * @throws InvalidTokenException Access Token이 유효하지 않은 경우 + * @throws es.princip.getp.application.auth.exception.ExpiredTokenException Access Token이 만료된 경우 + * @throws es.princip.getp.application.auth.exception.InvalidTokenException Access Token이 유효하지 않은 경우 */ @Override protected void doFilterInternal( - @NonNull final HttpServletRequest request, - @NonNull final HttpServletResponse response, - @NonNull final FilterChain filterChain + @NonNull final HttpServletRequest request, + @NonNull final HttpServletResponse response, + @NonNull final FilterChain filterChain ) throws ServletException, IOException { if (isOptionsRequest(request)) { filterChain.doFilter(request, response); @@ -53,4 +51,4 @@ protected void doFilterInternal( private boolean isOptionsRequest(HttpServletRequest request) { return HttpMethod.OPTIONS.matches(request.getMethod()); } -} +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/security/filter/AccessTokenExceptionFilter.java b/get-p-api/src/main/java/es/princip/getp/api/support/AccessTokenExceptionFilter.java similarity index 94% rename from src/main/java/es/princip/getp/api/security/filter/AccessTokenExceptionFilter.java rename to get-p-api/src/main/java/es/princip/getp/api/support/AccessTokenExceptionFilter.java index 79b17a57..66a0a40a 100644 --- a/src/main/java/es/princip/getp/api/security/filter/AccessTokenExceptionFilter.java +++ b/get-p-api/src/main/java/es/princip/getp/api/support/AccessTokenExceptionFilter.java @@ -1,7 +1,7 @@ -package es.princip.getp.api.security.filter; +package es.princip.getp.api.support; import com.fasterxml.jackson.databind.ObjectMapper; -import es.princip.getp.api.security.exception.JwtTokenException; +import es.princip.getp.application.auth.exception.JwtTokenException; import es.princip.getp.api.support.dto.ApiErrorResponse; import es.princip.getp.api.support.dto.ApiErrorResponse.ApiErrorResult; import es.princip.getp.domain.support.ErrorDescription; diff --git a/src/main/java/es/princip/getp/api/support/ControllerSupport.java b/get-p-api/src/main/java/es/princip/getp/api/support/ControllerSupport.java similarity index 66% rename from src/main/java/es/princip/getp/api/support/ControllerSupport.java rename to get-p-api/src/main/java/es/princip/getp/api/support/ControllerSupport.java index f162a7fa..7abee7e2 100644 --- a/src/main/java/es/princip/getp/api/support/ControllerSupport.java +++ b/get-p-api/src/main/java/es/princip/getp/api/support/ControllerSupport.java @@ -1,5 +1,6 @@ package es.princip.getp.api.support; +import es.princip.getp.application.auth.service.PrincipalDetails; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -9,4 +10,8 @@ public boolean isAuthenticated() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); return authentication != null && authentication.isAuthenticated(); } + + public boolean isAuthenticated(final PrincipalDetails principalDetails) { + return isAuthenticated() && principalDetails != null; + } } diff --git a/get-p-api/src/main/java/es/princip/getp/api/support/CursorDefault.java b/get-p-api/src/main/java/es/princip/getp/api/support/CursorDefault.java new file mode 100644 index 00000000..622a7a48 --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/support/CursorDefault.java @@ -0,0 +1,15 @@ +package es.princip.getp.api.support; + +import es.princip.getp.application.support.Cursor; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface CursorDefault { + String value() default "cursor"; + Class type() default Cursor.class; +} diff --git a/get-p-api/src/main/java/es/princip/getp/api/support/CursorHandlerMethodArgumentResolver.java b/get-p-api/src/main/java/es/princip/getp/api/support/CursorHandlerMethodArgumentResolver.java new file mode 100644 index 00000000..14876499 --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/support/CursorHandlerMethodArgumentResolver.java @@ -0,0 +1,29 @@ +package es.princip.getp.api.support; + +import es.princip.getp.application.support.Cursor; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.ModelAndViewContainer; + +@Component +@RequiredArgsConstructor +class CursorHandlerMethodArgumentResolver extends CursorHandlerMethodArgumentResolverSupport { + + private final CursorParser cursorParser; + + @Override + public Cursor resolveCursor( + @NonNull final MethodParameter parameter, + final ModelAndViewContainer mavContainer, + @NonNull final NativeWebRequest webRequest, + final WebDataBinderFactory binderFactory + ) { + final Class cursorType = getCursorType(parameter); + final String cursorString = getCursorString(webRequest, parameter); + return cursorParser.parse(cursorType, cursorString); + } +} diff --git a/get-p-api/src/main/java/es/princip/getp/api/support/CursorHandlerMethodArgumentResolverSupport.java b/get-p-api/src/main/java/es/princip/getp/api/support/CursorHandlerMethodArgumentResolverSupport.java new file mode 100644 index 00000000..530b5e44 --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/support/CursorHandlerMethodArgumentResolverSupport.java @@ -0,0 +1,61 @@ +package es.princip.getp.api.support; + +import es.princip.getp.application.support.Cursor; +import es.princip.getp.application.support.CursorPageable; +import lombok.NonNull; +import org.springframework.core.MethodParameter; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +import java.util.Optional; + +abstract class CursorHandlerMethodArgumentResolverSupport implements HandlerMethodArgumentResolver { + + private static final String DEFAULT_CURSOR_PARAMETER = "cursor"; + private static final Class DEFAULT_CURSOR_TYPE = Cursor.class; + + @Override + public boolean supportsParameter(final MethodParameter parameter) { + return CursorPageable.class.equals(parameter.getParameterType()) && + parameter.hasParameterAnnotation(CursorDefault.class); + } + + @Override + public Object resolveArgument( + @NonNull final MethodParameter parameter, + final ModelAndViewContainer mavContainer, + @NonNull final NativeWebRequest webRequest, + final WebDataBinderFactory binderFactory + ) { + return resolveCursor(parameter, mavContainer, webRequest, binderFactory); + } + + public abstract Cursor resolveCursor( + @NonNull MethodParameter parameter, + ModelAndViewContainer mavContainer, + @NonNull NativeWebRequest webRequest, + WebDataBinderFactory binderFactory + ); + + protected Class getCursorType(final MethodParameter parameter) { + final CursorDefault annotation = parameter.getParameterAnnotation(CursorDefault.class); + if (annotation == null) { + return DEFAULT_CURSOR_TYPE; + } + return annotation.type(); + } + + protected String getCursorString(final NativeWebRequest webRequest, final MethodParameter parameter) { + String cursorString = webRequest.getParameter(DEFAULT_CURSOR_PARAMETER); + if (cursorString != null) { + return cursorString; + } + final CursorDefault annotation = parameter.getParameterAnnotation(CursorDefault.class); + cursorString = webRequest.getParameter(Optional.ofNullable(annotation) + .map(CursorDefault::value) + .orElseThrow(() -> new IllegalArgumentException("@CursorDefault is required"))); + return cursorString; + } +} \ No newline at end of file diff --git a/get-p-api/src/main/java/es/princip/getp/api/support/CursorPageRequest.java b/get-p-api/src/main/java/es/princip/getp/api/support/CursorPageRequest.java new file mode 100644 index 00000000..92e54e9e --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/support/CursorPageRequest.java @@ -0,0 +1,37 @@ +package es.princip.getp.api.support; + +import es.princip.getp.application.support.Cursor; +import es.princip.getp.application.support.CursorPageable; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; + +public class CursorPageRequest implements CursorPageable { + + private final Pageable pageable; + private final T cursor; + + public CursorPageRequest(final Pageable pageable, final T cursor) { + this.pageable = pageable; + this.cursor = cursor; + } + + @Override + public int getPageSize() { + return pageable.getPageSize(); + } + + @Override + public T getCursor() { + return cursor; + } + + @Override + public boolean hasCursor() { + return cursor != null; + } + + @Override + public Sort getSort() { + return pageable.getSort(); + } +} diff --git a/get-p-api/src/main/java/es/princip/getp/api/support/CursorPageableHandlerMethodArgumentResolver.java b/get-p-api/src/main/java/es/princip/getp/api/support/CursorPageableHandlerMethodArgumentResolver.java new file mode 100644 index 00000000..06c73c8f --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/support/CursorPageableHandlerMethodArgumentResolver.java @@ -0,0 +1,40 @@ +package es.princip.getp.api.support; + +import es.princip.getp.application.support.Cursor; +import es.princip.getp.application.support.CursorPageable; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.springframework.core.MethodParameter; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableHandlerMethodArgumentResolver; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +@Component +@RequiredArgsConstructor +class CursorPageableHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { + + private final CursorHandlerMethodArgumentResolver cursorResolver; + private final PageableHandlerMethodArgumentResolver pageableResolver; + + @Override + public boolean supportsParameter(final MethodParameter parameter) { + return CursorPageable.class.equals(parameter.getParameterType()) && + parameter.hasParameterAnnotation(CursorDefault.class); + } + + @Override + public Object resolveArgument( + @NonNull final MethodParameter parameter, + final ModelAndViewContainer mavContainer, + @NonNull final NativeWebRequest webRequest, + final WebDataBinderFactory binderFactory + ) { + final Pageable pageable = pageableResolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); + final Cursor cursor = cursorResolver.resolveCursor(parameter, mavContainer, webRequest, binderFactory); + return new CursorPageRequest<>(pageable, cursor); + } +} diff --git a/get-p-api/src/main/java/es/princip/getp/api/support/CursorParser.java b/get-p-api/src/main/java/es/princip/getp/api/support/CursorParser.java new file mode 100644 index 00000000..05cdfda2 --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/support/CursorParser.java @@ -0,0 +1,34 @@ +package es.princip.getp.api.support; + +import com.fasterxml.jackson.databind.ObjectMapper; +import es.princip.getp.application.support.Cursor; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.Base64; + +@Component +@RequiredArgsConstructor +public class CursorParser { + + private final ObjectMapper objectMapper; + + public T parse(final Class clazz, final String cursorString) { + if (cursorString == null) { + return null; + } + try { + final T result = objectMapper.readValue(Base64.getDecoder().decode(cursorString), clazz); + if (result == null) { + return null; + } + if (result.getId() == null) { + throw new IllegalArgumentException("커서에 기본키가 포함되어 있지 않습니다."); + } + return result; + } catch (final IOException exception) { + return null; + } + } +} diff --git a/src/main/java/es/princip/getp/api/security/SecurityConfig.java b/get-p-api/src/main/java/es/princip/getp/api/support/SecurityConfig.java similarity index 93% rename from src/main/java/es/princip/getp/api/security/SecurityConfig.java rename to get-p-api/src/main/java/es/princip/getp/api/support/SecurityConfig.java index 05c29944..a100b5f3 100644 --- a/src/main/java/es/princip/getp/api/security/SecurityConfig.java +++ b/get-p-api/src/main/java/es/princip/getp/api/support/SecurityConfig.java @@ -1,7 +1,5 @@ -package es.princip.getp.api.security; +package es.princip.getp.api.support; -import es.princip.getp.api.security.filter.AccessTokenAuthorizationFilter; -import es.princip.getp.api.security.filter.AccessTokenExceptionFilter; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -67,8 +65,9 @@ public SecurityFilterChain securityFilterChain( CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList( - "https://h.princip.es", - "https://he.princip.es", + "https://api.principes.xyz", + "https://h.principes.xyz", + "https://he.principes.xyz", "http://localhost:5173", "https://beta-get-p.netlify.app" )); diff --git a/get-p-api/src/main/java/es/princip/getp/api/support/SwaggerConfig.java b/get-p-api/src/main/java/es/princip/getp/api/support/SwaggerConfig.java new file mode 100644 index 00000000..e454dfe5 --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/support/SwaggerConfig.java @@ -0,0 +1,35 @@ +package es.princip.getp.api.support; + +import org.springdoc.core.configuration.SpringDocConfiguration; +import org.springdoc.core.configuration.SpringDocUIConfiguration; +import org.springdoc.core.properties.SpringDocConfigProperties; +import org.springdoc.core.properties.SwaggerUiConfigProperties; +import org.springdoc.core.providers.ObjectMapperProvider; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Optional; + +@Configuration +public class SwaggerConfig { + + @Bean + SpringDocConfiguration springDocConfiguration(){ + return new SpringDocConfiguration(); + } + + @Bean + SpringDocConfigProperties springDocConfigProperties() { + return new SpringDocConfigProperties(); + } + + @Bean + ObjectMapperProvider objectMapperProvider(SpringDocConfigProperties properties){ + return new ObjectMapperProvider(properties); + } + + @Bean + SpringDocUIConfiguration SpringDocUIConfiguration(Optional properties){ + return new SpringDocUIConfiguration(properties); + } +} \ No newline at end of file diff --git a/get-p-api/src/main/java/es/princip/getp/api/support/WebMvcConfig.java b/get-p-api/src/main/java/es/princip/getp/api/support/WebMvcConfig.java new file mode 100644 index 00000000..db829b13 --- /dev/null +++ b/get-p-api/src/main/java/es/princip/getp/api/support/WebMvcConfig.java @@ -0,0 +1,20 @@ +package es.princip.getp.api.support; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.List; + +@Configuration +@RequiredArgsConstructor +public class WebMvcConfig implements WebMvcConfigurer { + + private final CursorPageableHandlerMethodArgumentResolver cursorPageableHandlerMethodArgumentResolver; + + @Override + public void addArgumentResolvers(final List resolvers) { + resolvers.add(cursorPageableHandlerMethodArgumentResolver); + } +} diff --git a/src/main/java/es/princip/getp/api/support/dto/ApiErrorResponse.java b/get-p-api/src/main/java/es/princip/getp/api/support/dto/ApiErrorResponse.java similarity index 100% rename from src/main/java/es/princip/getp/api/support/dto/ApiErrorResponse.java rename to get-p-api/src/main/java/es/princip/getp/api/support/dto/ApiErrorResponse.java diff --git a/src/main/java/es/princip/getp/api/support/dto/ApiResponse.java b/get-p-api/src/main/java/es/princip/getp/api/support/dto/ApiResponse.java similarity index 100% rename from src/main/java/es/princip/getp/api/support/dto/ApiResponse.java rename to get-p-api/src/main/java/es/princip/getp/api/support/dto/ApiResponse.java diff --git a/src/main/java/es/princip/getp/api/validation/UserMemberType.java b/get-p-api/src/main/java/es/princip/getp/api/validation/UserMemberType.java similarity index 100% rename from src/main/java/es/princip/getp/api/validation/UserMemberType.java rename to get-p-api/src/main/java/es/princip/getp/api/validation/UserMemberType.java diff --git a/src/main/java/es/princip/getp/api/validation/UserMemberTypeValidator.java b/get-p-api/src/main/java/es/princip/getp/api/validation/UserMemberTypeValidator.java similarity index 100% rename from src/main/java/es/princip/getp/api/validation/UserMemberTypeValidator.java rename to get-p-api/src/main/java/es/princip/getp/api/validation/UserMemberTypeValidator.java diff --git a/get-p-api/src/main/resources/api-config.yml b/get-p-api/src/main/resources/api-config.yml new file mode 100644 index 00000000..a03aa461 --- /dev/null +++ b/get-p-api/src/main/resources/api-config.yml @@ -0,0 +1,40 @@ +server: + port: ${SPRING_PORT} + servlet: + context-path: ${BASE_PATH} + +spring: + servlet: + multipart: + max-file-size: 5MB + +springdoc: + api-docs: + enabled: false + swagger-ui: + path: ${SWAGGER_PATH} + url: /oas/openapi3.yaml + +--- +spring: + config: + activate: + on-profile: local + +logging: + level: + org: + springframework: + security: DEBUG + +--- +spring: + config: + activate: + on-profile: dev + +logging: + level: + org: + springframework: + security: DEBUG \ No newline at end of file diff --git a/get-p-api/src/main/resources/application.yml b/get-p-api/src/main/resources/application.yml new file mode 100644 index 00000000..1f5cfde6 --- /dev/null +++ b/get-p-api/src/main/resources/application.yml @@ -0,0 +1,46 @@ +spring: + config: + import: + - api-config.yml + - app-config.yml + - persistence-config.yml + - infra-config.yml + +--- +spring: + config: + activate: + on-profile: local + +logging: + level: + es.princip.getp: DEBUG + +--- +spring: + config: + activate: + on-profile: dev + +logging: + level: + es.princip.getp: DEBUG + +management: + endpoints: + web: + exposure: + include: prometheus + base-path: ${MONITORING_URL} + enabled-by-default: false + jmx: + exposure: + exclude: '*' + include: info, health + endpoint: + prometheus: + enabled: true + info: + enabled: true + health: + enabled: true \ No newline at end of file diff --git a/get-p-api/src/main/resources/static/logo.png b/get-p-api/src/main/resources/static/logo.png new file mode 100644 index 00000000..6c006440 Binary files /dev/null and b/get-p-api/src/main/resources/static/logo.png differ diff --git a/src/test/java/es/princip/getp/api/config/MockDaoBeanFactoryPostProcessor.java b/get-p-api/src/test/java/es/princip/getp/api/config/MockDaoBeanFactoryPostProcessor.java similarity index 95% rename from src/test/java/es/princip/getp/api/config/MockDaoBeanFactoryPostProcessor.java rename to get-p-api/src/test/java/es/princip/getp/api/config/MockDaoBeanFactoryPostProcessor.java index 9d26b75a..a8e1ab73 100644 --- a/src/test/java/es/princip/getp/api/config/MockDaoBeanFactoryPostProcessor.java +++ b/get-p-api/src/test/java/es/princip/getp/api/config/MockDaoBeanFactoryPostProcessor.java @@ -1,6 +1,6 @@ package es.princip.getp.api.config; -import org.jetbrains.annotations.NotNull; +import jakarta.validation.constraints.NotNull; import org.junit.platform.commons.util.ClassFilter; import org.junit.platform.commons.util.ReflectionUtils; import org.springframework.beans.BeansException; diff --git a/src/test/java/es/princip/getp/api/config/MockServiceBeanFactoryPostProcessor.java b/get-p-api/src/test/java/es/princip/getp/api/config/MockServiceBeanFactoryPostProcessor.java similarity index 95% rename from src/test/java/es/princip/getp/api/config/MockServiceBeanFactoryPostProcessor.java rename to get-p-api/src/test/java/es/princip/getp/api/config/MockServiceBeanFactoryPostProcessor.java index 97b367d7..66036580 100644 --- a/src/test/java/es/princip/getp/api/config/MockServiceBeanFactoryPostProcessor.java +++ b/get-p-api/src/test/java/es/princip/getp/api/config/MockServiceBeanFactoryPostProcessor.java @@ -1,6 +1,6 @@ package es.princip.getp.api.config; -import org.jetbrains.annotations.NotNull; +import jakarta.validation.constraints.NotNull; import org.junit.platform.commons.util.ClassFilter; import org.junit.platform.commons.util.ReflectionUtils; import org.springframework.beans.BeansException; diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/auth/AuthControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/auth/AuthControllerTest.java new file mode 100644 index 00000000..a9ba859e --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/auth/AuthControllerTest.java @@ -0,0 +1,107 @@ +package es.princip.getp.api.controller.auth; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; +import es.princip.getp.api.controller.auth.dto.request.LoginRequest; +import es.princip.getp.application.auth.dto.response.Token; +import es.princip.getp.api.support.ControllerTest; +import es.princip.getp.application.auth.service.AuthService; +import jakarta.servlet.http.HttpServletRequest; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.docs.ConstraintDescriptor.fieldWithConstraint; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.refreshTokenHeaderDescription; +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; +import static es.princip.getp.fixture.common.EmailFixture.EMAIL; +import static es.princip.getp.fixture.member.PasswordFixture.PASSWORD; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.BDDMockito.given; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.snippet.Attributes.key; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class AuthControllerTest extends ControllerTest { + + @Autowired + private AuthService authService; + + @Nested + class 로그인 { + + private final LoginRequest request = new LoginRequest(EMAIL, PASSWORD); + + @Test + void 사용자는_로그인을_할_수_있다() throws Exception { + final Token token = new Token("Bearer", "${ACCESS_TOKEN}", "${REFRESH_TOKEN}"); + given(authService.login(anyString(), anyString())).willReturn(token); + + mockMvc.perform(post("/auth/login") + .content(objectMapper.writeValueAsString(request))) + .andExpect(status().isCreated()) + .andDo(document( + "auth/login", + ResourceSnippetParameters.builder() + .tag("인증") + .description("사용자는 로그인을 할 수 있다.") + .summary("로그인") + .requestSchema(Schema.schema("LoginRequest")) + .responseSchema(Schema.schema("TokenResponse")), + requestFields( + fieldWithConstraint("email", LoginRequest.class).description("이메일"), + fieldWithConstraint("password", LoginRequest.class).description("비밀번호") + ), + responseFields( + statusField(), + fieldWithPath("data.grantType").description("토큰 타입") + .attributes(key("format").value("Bearer")), + fieldWithPath("data.accessToken").description("Access Token"), + fieldWithPath("data.refreshToken").description("Refresh Token") + ) + )) + .andDo(print()); + } + } + + @Nested + class Access_Token_재발급 { + + @Test + void 사용자는_로그인_유지를_할_수있다() throws Exception { + final Token token = new Token( + "Bearer", + "${ACCESS_TOKEN}", + "${REFRESH_TOKEN}" + ); + given(authService.reissueAccessToken(any(HttpServletRequest.class))) + .willReturn(token); + + mockMvc.perform(post("/auth/reissue") + .header("Refresh-Token", "Bearer ${REFRESH_TOKEN}")) + .andExpect(status().isCreated()) + .andDo(document( + "auth/reissue-access-token", + ResourceSnippetParameters.builder() + .tag("인증") + .description("사용자는 로그인 유지를 할 수 있다.") + .summary("Access Token 재발급") + .requestSchema(Schema.schema("ReissueAccessTokenRequest")) + .responseSchema(Schema.schema("TokenResponse")), + requestHeaders(refreshTokenHeaderDescription()), + responseFields( + statusField(), + fieldWithPath("data.grantType").description("토큰 타입") + .attributes(key("format").value("Bearer")), + fieldWithPath("data.accessToken").description("Access Token"), + fieldWithPath("data.refreshToken").description("Refresh Token") + ) + )) + .andDo(print()); + } + } +} \ No newline at end of file diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/auth/SignUpControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/auth/SignUpControllerTest.java new file mode 100644 index 00000000..e330abf7 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/auth/SignUpControllerTest.java @@ -0,0 +1,112 @@ +package es.princip.getp.api.controller.auth; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; +import es.princip.getp.api.controller.auth.dto.request.EmailVerificationCodeRequest; +import es.princip.getp.api.controller.auth.dto.request.ServiceTermAgreementRequest; +import es.princip.getp.api.controller.auth.dto.request.SignUpRequest; +import es.princip.getp.api.support.ControllerTest; +import es.princip.getp.application.auth.dto.command.SignUpCommand; +import es.princip.getp.application.auth.service.SignUpService; +import es.princip.getp.domain.member.model.MemberType; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Set; + +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.docs.ConstraintDescriptor.fieldWithConstraint; +import static es.princip.getp.api.docs.EnumDescriptor.fieldWithEnum; +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; +import static es.princip.getp.fixture.auth.EmailVerificationFixture.VERIFICATION_CODE; +import static es.princip.getp.fixture.common.EmailFixture.EMAIL; +import static es.princip.getp.fixture.member.PasswordFixture.PASSWORD; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.willDoNothing; +import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class SignUpControllerTest extends ControllerTest { + + @Autowired private SignUpService signUpService; + + @Nested + class 이메일_인증_코드_전송 { + + private final EmailVerificationCodeRequest request = new EmailVerificationCodeRequest(EMAIL); + + @Test + void 사용자는_회원_가입_시_이메일_인증을_해야_한다() throws Exception { + mockMvc.perform(post("/auth/signup/email/send") + .content(objectMapper.writeValueAsString(request))) + .andExpect(status().isOk()) + .andDo(document("auth/send-email-verification-code-for-signup", + ResourceSnippetParameters.builder() + .tag("인증") + .description("사용자는 회원 가입 시 이메일 인증을 해야 한다.") + .summary("이메일 인증 코드 전송") + .requestSchema(Schema.schema("EmailVerificationCodeRequest")) + .responseSchema(Schema.schema("StatusResponse")), + requestFields( + fieldWithConstraint("email", EmailVerificationCodeRequest.class) + .description("이메일") + ), + responseFields(statusField()) + )) + .andDo(print()); + } + } + + @Nested + class 회원_가입 { + + @ParameterizedTest + @EnumSource(value = MemberType.class, names = { "ROLE_PEOPLE", "ROLE_CLIENT" }) + void 사용자는_회원_가입을_할_수_있다(MemberType memberType) throws Exception { + final SignUpRequest request = new SignUpRequest( + EMAIL, + PASSWORD, + VERIFICATION_CODE, + Set.of( + new ServiceTermAgreementRequest("tag1", true), + new ServiceTermAgreementRequest("tag2", false) + ), + memberType + ); + willDoNothing().given(signUpService).signUp(any(SignUpCommand.class)); + + mockMvc.perform(post("/auth/signup") + .content(objectMapper.writeValueAsString(request))) + .andExpect(status().isCreated()) + .andDo(document("auth/signup", + ResourceSnippetParameters.builder() + .tag("인증") + .description("사용자는 회원 가입을 할 수 있다.") + .summary("회원 가입") + .requestSchema(Schema.schema("SignUpRequest")) + .responseSchema(Schema.schema("StatusResponse")), + requestFields( + fieldWithConstraint("email", SignUpRequest.class) + .description("이메일"), + fieldWithConstraint("password", SignUpRequest.class) + .description("비밀번호"), + fieldWithEnum(MemberType.class).withPath("memberType") + .description("회원 유형. 일반 사용자는 피플인 ROLE_PEOPLE 또는 의뢰자인 ROLE_CLIENT만 가능"), + fieldWithConstraint("verificationCode", SignUpRequest.class) + .description("이메일 인증 코드"), + fieldWithConstraint("serviceTerms[].tag", ServiceTermAgreementRequest.class) + .description("서비스 약관 태그"), + fieldWithConstraint("serviceTerms[].agreed", ServiceTermAgreementRequest.class) + .description("서비스 약관 동의 여부") + ), + responseFields(statusField()) + )) + .andDo(print()); + } + } +} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/client/command/MyClientControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/client/command/MyClientControllerTest.java similarity index 52% rename from src/test/java/es/princip/getp/api/controller/client/command/MyClientControllerTest.java rename to get-p-api/src/test/java/es/princip/getp/api/controller/client/command/MyClientControllerTest.java index 52a528ba..9b17ca4a 100644 --- a/src/test/java/es/princip/getp/api/controller/client/command/MyClientControllerTest.java +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/client/command/MyClientControllerTest.java @@ -1,28 +1,29 @@ package es.princip.getp.api.controller.client.command; -import es.princip.getp.api.support.ControllerTest; -import es.princip.getp.api.controller.client.command.description.EditMyClientRequestDescription; -import es.princip.getp.api.controller.client.command.description.RegisterMyClientRequestDescription; +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; import es.princip.getp.api.controller.client.command.dto.request.EditMyClientRequest; import es.princip.getp.api.controller.client.command.dto.request.RegisterMyClientRequest; import es.princip.getp.api.security.annotation.WithCustomMockUser; -import es.princip.getp.api.security.details.PrincipalDetails; +import es.princip.getp.application.auth.service.PrincipalDetails; +import es.princip.getp.api.support.ControllerTest; import es.princip.getp.application.client.port.in.EditClientUseCase; import es.princip.getp.application.client.port.in.RegisterClientUseCase; +import es.princip.getp.domain.client.model.ClientId; import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.member.model.MemberType; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.restdocs.payload.PayloadDocumentation; import org.springframework.test.web.servlet.ResultActions; -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; -import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescriptor; -import static es.princip.getp.api.docs.PayloadDocumentationHelper.responseFields; +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.controller.client.command.description.EditMyClientRequestDescription.editMyClientRequestDescription; +import static es.princip.getp.api.controller.client.command.description.RegisterMyClientRequestDescription.registerMyClientRequestDescription; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescription; +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; import static es.princip.getp.fixture.client.AddressFixture.address; -import static es.princip.getp.fixture.client.BankAccountFixture.bankAccount; import static es.princip.getp.fixture.common.EmailFixture.EMAIL; import static es.princip.getp.fixture.member.NicknameFixture.NICKNAME; import static es.princip.getp.fixture.member.PhoneNumberFixture.PHONE_NUMBER; @@ -30,32 +31,28 @@ import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.willDoNothing; import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; class MyClientControllerTest extends ControllerTest { - @Autowired - private RegisterClientUseCase registerClientUseCase; + @Autowired private RegisterClientUseCase registerClientUseCase; - @Autowired - private EditClientUseCase editClientUseCase; + @Autowired private EditClientUseCase editClientUseCase; private static final String REQUEST_URI = "/client/me"; @Nested - @DisplayName("내 의뢰자 정보 등록") - class RegisterMyClient { + class 내_의뢰자_정보_등록 { final RegisterMyClientRequest request = new RegisterMyClientRequest( NICKNAME, EMAIL, PHONE_NUMBER, - address(), - bankAccount() + address() ); - final Long clientId = 1L; - final Long memberId = 1L; + final ClientId clientId = new ClientId(1L); private ResultActions perform() throws Exception { return mockMvc.perform(post(REQUEST_URI) @@ -64,38 +61,40 @@ private ResultActions perform() throws Exception { } @Test - @DisplayName("의뢰자는 의뢰자 정보를 등록할 수 있다.") @WithCustomMockUser(memberType = MemberType.ROLE_CLIENT) - void registerMyClient(PrincipalDetails principalDetails) throws Exception { + void 의뢰자는_의뢰자_정보를_등록할_수_있다(PrincipalDetails principalDetails) throws Exception { final Member member = principalDetails.getMember(); given(registerClientUseCase.register(eq(request.toCommand(member)))) .willReturn(clientId); perform() .andExpect(status().isCreated()) - .andDo( - restDocs.document( - requestHeaders(authorizationHeaderDescriptor()), - PayloadDocumentation.requestFields(RegisterMyClientRequestDescription.description()), - responseFields( - getDescriptor("clientId", "등록된 의뢰자 ID") - ) + .andDo(document("client/register-my-client", + ResourceSnippetParameters.builder() + .tag("의뢰자") + .description("의뢰자는 의뢰자 정보를 등록할 수 있다.") + .summary("내 의뢰자 정보 등록") + .requestSchema(Schema.schema("RegisterMyClientRequest")) + .responseSchema(Schema.schema("RegisterMyClientResponse")), + requestHeaders(authorizationHeaderDescription()), + requestFields(registerMyClientRequestDescription()), + responseFields( + statusField(), + fieldWithPath("data.clientId").description("등록된 의뢰자 ID") ) - ) + )) .andDo(print()); } } @Nested - @DisplayName("내 의뢰자 정보 수정") - class EditMyClient { + class 내_의뢰자_정보_수정 { final EditMyClientRequest request = new EditMyClientRequest( NICKNAME, EMAIL, PHONE_NUMBER, - address(), - bankAccount() + address() ); private ResultActions perform() throws Exception { @@ -105,20 +104,24 @@ private ResultActions perform() throws Exception { } @Test - @DisplayName("의뢰자는 자신의 의뢰자 정보를 수정할 수 있다.") @WithCustomMockUser(memberType = MemberType.ROLE_CLIENT) - void editMyClient(final PrincipalDetails principalDetails) throws Exception { - final Long memberId = principalDetails.getMember().getMemberId(); + void 의뢰자는_자신의_의뢰자_정보를_수정할_수_있다(final PrincipalDetails principalDetails) throws Exception { + final MemberId memberId = principalDetails.getMember().getId(); willDoNothing().given(editClientUseCase).edit(eq(request.toCommand(memberId))); perform() .andExpect(status().isOk()) - .andDo( - restDocs.document( - requestHeaders(authorizationHeaderDescriptor()), - PayloadDocumentation.requestFields(EditMyClientRequestDescription.description()) - ) - ) + .andDo(document("client/edit-my-client", + ResourceSnippetParameters.builder() + .tag("의뢰자") + .description("의뢰자는 자신의 의뢰자 정보를 수정할 수 있다.") + .summary("내 의뢰자 정보 수정") + .requestSchema(Schema.schema("EditMyClientRequest")) + .responseSchema(Schema.schema("StatusResponse")), + requestHeaders(authorizationHeaderDescription()), + requestFields(editMyClientRequestDescription()), + responseFields(statusField()) + )) .andDo(print()); } } diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/client/command/description/EditMyClientRequestDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/client/command/description/EditMyClientRequestDescription.java new file mode 100644 index 00000000..77c2764d --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/client/command/description/EditMyClientRequestDescription.java @@ -0,0 +1,26 @@ +package es.princip.getp.api.controller.client.command.description; + +import es.princip.getp.api.controller.client.command.dto.request.EditMyClientRequest; +import es.princip.getp.domain.client.model.Address; +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.ConstraintDescriptor.fieldWithConstraint; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + +public class EditMyClientRequestDescription { + + public static FieldDescriptor[] editMyClientRequestDescription() { + final Class clazz = EditMyClientRequest.class; + return new FieldDescriptor[] { + fieldWithConstraint("nickname", clazz).description("닉네임"), + fieldWithConstraint("email", clazz).description("이메일"), + fieldWithConstraint("phoneNumber", clazz).description("전화번호"), + fieldWithPath("address").optional().description("주소"), + fieldWithConstraint("address.zipcode", Address.class).description("우편번호"), + fieldWithConstraint("address.street", Address.class).description("도로명 주소"), + fieldWithConstraint("address.detail", Address.class) + .optional() + .description("상세 주소") + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/client/command/description/RegisterMyClientRequestDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/client/command/description/RegisterMyClientRequestDescription.java new file mode 100644 index 00000000..56123caa --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/client/command/description/RegisterMyClientRequestDescription.java @@ -0,0 +1,28 @@ +package es.princip.getp.api.controller.client.command.description; + +import es.princip.getp.api.controller.client.command.dto.request.RegisterMyClientRequest; +import es.princip.getp.domain.client.model.Address; +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.ConstraintDescriptor.fieldWithConstraint; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + +public class RegisterMyClientRequestDescription { + + public static FieldDescriptor[] registerMyClientRequestDescription() { + final Class clazz = RegisterMyClientRequest.class; + return new FieldDescriptor[]{ + fieldWithConstraint("nickname", clazz).description("닉네임"), + fieldWithConstraint("email", clazz) + .optional() + .description("이메일. 미입력 시 회원 정보의 이메일로 등록됩니다."), + fieldWithConstraint("phoneNumber", clazz).description("전화번호"), + fieldWithPath("address").optional().description("주소"), + fieldWithConstraint("address.zipcode", Address.class).description("우편번호"), + fieldWithConstraint("address.street", Address.class).description("도로명 주소"), + fieldWithConstraint("address.detail", Address.class) + .optional() + .description("상세 주소") + }; + } +} diff --git a/src/test/java/es/princip/getp/api/controller/client/query/ClientQueryControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/client/query/ClientQueryControllerTest.java similarity index 60% rename from src/test/java/es/princip/getp/api/controller/client/query/ClientQueryControllerTest.java rename to get-p-api/src/test/java/es/princip/getp/api/controller/client/query/ClientQueryControllerTest.java index 475cfac3..5c3b2262 100644 --- a/src/test/java/es/princip/getp/api/controller/client/query/ClientQueryControllerTest.java +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/client/query/ClientQueryControllerTest.java @@ -1,12 +1,13 @@ package es.princip.getp.api.controller.client.query; -import es.princip.getp.api.controller.client.query.dto.ClientResponse; +import es.princip.getp.application.client.dto.response.ClientResponse; import es.princip.getp.api.security.annotation.WithCustomMockUser; import es.princip.getp.api.support.ControllerTest; import es.princip.getp.application.client.port.out.ClientQuery; +import es.princip.getp.domain.client.model.ClientId; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.member.model.MemberType; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -15,7 +16,6 @@ import java.time.LocalDateTime; import static es.princip.getp.fixture.client.AddressFixture.address; -import static es.princip.getp.fixture.client.BankAccountFixture.bankAccount; import static es.princip.getp.fixture.common.EmailFixture.EMAIL; import static es.princip.getp.fixture.member.NicknameFixture.NICKNAME; import static es.princip.getp.fixture.member.PhoneNumberFixture.PHONE_NUMBER; @@ -35,64 +35,51 @@ void tearDown() { } @Nested - @DisplayName("의뢰자 정보 조회") - class GetMyClient { + class 의뢰자_정보_조회 { - final Long clientId = 1L; + private final MemberId memberId = new MemberId(1L); + private final ClientId clientId = new ClientId(1L); @Test - @DisplayName("관리자는 의뢰자 정보를 조회할 수 있다.") @WithCustomMockUser(memberType = MemberType.ROLE_ADMIN) - void getClient_WhenMemberTypeIsAdmin() throws Exception { + void 관리자는_의뢰자_정보를_조회할_수_있다() throws Exception { final LocalDateTime now = LocalDateTime.now(); final ClientResponse response = new ClientResponse( - clientId, + clientId.getValue(), NICKNAME, PHONE_NUMBER, EMAIL, - profileImage(1L).getUrl(), + profileImage(memberId).getUrl(), address(), - bankAccount(), now, now ); - given(clientQuery.findClientById(clientId)).willReturn(response); + given(clientQuery.findClientBy(clientId)).willReturn(response); - mockMvc.perform(get("/client/{clientId}", clientId)) + mockMvc.perform(get("/client/{clientId}", clientId.getValue())) .andExpect(status().isOk()) .andDo(print()); } @Test - @DisplayName("매니저는 의뢰자 정보를 조회할 수 있다.") @WithCustomMockUser(memberType = MemberType.ROLE_MANAGER) - void getClient_WhenMemberTypeIsManager() throws Exception { + void 매니저는_의뢰자_정보를_조회할_수_있다() throws Exception { final LocalDateTime now = LocalDateTime.now(); final ClientResponse response = new ClientResponse( - clientId, + clientId.getValue(), NICKNAME, PHONE_NUMBER, EMAIL, - profileImage(1L).getUrl(), + profileImage(memberId).getUrl(), address(), - bankAccount(), now, now ); - given(clientQuery.findClientById(clientId)).willReturn(response); + given(clientQuery.findClientBy(clientId)).willReturn(response); - mockMvc.perform(get("/client/{clientId}", clientId)) + mockMvc.perform(get("/client/{clientId}", clientId.getValue())) .andExpect(status().isOk()) .andDo(print()); } - - @Test - @DisplayName("관리자나 매니저가 아니면 의뢰자 정보를 조회할 수 없다.") - @WithCustomMockUser(memberType = MemberType.ROLE_CLIENT) - void getClient_WhenMemberTypeIsNotAdminOrManager_Fail() throws Exception { - mockMvc.perform(get("/client/{clientId}", clientId)) - .andExpect(status().isForbidden()) - .andDo(print()); - } } } \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/client/query/MyClientQueryControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/client/query/MyClientQueryControllerTest.java similarity index 55% rename from src/test/java/es/princip/getp/api/controller/client/query/MyClientQueryControllerTest.java rename to get-p-api/src/test/java/es/princip/getp/api/controller/client/query/MyClientQueryControllerTest.java index beaa79a0..4930f915 100644 --- a/src/test/java/es/princip/getp/api/controller/client/query/MyClientQueryControllerTest.java +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/client/query/MyClientQueryControllerTest.java @@ -1,13 +1,13 @@ package es.princip.getp.api.controller.client.query; -import es.princip.getp.api.support.ControllerTest; -import es.princip.getp.api.controller.client.query.description.ClientResponseDescription; -import es.princip.getp.api.controller.client.query.dto.ClientResponse; -import es.princip.getp.api.docs.PayloadDocumentationHelper; +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; +import es.princip.getp.application.client.dto.response.ClientResponse; import es.princip.getp.api.security.annotation.WithCustomMockUser; +import es.princip.getp.api.support.ControllerTest; import es.princip.getp.application.client.port.out.ClientQuery; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.member.model.MemberType; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -15,37 +15,36 @@ import java.time.LocalDateTime; -import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescriptor; +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.controller.client.query.description.ClientResponseDescription.clientResponseDescription; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescription; import static es.princip.getp.fixture.client.AddressFixture.address; -import static es.princip.getp.fixture.client.BankAccountFixture.bankAccount; import static es.princip.getp.fixture.common.EmailFixture.EMAIL; import static es.princip.getp.fixture.member.NicknameFixture.NICKNAME; import static es.princip.getp.fixture.member.PhoneNumberFixture.PHONE_NUMBER; import static es.princip.getp.fixture.member.ProfileImageFixture.profileImage; import static org.mockito.BDDMockito.given; import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; class MyClientQueryControllerTest extends ControllerTest { - @Autowired - private ClientQuery clientQuery; + @Autowired private ClientQuery clientQuery; @Nested - @DisplayName("내 의뢰자 정보 조회") - class GetMyClient { + class 내_의뢰자_정보_조회 { - final Long memberId = 1L; - final LocalDateTime now = LocalDateTime.now(); - final ClientResponse response = new ClientResponse( + private final MemberId memberId = new MemberId(1L); + private final LocalDateTime now = LocalDateTime.now(); + private final ClientResponse response = new ClientResponse( 1L, NICKNAME, PHONE_NUMBER, EMAIL, profileImage(memberId).getUrl(), address(), - bankAccount(), now, now ); @@ -56,19 +55,21 @@ private ResultActions perform() throws Exception { } @Test - @DisplayName("의뢰자는 자신의 의뢰자 정보를 조회할 수 있다.") @WithCustomMockUser(memberType = MemberType.ROLE_CLIENT) - void getMyClient() throws Exception { - given(clientQuery.findClientByMemberId(memberId)).willReturn(response); + void 의뢰자는_자신의_의뢰자_정보를_조회할_수_있다() throws Exception { + given(clientQuery.findClientBy(memberId)).willReturn(response); perform() .andExpect(status().isOk()) - .andDo( - restDocs.document( - requestHeaders(authorizationHeaderDescriptor()), - PayloadDocumentationHelper.responseFields(ClientResponseDescription.description()) - ) - ) + .andDo(document("client/get-my-client", + ResourceSnippetParameters.builder() + .tag("의뢰자") + .description("의뢰자는 자신의 의뢰자 정보를 조회할 수 있다.") + .summary("의뢰자 정보 조회") + .responseSchema(Schema.schema("ClientResponse")), + requestHeaders(authorizationHeaderDescription()), + responseFields(clientResponseDescription()) + )) .andDo(print()); } } diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/client/query/description/ClientResponseDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/client/query/description/ClientResponseDescription.java new file mode 100644 index 00000000..4580d12c --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/client/query/description/ClientResponseDescription.java @@ -0,0 +1,25 @@ +package es.princip.getp.api.controller.client.query.description; + +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + +public class ClientResponseDescription { + + public static FieldDescriptor[] clientResponseDescription() { + return new FieldDescriptor[]{ + statusField(), + fieldWithPath("data.clientId").description("의뢰자 ID"), + fieldWithPath("data.email").description("이메일"), + fieldWithPath("data.nickname").description("닉네임"), + fieldWithPath("data.phoneNumber").description("전화번호"), + fieldWithPath("data.profileImageUri").description("프로필 이미지 URI"), + fieldWithPath("data.createdAt").description("의뢰자 정보 등록 일시"), + fieldWithPath("data.updatedAt").description("최근 의뢰자 정보 수정 일시"), + fieldWithPath("data.address.zipcode").description("우편번호"), + fieldWithPath("data.address.street").description("도로명 주소"), + fieldWithPath("data.address.detail").description("상세 주소") + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/common/description/BankAccountDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/common/description/BankAccountDescription.java new file mode 100644 index 00000000..67fab65c --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/common/description/BankAccountDescription.java @@ -0,0 +1,16 @@ +package es.princip.getp.api.controller.common.description; + +import org.springframework.restdocs.payload.FieldDescriptor; + +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + +public class BankAccountDescription { + + public static FieldDescriptor[] description() { + return new FieldDescriptor[] { + fieldWithPath("bankAccount.bank").description("은행명"), + fieldWithPath("bankAccount.accountNumber").description("계좌번호"), + fieldWithPath("bankAccount.accountHolder").description("예금주"), + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/common/fixture/AddressResponseFixture.java b/get-p-api/src/test/java/es/princip/getp/api/controller/common/fixture/AddressResponseFixture.java new file mode 100644 index 00000000..d357e8c8 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/common/fixture/AddressResponseFixture.java @@ -0,0 +1,18 @@ +package es.princip.getp.api.controller.common.fixture; + +import es.princip.getp.application.common.dto.response.AddressResponse; +import es.princip.getp.domain.client.model.Address; + +import static es.princip.getp.fixture.client.AddressFixture.address; + +public class AddressResponseFixture { + + public static AddressResponse addressResponse() { + final Address address = address(); + return new AddressResponse( + address.getZipcode(), + address.getStreet(), + address.getDetail() + ); + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/common/fixture/HashtagDtoFixture.java b/get-p-api/src/test/java/es/princip/getp/api/controller/common/fixture/HashtagDtoFixture.java new file mode 100644 index 00000000..014c0b04 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/common/fixture/HashtagDtoFixture.java @@ -0,0 +1,22 @@ +package es.princip.getp.api.controller.common.fixture; + +import es.princip.getp.domain.common.model.Hashtag; + +import java.util.List; + +import static es.princip.getp.fixture.common.HashtagFixture.hashtags; + +public class HashtagDtoFixture { + + public static List hashtagsRequest() { + return hashtags().stream() + .map(Hashtag::getValue) + .toList(); + } + + public static List hashtagsResponse() { + return hashtags().stream() + .map(Hashtag::getValue) + .toList(); + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/common/fixture/TechStackDtoFixture.java b/get-p-api/src/test/java/es/princip/getp/api/controller/common/fixture/TechStackDtoFixture.java new file mode 100644 index 00000000..8b415053 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/common/fixture/TechStackDtoFixture.java @@ -0,0 +1,22 @@ +package es.princip.getp.api.controller.common.fixture; + +import es.princip.getp.domain.common.model.TechStack; + +import java.util.List; + +import static es.princip.getp.fixture.common.TechStackFixture.techStacks; + +public class TechStackDtoFixture { + + public static List techStacksRequest() { + return techStacks().stream() + .map(TechStack::getValue) + .toList(); + } + + public static List techStacksResponse() { + return techStacks().stream() + .map(TechStack::getValue) + .toList(); + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/like/command/PeopleLikeControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/like/command/PeopleLikeControllerTest.java new file mode 100644 index 00000000..8afc6544 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/like/command/PeopleLikeControllerTest.java @@ -0,0 +1,82 @@ +package es.princip.getp.api.controller.like.command; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; +import es.princip.getp.api.security.annotation.WithCustomMockUser; +import es.princip.getp.api.support.ControllerTest; +import es.princip.getp.application.like.people.port.in.LikePeopleUseCase; +import es.princip.getp.application.like.people.port.in.UnlikePeopleUseCase; +import es.princip.getp.domain.member.model.MemberType; +import es.princip.getp.domain.people.model.PeopleId; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescription; +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.willDoNothing; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class PeopleLikeControllerTest extends ControllerTest { + + @Autowired private LikePeopleUseCase likePeopleUseCase; + @Autowired private UnlikePeopleUseCase unlikePeopleUseCase; + + @Nested + class 피플_좋아요 { + + private final PeopleId peopleId = new PeopleId(1L); + + @Test + @WithCustomMockUser(memberType = MemberType.ROLE_CLIENT) + void 의뢰자는_피플에게_좋아요를_누를_수_있다() throws Exception { + willDoNothing().given(likePeopleUseCase).like(any(), eq(peopleId)); + + mockMvc.perform(post("/people/{peopleId}/likes", peopleId.getValue()) + .header("Authorization", "Bearer ${ACCESS_TOKEN}")) + .andExpect(status().isCreated()) + .andDo(document("like/like-people", + ResourceSnippetParameters.builder() + .tag("좋아요") + .description("의뢰자는 피플에게 좋아요를 누를 수 있다.") + .summary("피플 좋아요") + .responseSchema(Schema.schema("StatusResponse")), + requestHeaders(authorizationHeaderDescription()), + responseFields(statusField()) + )) + .andDo(print()); + } + } + + @Nested + class 피플_좋아요_취소 { + + private final PeopleId peopleId = new PeopleId(1L); + + @Test + @WithCustomMockUser(memberType = MemberType.ROLE_CLIENT) + void 의뢰자는_피플에게_눌렀던_좋아요를_취소를_할_수_있다() throws Exception { + willDoNothing().given(unlikePeopleUseCase).unlike(any(), eq(peopleId)); + + mockMvc.perform(delete("/people/{peopleId}/likes", peopleId.getValue()) + .header("Authorization", "Bearer ${ACCESS_TOKEN}")) + .andExpect(status().isNoContent()) + .andDo(document("like/unlike-people", + ResourceSnippetParameters.builder() + .tag("좋아요") + .description("의뢰자는 피플에게 눌렀던 좋아요를 취소를 할 수 있다.") + .summary("피플 좋아요 취소") + .responseSchema(Schema.schema("StatusResponse")), + requestHeaders(authorizationHeaderDescription()), + responseFields(statusField()) + )) + .andDo(print()); + } + } +} \ No newline at end of file diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/like/command/ProjectLikeControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/like/command/ProjectLikeControllerTest.java new file mode 100644 index 00000000..5201b307 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/like/command/ProjectLikeControllerTest.java @@ -0,0 +1,93 @@ +package es.princip.getp.api.controller.like.command; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; +import es.princip.getp.api.security.annotation.WithCustomMockUser; +import es.princip.getp.api.support.ControllerTest; +import es.princip.getp.application.like.project.port.in.LikeProjectUseCase; +import es.princip.getp.application.like.project.port.in.UnlikeProjectUseCase; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.member.model.MemberType; +import es.princip.getp.domain.project.commission.model.ProjectId; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.web.servlet.ResultActions; + +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescription; +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; +import static org.mockito.BDDMockito.willDoNothing; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class ProjectLikeControllerTest extends ControllerTest { + + @Autowired private LikeProjectUseCase likeProjectUseCase; + @Autowired private UnlikeProjectUseCase unlikeProjectUseCase; + + @Nested + class 프로젝트_좋아요 { + + private final MemberId memberId = new MemberId(1L); + private final ProjectId projectId = new ProjectId(1L); + + private ResultActions perform() throws Exception { + return mockMvc.perform(post("/projects/{projectId}/likes", projectId.getValue()) + .header("Authorization", "Bearer ${ACCESS_TOKEN}")); + } + + @Test + @WithCustomMockUser(memberType = MemberType.ROLE_PEOPLE) + void 피플은_프로젝트에_좋아요를_누를_수_있다() throws Exception { + willDoNothing().given(likeProjectUseCase).like(memberId, projectId); + + perform() + .andExpect(status().isCreated()) + .andDo(document("like/like-project", + ResourceSnippetParameters.builder() + .tag("좋아요") + .description("피플은 프로젝트에 좋아요를 누를 수 있다.") + .summary("프로젝트 좋아요") + .responseSchema(Schema.schema("StatusResponse")), + requestHeaders(authorizationHeaderDescription()), + responseFields(statusField()) + )) + .andDo(print()); + } + } + + @Nested + class 프로젝트_좋아요_취소 { + + private final MemberId memberId = new MemberId(1L); + private final ProjectId projectId = new ProjectId(1L); + + private ResultActions perform() throws Exception { + return mockMvc.perform(delete("/projects/{projectId}/likes", projectId.getValue()) + .header("Authorization", "Bearer ${ACCESS_TOKEN}")); + } + + @Test + @WithCustomMockUser(memberType = MemberType.ROLE_PEOPLE) + void 피플은_프로젝트에_눌렀던_좋아요를_취소할_수_있다() throws Exception { + willDoNothing().given(unlikeProjectUseCase).unlike(memberId, projectId); + + perform() + .andExpect(status().isNoContent()) + .andDo(document("like/unlike-project", + ResourceSnippetParameters.builder() + .tag("좋아요") + .description("피플은 프로젝트에 눌렀던 좋아요를 취소할 수 있다.") + .summary("프로젝트 좋아요 취소") + .responseSchema(Schema.schema("StatusResponse")), + requestHeaders(authorizationHeaderDescription()), + responseFields(statusField()) + )) + .andDo(print()); + } + + } +} diff --git a/src/test/java/es/princip/getp/api/controller/member/command/MyMemberControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/member/command/MyMemberControllerTest.java similarity index 52% rename from src/test/java/es/princip/getp/api/controller/member/command/MyMemberControllerTest.java rename to get-p-api/src/test/java/es/princip/getp/api/controller/member/command/MyMemberControllerTest.java index f3c73cd7..73c1adb8 100644 --- a/src/test/java/es/princip/getp/api/controller/member/command/MyMemberControllerTest.java +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/member/command/MyMemberControllerTest.java @@ -1,23 +1,26 @@ package es.princip.getp.api.controller.member.command; -import es.princip.getp.api.support.ControllerTest; +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; import es.princip.getp.api.security.annotation.WithCustomMockUser; -import es.princip.getp.application.member.command.RegisterProfileImageCommand; +import es.princip.getp.api.support.ControllerTest; +import es.princip.getp.application.member.dto.command.RegisterProfileImageCommand; import es.princip.getp.application.member.port.in.ProfileImageUseCase; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.member.model.MemberType; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; -import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescriptor; -import static es.princip.getp.api.docs.PayloadDocumentationHelper.responseFields; +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescription; +import static es.princip.getp.api.controller.member.fixture.MultipartFileFixture.imageMultiPartFile; import static es.princip.getp.fixture.member.ProfileImageFixture.profileImage; -import static es.princip.getp.fixture.storage.MultipartFileFixture.imageMultiPartFile; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; import static org.springframework.restdocs.request.RequestDocumentation.partWithName; import static org.springframework.restdocs.request.RequestDocumentation.requestParts; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; @@ -29,33 +32,35 @@ class MyMemberControllerTest extends ControllerTest { private ProfileImageUseCase profileImageUseCase; @Nested - @DisplayName("프로필 업로드") - class UploadProfileImage { + class 프로필_사진_업로드 { - private final Long memberId = 1L; + private final MemberId memberId = new MemberId(1L); - @DisplayName("사용자는 프로필 사진을 올릴 수 있다.") - @WithCustomMockUser(memberType = MemberType.ROLE_PEOPLE) @Test - public void uploadProfileImage() throws Exception { + @WithCustomMockUser(memberType = MemberType.ROLE_PEOPLE) + public void 사용자는_프로필_사진을_올릴_수_있다() throws Exception { given(profileImageUseCase.registerProfileImage(any(RegisterProfileImageCommand.class))) .willReturn(profileImage(memberId).getUrl()); mockMvc.perform(multipart("/member/me/profile-image") .file(imageMultiPartFile()) - .header("Authorization", "Bearer ${ACCESS_TOKEN}") - ) - .andExpect(status().isCreated()) - .andDo( - restDocs.document( - requestHeaders(authorizationHeaderDescriptor()), + .header("Authorization", "Bearer ${ACCESS_TOKEN}")) + .andExpect(status().isCreated()) + .andDo(document("member/upload-profile-image", + ResourceSnippetParameters.builder() + .tag("회원") + .description("사용자는 프로필 사진을 올릴 수 있다.") + .summary("프로필 사진 업로드") + .requestSchema(Schema.schema("UploadProfileImageRequest")) + .responseSchema(Schema.schema("UploadProfileImageResponse")), + requestHeaders(authorizationHeaderDescription()), requestParts(partWithName("image").description("프로필 이미지")), responseFields( - getDescriptor("profileImageUri", "업로드된 프로필 사진의 URI") + fieldWithPath("status").description("응답 상태"), + fieldWithPath("data.profileImageUri").description("업로드된 프로필 사진의 URI") ) - ) - ) - .andDo(print()); + )) + .andDo(print()); } } } \ No newline at end of file diff --git a/src/test/java/es/princip/getp/fixture/storage/MultipartFileFixture.java b/get-p-api/src/test/java/es/princip/getp/api/controller/member/fixture/MultipartFileFixture.java similarity index 57% rename from src/test/java/es/princip/getp/fixture/storage/MultipartFileFixture.java rename to get-p-api/src/test/java/es/princip/getp/api/controller/member/fixture/MultipartFileFixture.java index 9e3e7f5e..35ec1f5e 100644 --- a/src/test/java/es/princip/getp/fixture/storage/MultipartFileFixture.java +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/member/fixture/MultipartFileFixture.java @@ -1,4 +1,4 @@ -package es.princip.getp.fixture.storage; +package es.princip.getp.api.controller.member.fixture; import org.springframework.mock.web.MockMultipartFile; @@ -10,11 +10,4 @@ public static MockMultipartFile imageMultiPartFile() { new byte[] {0x00, 0x01, 0x02, 0x03} ); } - - public static MockMultipartFile fileMultiPartFile() { - return new MockMultipartFile( - "file", - "dummy".getBytes() - ); - } } diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/member/query/MyMemberQueryControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/member/query/MyMemberQueryControllerTest.java new file mode 100644 index 00000000..c2ea2103 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/member/query/MyMemberQueryControllerTest.java @@ -0,0 +1,60 @@ +package es.princip.getp.api.controller.member.query; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; +import es.princip.getp.api.security.annotation.WithCustomMockUser; +import es.princip.getp.api.support.ControllerTest; +import es.princip.getp.domain.member.model.MemberType; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.test.web.servlet.ResultActions; + +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescription; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.snippet.Attributes.key; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class MyMemberQueryControllerTest extends ControllerTest { + + @Nested + class 내_회원_정보_조회 { + + private ResultActions perform() throws Exception { + return mockMvc.perform(get("/member/me") + .header("Authorization", "Bearer ${ACCESS_TOKEN}")); + } + + @Test + @WithCustomMockUser(memberType = MemberType.ROLE_PEOPLE) + void 사용자는_자신의_회원_정보를_조회할_수_있다() throws Exception { + + perform() + .andExpect(status().isOk()) + .andDo(document("member/get-my-member", + ResourceSnippetParameters.builder() + .tag("회원") + .description("사용자는 자신의 회원 정보를 조회할 수 있다.") + .summary("내 회원 정보 조회") + .responseSchema(Schema.schema("MemberResponse")), + requestHeaders(authorizationHeaderDescription()), + responseFields( + fieldWithPath("status").description("응답 상태"), + fieldWithPath("data.memberId").description("회원 ID"), + fieldWithPath("data.email").description("이메일"), + fieldWithPath("data.nickname").description("닉네임"), + fieldWithPath("data.profileImageUri").description("프로필 사진 URI"), + fieldWithPath("data.memberType").description("회원 유형"), + fieldWithPath("data.createdAt").description("회원 가입일") + .attributes(key("format").value("yyyy-MM-dd'T'HH:mm:ss")), + fieldWithPath("data.updatedAt").description("회원 정보 수정일") + .attributes(key("format").value("yyyy-MM-dd'T'HH:mm:ss")) + ) + )) + .andDo(print()); + } + } +} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/people/command/MyPeopleControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/people/command/MyPeopleControllerTest.java similarity index 52% rename from src/test/java/es/princip/getp/api/controller/people/command/MyPeopleControllerTest.java rename to get-p-api/src/test/java/es/princip/getp/api/controller/people/command/MyPeopleControllerTest.java index fe3a2dce..a72fd127 100644 --- a/src/test/java/es/princip/getp/api/controller/people/command/MyPeopleControllerTest.java +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/people/command/MyPeopleControllerTest.java @@ -1,26 +1,28 @@ package es.princip.getp.api.controller.people.command; -import es.princip.getp.api.support.ControllerTest; -import es.princip.getp.api.controller.people.command.description.request.CreatePeopleRequestDescription; -import es.princip.getp.api.controller.people.command.description.request.UpdatePeopleRequestDescription; -import es.princip.getp.api.controller.people.command.description.response.CreatePeopleResponseDescription; +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; import es.princip.getp.api.controller.people.command.dto.request.EditPeopleRequest; import es.princip.getp.api.controller.people.command.dto.request.RegisterPeopleRequest; import es.princip.getp.api.security.annotation.WithCustomMockUser; -import es.princip.getp.application.people.command.EditPeopleCommand; -import es.princip.getp.application.people.command.RegisterPeopleCommand; +import es.princip.getp.api.support.ControllerTest; +import es.princip.getp.application.people.dto.command.EditPeopleCommand; +import es.princip.getp.application.people.dto.command.RegisterPeopleCommand; import es.princip.getp.application.people.port.in.EditPeopleUseCase; import es.princip.getp.application.people.port.in.RegisterPeopleUseCase; -import es.princip.getp.domain.people.model.PeopleType; -import org.junit.jupiter.api.DisplayName; +import es.princip.getp.domain.member.model.MemberType; +import es.princip.getp.domain.people.model.PeopleId; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.web.servlet.ResultActions; -import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescriptor; -import static es.princip.getp.api.docs.PayloadDocumentationHelper.responseFields; -import static es.princip.getp.domain.member.model.MemberType.ROLE_CLIENT; +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.controller.people.command.description.request.EditPeopleRequestDescription.editPeopleRequestDescription; +import static es.princip.getp.api.controller.people.command.description.request.RegisterPeopleRequestDescription.registerPeopleRequestDescription; +import static es.princip.getp.api.controller.people.command.description.response.RegisterPeopleResponseDescription.registerPeopleResponseDescription; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescription; +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; import static es.princip.getp.domain.member.model.MemberType.ROLE_PEOPLE; import static es.princip.getp.fixture.common.EmailFixture.EMAIL; import static es.princip.getp.fixture.member.NicknameFixture.NICKNAME; @@ -30,6 +32,7 @@ import static org.mockito.BDDMockito.willDoNothing; import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -38,17 +41,15 @@ class MyPeopleControllerTest extends ControllerTest { @Autowired private RegisterPeopleUseCase registerPeopleUseCase; @Autowired private EditPeopleUseCase editPeopleUseCase; - @DisplayName("피플은 자신의 정보를 등록할 수 있다.") @Nested - class CreateMyPeople { + class 내_피플_정보_등록 { private final RegisterPeopleRequest request = new RegisterPeopleRequest( NICKNAME, EMAIL, - PHONE_NUMBER, - PeopleType.INDIVIDUAL + PHONE_NUMBER ); - private final Long peopleId = 1L; + private final PeopleId peopleId = new PeopleId(1L); private ResultActions perform() throws Exception { return mockMvc.perform(post("/people/me") @@ -56,37 +57,35 @@ private ResultActions perform() throws Exception { .content(objectMapper.writeValueAsString(request))); } - @WithCustomMockUser(memberType = ROLE_PEOPLE) @Test - public void createMyPeople() throws Exception { + @WithCustomMockUser(memberType = ROLE_PEOPLE) + public void 피플은_자신의_정보를_등록할_수_있다() throws Exception { given(registerPeopleUseCase.register(any(RegisterPeopleCommand.class))).willReturn(peopleId); perform() .andExpect(status().isCreated()) - .andDo(restDocs.document( - requestHeaders(authorizationHeaderDescriptor()), - requestFields(CreatePeopleRequestDescription.description()), - responseFields(CreatePeopleResponseDescription.description()) + .andDo(document("people/register-my-people", + ResourceSnippetParameters.builder() + .tag("피플") + .description("피플은 자신의 정보를 등록할 수 있다.") + .summary("내 피플 정보 등록") + .requestSchema(Schema.schema("RegisterMyPeopleRequest")) + .responseSchema(Schema.schema("RegisterMyPeopleResponse")), + requestHeaders(authorizationHeaderDescription()), + requestFields(registerPeopleRequestDescription()), + responseFields(registerPeopleResponseDescription()) )) .andDo(print()); } - - @WithCustomMockUser(memberType = ROLE_CLIENT) - @Test - public void createMyPeople_WhenMemberTypeIsClient_ShouldFail() throws Exception { - expectForbidden(perform()); - } } - @DisplayName("피플은 자신의 정보를 수정할 수 있다.") @Nested - class UpdateMyPeople { + class 내_피플_정보_수정 { private final EditPeopleRequest request = new EditPeopleRequest( NICKNAME, EMAIL, - PHONE_NUMBER, - PeopleType.INDIVIDUAL + PHONE_NUMBER ); private ResultActions perform() throws Exception { @@ -95,24 +94,25 @@ private ResultActions perform() throws Exception { .content(objectMapper.writeValueAsString(request))); } - @WithCustomMockUser(memberType = ROLE_PEOPLE) @Test - public void updateMyPeople() throws Exception { + @WithCustomMockUser(memberType = MemberType.ROLE_PEOPLE) + public void 피플은_자신의_정보를_수정할_수_있다() throws Exception { willDoNothing().given(editPeopleUseCase).edit(any(EditPeopleCommand.class)); perform() .andExpect(status().isCreated()) - .andDo(restDocs.document( - requestHeaders(authorizationHeaderDescriptor()), - requestFields(UpdatePeopleRequestDescription.description()) + .andDo(document("people/edit-my-people", + ResourceSnippetParameters.builder() + .tag("피플") + .description("피플은 자신의 정보를 수정할 수 있다.") + .summary("내 피플 정보 수정") + .requestSchema(Schema.schema("EditMyPeopleRequest")) + .responseSchema(Schema.schema("StatusResponse")), + requestHeaders(authorizationHeaderDescription()), + requestFields(editPeopleRequestDescription()), + responseFields(statusField()) )) .andDo(print()); } - - @WithCustomMockUser(memberType = ROLE_CLIENT) - @Test - public void updateMyPeople_WhenMemberTypeIsClient_ShouldFail() throws Exception { - expectForbidden(perform()); - } } } \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/people/command/MyPeopleProfileControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/people/command/MyPeopleProfileControllerTest.java similarity index 54% rename from src/test/java/es/princip/getp/api/controller/people/command/MyPeopleProfileControllerTest.java rename to get-p-api/src/test/java/es/princip/getp/api/controller/people/command/MyPeopleProfileControllerTest.java index 69e2d578..7f0ac282 100644 --- a/src/test/java/es/princip/getp/api/controller/people/command/MyPeopleProfileControllerTest.java +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/people/command/MyPeopleProfileControllerTest.java @@ -1,34 +1,37 @@ package es.princip.getp.api.controller.people.command; -import es.princip.getp.api.support.ControllerTest; -import es.princip.getp.api.controller.people.command.description.request.EditPeopleProfileRequestDescription; -import es.princip.getp.api.controller.people.command.description.request.WritePeopleProfileRequestDescription; +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; import es.princip.getp.api.controller.people.command.dto.request.EditPeopleProfileRequest; import es.princip.getp.api.controller.people.command.dto.request.RegisterPeopleProfileRequest; import es.princip.getp.api.security.annotation.WithCustomMockUser; -import es.princip.getp.application.people.command.EditPeopleProfileCommand; -import es.princip.getp.application.people.command.RegisterPeopleProfileCommand; +import es.princip.getp.api.support.ControllerTest; +import es.princip.getp.application.people.dto.command.EditPeopleProfileCommand; +import es.princip.getp.application.people.dto.command.RegisterPeopleProfileCommand; import es.princip.getp.application.people.port.in.EditPeopleProfileUseCase; import es.princip.getp.application.people.port.in.RegisterPeopleProfileUseCase; -import org.junit.jupiter.api.DisplayName; +import es.princip.getp.domain.member.model.MemberType; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.web.servlet.ResultActions; -import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescriptor; -import static es.princip.getp.domain.member.model.MemberType.ROLE_CLIENT; -import static es.princip.getp.domain.member.model.MemberType.ROLE_PEOPLE; -import static es.princip.getp.fixture.common.HashtagFixture.hashtagsRequest; -import static es.princip.getp.fixture.common.TechStackFixture.techStacksRequest; +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.controller.common.fixture.HashtagDtoFixture.hashtagsRequest; +import static es.princip.getp.api.controller.common.fixture.TechStackDtoFixture.techStacksRequest; +import static es.princip.getp.api.controller.people.fixture.PortfolioFixture.portfoliosRequest; +import static es.princip.getp.api.controller.people.command.description.request.EditPeopleProfileRequestDescription.editPeopleProfileRequestDescription; +import static es.princip.getp.api.controller.people.command.description.request.RegisterPeopleProfileRequestDescription.registerPeopleProfileRequestDescription; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescription; +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; import static es.princip.getp.fixture.people.ActivityAreaFixture.activityArea; import static es.princip.getp.fixture.people.EducationFixture.education; import static es.princip.getp.fixture.people.IntroductionFixture.introduction; -import static es.princip.getp.fixture.people.PortfolioFixture.portfoliosRequest; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.willDoNothing; import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -39,9 +42,8 @@ class MyPeopleProfileControllerTest extends ControllerTest { private static final String MY_PEOPLE_PROFILE_URI = "/people/me/profile"; - @DisplayName("피플은 자신의 프로필을 등록할 수 있다.") @Nested - class WriteMyPeopleProfile { + class 내_피플_프로필_등록 { private final RegisterPeopleProfileRequest request = new RegisterPeopleProfileRequest( education(), @@ -58,31 +60,31 @@ private ResultActions perform() throws Exception { .content(objectMapper.writeValueAsString(request))); } - @WithCustomMockUser(memberType = ROLE_PEOPLE) @Test - void writeMyPeopleProfile() throws Exception { + @WithCustomMockUser(memberType = MemberType.ROLE_PEOPLE) + void 피플은_자신의_프로필을_등록할_수_있다() throws Exception { willDoNothing().given(registerPeopleProfileUseCase) .register(any(RegisterPeopleProfileCommand.class)); perform() .andExpect(status().isCreated()) - .andDo(restDocs.document( - requestHeaders(authorizationHeaderDescriptor()), - requestFields(WritePeopleProfileRequestDescription.description()) + .andDo(document("people/register-my-people-profile", + ResourceSnippetParameters.builder() + .tag("피플") + .description("피플은 자신의 프로필을 등록할 수 있다.") + .summary("내 피플 프로필 등록") + .requestSchema(Schema.schema("RegisterMyPeopleProfileRequest")) + .responseSchema(Schema.schema("StatusResponse")), + requestHeaders(authorizationHeaderDescription()), + requestFields(registerPeopleProfileRequestDescription()), + responseFields(statusField()) )) .andDo(print()); } - - @WithCustomMockUser(memberType = ROLE_CLIENT) - @Test - void writeMyPeopleProfile_WhenMemberTypeIsClient_ShouldFail() throws Exception { - expectForbidden(perform()); - } } - @DisplayName("피플은 자신의 프로필을 수정할 수 있다.") @Nested - class EditMyPeopleProfile { + class 내_피플_프로필_수정 { private final EditPeopleProfileRequest request = new EditPeopleProfileRequest( education(), @@ -99,25 +101,26 @@ private ResultActions perform() throws Exception { .content(objectMapper.writeValueAsString(request))); } - @WithCustomMockUser(memberType = ROLE_PEOPLE) @Test - void editMyPeopleProfile() throws Exception { + @WithCustomMockUser(memberType = MemberType.ROLE_PEOPLE) + void 피플은_자신의_프로필을_수정할_수_있다() throws Exception { willDoNothing().given(editPeopleProfileUseCase) .edit(any(EditPeopleProfileCommand.class)); perform() .andExpect(status().isOk()) - .andDo(restDocs.document( - requestHeaders(authorizationHeaderDescriptor()), - requestFields(EditPeopleProfileRequestDescription.description()) + .andDo(document("people/edit-my-people-profile", + ResourceSnippetParameters.builder() + .tag("피플") + .description("피플은 자신의 프로필을 수정할 수 있다.") + .summary("내 피플 프로필 수정") + .requestSchema(Schema.schema("EditMyPeopleProfileRequest")) + .responseSchema(Schema.schema("StatusResponse")), + requestHeaders(authorizationHeaderDescription()), + requestFields(editPeopleProfileRequestDescription()), + responseFields(statusField()) )) .andDo(print()); } - - @WithCustomMockUser(memberType = ROLE_CLIENT) - @Test - void editMyPeopleProfile_WhenMemberTypeIsClient_ShouldFail() throws Exception { - expectForbidden(perform()); - } } } \ No newline at end of file diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/people/command/description/request/EditPeopleProfileRequestDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/people/command/description/request/EditPeopleProfileRequestDescription.java new file mode 100644 index 00000000..b95e1903 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/people/command/description/request/EditPeopleProfileRequestDescription.java @@ -0,0 +1,29 @@ +package es.princip.getp.api.controller.people.command.description.request; + +import es.princip.getp.api.controller.people.command.dto.request.EditPeopleProfileRequest; +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.ConstraintDescriptor.fieldWithConstraint; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + +public class EditPeopleProfileRequestDescription { + + public static FieldDescriptor[] editPeopleProfileRequestDescription() { + final Class clazz = EditPeopleProfileRequest.class; + return new FieldDescriptor[] { + fieldWithPath("education").description("학력"), + fieldWithConstraint("education.school", clazz) + .optional() + .description("학교명"), + fieldWithConstraint("education.major", clazz).description("전공명"), + fieldWithConstraint("activityArea", clazz).description("활동 지역"), + fieldWithConstraint("introduction", clazz) + .optional() + .description("소개"), + fieldWithConstraint("techStacks[]", clazz).description("기술 스택"), + fieldWithConstraint("portfolios[].url", clazz).description("포트폴리오 URL"), + fieldWithConstraint("portfolios[].description", clazz).description("포트폴리오 설명"), + fieldWithConstraint("hashtags[]", clazz).description("해시태그") + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/people/command/description/request/EditPeopleRequestDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/people/command/description/request/EditPeopleRequestDescription.java new file mode 100644 index 00000000..46962689 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/people/command/description/request/EditPeopleRequestDescription.java @@ -0,0 +1,18 @@ +package es.princip.getp.api.controller.people.command.description.request; + +import es.princip.getp.api.controller.people.command.dto.request.EditPeopleRequest; +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.ConstraintDescriptor.fieldWithConstraint; + +public class EditPeopleRequestDescription { + + public static FieldDescriptor[] editPeopleRequestDescription() { + final Class clazz = EditPeopleRequest.class; + return new FieldDescriptor[] { + fieldWithConstraint("nickname", clazz).description("닉네임"), + fieldWithConstraint("email", clazz).description("이메일(기본값은 회원 가입 시 기입한 이메일)"), + fieldWithConstraint("phoneNumber", clazz).description("전화번호") + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/people/command/description/request/RegisterPeopleProfileRequestDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/people/command/description/request/RegisterPeopleProfileRequestDescription.java new file mode 100644 index 00000000..1623ac8a --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/people/command/description/request/RegisterPeopleProfileRequestDescription.java @@ -0,0 +1,29 @@ +package es.princip.getp.api.controller.people.command.description.request; + +import es.princip.getp.api.controller.people.command.dto.request.RegisterPeopleProfileRequest; +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.ConstraintDescriptor.fieldWithConstraint; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + +public class RegisterPeopleProfileRequestDescription { + + public static FieldDescriptor[] registerPeopleProfileRequestDescription() { + final Class clazz = RegisterPeopleProfileRequest.class; + return new FieldDescriptor[] { + fieldWithPath("education").description("학력"), + fieldWithConstraint("education.school", clazz) + .optional() + .description("학교명"), + fieldWithConstraint("education.major", clazz).description("전공명"), + fieldWithConstraint("activityArea", clazz).description("활동 지역"), + fieldWithConstraint("introduction", clazz) + .optional() + .description("소개"), + fieldWithConstraint("techStacks[]", clazz).description("기술 스택"), + fieldWithConstraint("portfolios[].url", clazz).description("포트폴리오 URL"), + fieldWithConstraint("portfolios[].description", clazz).description("포트폴리오 설명"), + fieldWithConstraint("hashtags[]", clazz).description("해시태그") + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/people/command/description/request/RegisterPeopleRequestDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/people/command/description/request/RegisterPeopleRequestDescription.java new file mode 100644 index 00000000..5bd07214 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/people/command/description/request/RegisterPeopleRequestDescription.java @@ -0,0 +1,20 @@ +package es.princip.getp.api.controller.people.command.description.request; + +import es.princip.getp.api.controller.people.command.dto.request.RegisterPeopleRequest; +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.ConstraintDescriptor.fieldWithConstraint; + +public class RegisterPeopleRequestDescription { + + public static FieldDescriptor[] registerPeopleRequestDescription() { + final Class clazz = RegisterPeopleRequest.class; + return new FieldDescriptor[] { + fieldWithConstraint("nickname", clazz).description("닉네임"), + fieldWithConstraint("email", clazz) + .optional() + .description("이메일. 미입력 시 회원 정보의 이메일로 등록됩니다."), + fieldWithConstraint("phoneNumber", clazz).description("전화번호") + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/people/command/description/response/RegisterPeopleResponseDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/people/command/description/response/RegisterPeopleResponseDescription.java new file mode 100644 index 00000000..4b6fb210 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/people/command/description/response/RegisterPeopleResponseDescription.java @@ -0,0 +1,16 @@ +package es.princip.getp.api.controller.people.command.description.response; + +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + +public class RegisterPeopleResponseDescription { + + public static FieldDescriptor[] registerPeopleResponseDescription() { + return new FieldDescriptor[] { + statusField(), + fieldWithPath("data.peopleId").description("피플 ID") + }; + } +} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/fixture/people/PortfolioFixture.java b/get-p-api/src/test/java/es/princip/getp/api/controller/people/fixture/PortfolioFixture.java similarity index 64% rename from src/test/java/es/princip/getp/fixture/people/PortfolioFixture.java rename to get-p-api/src/test/java/es/princip/getp/api/controller/people/fixture/PortfolioFixture.java index 6cc637af..6a3ef2c2 100644 --- a/src/test/java/es/princip/getp/fixture/people/PortfolioFixture.java +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/people/fixture/PortfolioFixture.java @@ -1,20 +1,12 @@ -package es.princip.getp.fixture.people; +package es.princip.getp.api.controller.people.fixture; import es.princip.getp.api.controller.people.command.dto.request.PortfolioRequest; -import es.princip.getp.api.controller.people.query.dto.peopleProfile.PortfolioResponse; -import es.princip.getp.domain.people.model.Portfolio; +import es.princip.getp.application.people.dto.response.peopleProfile.PortfolioResponse; import java.util.List; public class PortfolioFixture { - public static List portfolios() { - return List.of( - Portfolio.of("포트폴리오1 내용", "https://github.com/scv1702/1"), - Portfolio.of("포트폴리오2 내용", "https://github.com/scv1702/2") - ); - } - public static List portfoliosRequest() { return List.of( new PortfolioRequest("포트폴리오1 내용", "https://github.com/scv1702/1"), diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/MyPeopleProfileQueryControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/MyPeopleProfileQueryControllerTest.java new file mode 100644 index 00000000..852ed2f0 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/MyPeopleProfileQueryControllerTest.java @@ -0,0 +1,74 @@ +package es.princip.getp.api.controller.people.query; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; +import es.princip.getp.api.security.annotation.WithCustomMockUser; +import es.princip.getp.api.support.ControllerTest; +import es.princip.getp.application.auth.service.PrincipalDetails; +import es.princip.getp.application.people.dto.response.peopleProfile.PeopleProfileDetailResponse; +import es.princip.getp.application.people.port.in.GetMyPeopleQuery; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.member.model.MemberType; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.web.servlet.ResultActions; + +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.controller.common.fixture.HashtagDtoFixture.hashtagsResponse; +import static es.princip.getp.api.controller.common.fixture.TechStackDtoFixture.techStacksResponse; +import static es.princip.getp.api.controller.people.fixture.PortfolioFixture.portfoliosResponse; +import static es.princip.getp.api.controller.people.query.description.DetailPeopleProfileResponseDescription.detailPeopleProfileResponseDescription; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescription; +import static es.princip.getp.fixture.people.ActivityAreaFixture.activityArea; +import static es.princip.getp.fixture.people.EducationFixture.education; +import static es.princip.getp.fixture.people.IntroductionFixture.introduction; +import static org.mockito.BDDMockito.given; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class MyPeopleProfileQueryControllerTest extends ControllerTest { + + @Autowired private GetMyPeopleQuery getMyPeopleQuery; + + private static final String MY_PEOPLE_PROFILE_URI = "/people/me/profile"; + + @Nested + class 내_피플_프로필_조회 { + + private ResultActions perform() throws Exception { + return mockMvc.perform(get(MY_PEOPLE_PROFILE_URI) + .header("Authorization", "Bearer ${ACCESS_TOKEN}")); + } + + @Test + @WithCustomMockUser(memberType = MemberType.ROLE_PEOPLE) + public void 피플은_자신의_프로필을_조회할_수_있다(PrincipalDetails principalDetails) throws Exception { + final MemberId memberId = principalDetails.getMember().getId(); + final PeopleProfileDetailResponse response = new PeopleProfileDetailResponse( + introduction(), + activityArea(), + education(), + techStacksResponse(), + hashtagsResponse(), + portfoliosResponse() + ); + given(getMyPeopleQuery.getDetailProfileBy(memberId)).willReturn(response); + + perform() + .andExpect(status().isOk()) + .andDo(document("people/get-my-people-profile", + ResourceSnippetParameters.builder() + .tag("피플") + .description("피플은 자신의 프로필을 조회할 수 있다.") + .summary("내 피플 프로필 조회") + .responseSchema(Schema.schema("DetailPeopleProfileResponse")), + requestHeaders(authorizationHeaderDescription()), + responseFields(detailPeopleProfileResponseDescription()) + )) + .andDo(print()); + } + } +} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/people/query/MyPeopleQueryControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/MyPeopleQueryControllerTest.java similarity index 50% rename from src/test/java/es/princip/getp/api/controller/people/query/MyPeopleQueryControllerTest.java rename to get-p-api/src/test/java/es/princip/getp/api/controller/people/query/MyPeopleQueryControllerTest.java index fd5966a3..928d3374 100644 --- a/src/test/java/es/princip/getp/api/controller/people/query/MyPeopleQueryControllerTest.java +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/MyPeopleQueryControllerTest.java @@ -1,14 +1,14 @@ package es.princip.getp.api.controller.people.query; -import es.princip.getp.api.support.ControllerTest; -import es.princip.getp.api.controller.people.query.description.MyPeopleResponseDescription; -import es.princip.getp.api.controller.people.query.dto.people.MyPeopleResponse; -import es.princip.getp.api.docs.PayloadDocumentationHelper; +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; +import es.princip.getp.application.people.dto.response.people.MyPeopleResponse; import es.princip.getp.api.security.annotation.WithCustomMockUser; -import es.princip.getp.api.security.details.PrincipalDetails; +import es.princip.getp.application.auth.service.PrincipalDetails; +import es.princip.getp.api.support.ControllerTest; import es.princip.getp.application.people.port.in.GetMyPeopleQuery; -import es.princip.getp.domain.people.model.PeopleType; -import org.junit.jupiter.api.DisplayName; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.member.model.MemberType; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -16,64 +16,60 @@ import java.time.LocalDateTime; -import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescriptor; -import static es.princip.getp.domain.member.model.MemberType.ROLE_CLIENT; -import static es.princip.getp.domain.member.model.MemberType.ROLE_PEOPLE; +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.controller.people.query.description.MyPeopleResponseDescription.myPeopleResponseDescription; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescription; import static es.princip.getp.fixture.common.EmailFixture.EMAIL; import static es.princip.getp.fixture.member.NicknameFixture.NICKNAME; import static es.princip.getp.fixture.member.PhoneNumberFixture.PHONE_NUMBER; import static es.princip.getp.fixture.member.ProfileImageFixture.profileImage; import static org.mockito.BDDMockito.given; import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; class MyPeopleQueryControllerTest extends ControllerTest { - @Autowired - private GetMyPeopleQuery getMyPeopleQuery; + @Autowired private GetMyPeopleQuery getMyPeopleQuery; @Nested - @DisplayName("내 피플 정보 조회") - class GetMyPeople { + class 내_피플_정보_조회 { private ResultActions perform() throws Exception { return mockMvc.perform(get("/people/me") - .header("Authorization", "Bearer ${ACCESS_TOKEN}")); + .header("Authorization", "Bearer ${ACCESS_TOKEN}")); } @Test - @WithCustomMockUser(memberType = ROLE_PEOPLE) - @DisplayName("피플은 자신의 정보를 조회할 수 있다.") - public void getMyPeople(PrincipalDetails principalDetails) throws Exception { - Long memberId = principalDetails.getMember().getMemberId(); + @WithCustomMockUser(memberType = MemberType.ROLE_PEOPLE) + public void 피플은_자신의_정보를_조회할_수_있다(PrincipalDetails principalDetails) throws Exception { + final MemberId memberId = principalDetails.getMember().getId(); MyPeopleResponse response = new MyPeopleResponse( 1L, EMAIL, NICKNAME, PHONE_NUMBER, - profileImage(1L).getUrl(), - PeopleType.INDIVIDUAL, + profileImage(memberId).getUrl(), 0, 0, LocalDateTime.now(), LocalDateTime.now() ); - given(getMyPeopleQuery.getByMemberId(memberId)).willReturn(response); + given(getMyPeopleQuery.getBy(memberId)).willReturn(response); perform() .andExpect(status().isOk()) - .andDo(restDocs.document( - requestHeaders(authorizationHeaderDescriptor()), - PayloadDocumentationHelper.responseFields(MyPeopleResponseDescription.description()) + .andDo(document("people/get-my-people", + ResourceSnippetParameters.builder() + .tag("피플") + .description("피플은 자신의 정보를 조회할 수 있다.") + .summary("내 피플 정보 조회") + .responseSchema(Schema.schema("MyPeopleResponse")), + requestHeaders(authorizationHeaderDescription()), + responseFields(myPeopleResponseDescription()) )) .andDo(print()); } - - @Test - @WithCustomMockUser(memberType = ROLE_CLIENT) - public void getMyPeople_WhenMemberTypeIsClient_ShouldFail() throws Exception { - expectForbidden(perform()); - } } } \ No newline at end of file diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/PeopleQueryControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/PeopleQueryControllerTest.java new file mode 100644 index 00000000..16210ed3 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/PeopleQueryControllerTest.java @@ -0,0 +1,157 @@ +package es.princip.getp.api.controller.people.query; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; +import es.princip.getp.api.security.annotation.WithCustomMockUser; +import es.princip.getp.api.support.ControllerTest; +import es.princip.getp.application.people.dto.command.GetPeopleCommand; +import es.princip.getp.application.people.dto.response.people.CardPeopleResponse; +import es.princip.getp.application.people.dto.response.people.PeopleDetailResponse; +import es.princip.getp.application.people.dto.response.peopleProfile.CardPeopleProfileResponse; +import es.princip.getp.application.people.dto.response.peopleProfile.PeopleProfileDetailResponse; +import es.princip.getp.application.people.port.in.GetPeopleQuery; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.fixture.member.MemberFixture; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.*; +import org.springframework.test.web.servlet.ResultActions; + +import java.util.List; + +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.controller.common.fixture.HashtagDtoFixture.hashtagsResponse; +import static es.princip.getp.api.controller.common.fixture.TechStackDtoFixture.techStacksResponse; +import static es.princip.getp.api.controller.people.fixture.PortfolioFixture.portfoliosResponse; +import static es.princip.getp.api.controller.people.query.description.DetailPeopleResponseDescription.detailPeopleResponseDescription; +import static es.princip.getp.api.controller.people.query.description.GetCardPeoplePageQueryParametersDescription.getCardPeoplePageQueryParametersDescription; +import static es.princip.getp.api.controller.people.query.description.PagedCardPeopleResponseDescription.pagedCardPeopleResponseDescription; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescription; +import static es.princip.getp.api.docs.PageResponseDescriptor.pageResponseFieldDescriptors; +import static es.princip.getp.domain.member.model.MemberType.ROLE_CLIENT; +import static es.princip.getp.fixture.member.NicknameFixture.NICKNAME; +import static es.princip.getp.fixture.member.ProfileImageFixture.profileImage; +import static es.princip.getp.fixture.people.ActivityAreaFixture.activityArea; +import static es.princip.getp.fixture.people.EducationFixture.education; +import static es.princip.getp.fixture.people.IntroductionFixture.INTRODUCTION; +import static es.princip.getp.fixture.people.IntroductionFixture.introduction; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.spy; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class PeopleQueryControllerTest extends ControllerTest { + + @Autowired private GetPeopleQuery getPeopleQuery; + + @Nested + class 피플_목록_조회 { + + private final MemberId memberId = new MemberId(1L); + + private final int page = 0; + private final int size = 3; + private final Sort sort = Sort.by(Sort.Order.desc("peopleId")); + private final Pageable pageable = PageRequest.of(page, size, sort); + + private ResultActions perform() throws Exception { + return mockMvc.perform(get("/people") + .header("Authorization", "Bearer ${ACCESS_TOKEN}") + .queryParam("page", String.valueOf(page)) + .queryParam("size", String.valueOf(size)) + .queryParam("sort", "peopleId,desc")); + } + + @Test + public void 사용자는_피플_목록을_조회할_수_있다() throws Exception { + final List content = List.of( + new CardPeopleResponse( + 1L, + NICKNAME, + profileImage(memberId).getUrl(), + 3, + 5, + new CardPeopleProfileResponse( + INTRODUCTION, + activityArea(), + hashtagsResponse() + ) + ) + ); + final Page response = new PageImpl<>(content, pageable, content.size()); + given(getPeopleQuery.getPagedCards(any(GetPeopleCommand.class))).willReturn(response); + + perform() + .andExpect(status().isOk()) + .andDo(document("people/get-people-list", + ResourceSnippetParameters.builder() + .tag("피플") + .description("사용자는 피플 목록을 조회할 수 있다.") + .summary("피플 목록 조회") + .responseSchema(Schema.schema("PagedCardPeopleResponse")), + requestHeaders(authorizationHeaderDescription().optional()), + queryParameters(getCardPeoplePageQueryParametersDescription(page, size)), + responseFields(pagedCardPeopleResponseDescription()) + .and(pageResponseFieldDescriptors()) + )) + .andDo(print()); + } + } + + @Nested + class 피플_상세_정보_조회 { + + private final Member member = spy(MemberFixture.member(ROLE_CLIENT)); + private final MemberId memberId = new MemberId(1L); + private final PeopleId peopleId = new PeopleId(1L); + + private ResultActions perform() throws Exception { + return mockMvc.perform(get("/people/{peopleId}", peopleId.getValue()) + .header("Authorization", "Bearer ${ACCESS_TOKEN}")); + } + + @Test + @WithCustomMockUser(memberType = ROLE_CLIENT) + public void 사용자는_피플_상세_정보를_조회할_수있다() throws Exception { + final PeopleDetailResponse response = new PeopleDetailResponse( + 1L, + NICKNAME, + profileImage(memberId).getUrl(), + 0, + 0, + true, + new PeopleProfileDetailResponse( + introduction(), + activityArea(), + education(), + techStacksResponse(), + hashtagsResponse(), + portfoliosResponse() + ) + ); + + given(member.getId()).willReturn(memberId); + given(getPeopleQuery.getDetailBy(any(Member.class), any(PeopleId.class))).willReturn(response); + + perform() + .andExpect(status().isOk()) + .andDo(document("people/get-people", + ResourceSnippetParameters.builder() + .tag("피플") + .description("사용자는 피플 상세 정보를 조회할 수 있다.") + .summary("피플 상세 정보 조회") + .responseSchema(Schema.schema("DetailPeopleResponse")), + requestHeaders(authorizationHeaderDescription().optional()), + pathParameters(parameterWithName("peopleId").description("피플 ID")), + responseFields(detailPeopleResponseDescription()) + )); + } + } +} \ No newline at end of file diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/description/DetailPeopleProfileResponseDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/description/DetailPeopleProfileResponseDescription.java new file mode 100644 index 00000000..c39f5d62 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/description/DetailPeopleProfileResponseDescription.java @@ -0,0 +1,24 @@ +package es.princip.getp.api.controller.people.query.description; + +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + + +public class DetailPeopleProfileResponseDescription { + + public static FieldDescriptor[] detailPeopleProfileResponseDescription() { + return new FieldDescriptor[] { + statusField(), + fieldWithPath("data.education.school").description("학력"), + fieldWithPath("data.education.major").description("전공"), + fieldWithPath("data.activityArea").description("활동 지역"), + fieldWithPath("data.introduction").description("소개"), + fieldWithPath("data.techStacks[]").description("기술 스택"), + fieldWithPath("data.portfolios[].description").description("포트폴리오 설명"), + fieldWithPath("data.portfolios[].url").description("포트폴리오 URL"), + fieldWithPath("data.hashtags[]").description("해시태그") + }; + } +} \ No newline at end of file diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/description/DetailPeopleResponseDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/description/DetailPeopleResponseDescription.java new file mode 100644 index 00000000..be222a31 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/description/DetailPeopleResponseDescription.java @@ -0,0 +1,30 @@ +package es.princip.getp.api.controller.people.query.description; + +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + + +public class DetailPeopleResponseDescription { + + public static FieldDescriptor[] detailPeopleResponseDescription() { + return new FieldDescriptor[] { + statusField(), + fieldWithPath("data.peopleId").description("피플 ID"), + fieldWithPath("data.nickname").description("닉네임"), + fieldWithPath("data.profileImageUri").description("프로필 이미지 URI"), + fieldWithPath("data.completedProjectsCount").description("완수한 프로젝트 수"), + fieldWithPath("data.likesCount").description("받은 좋아요 수"), + fieldWithPath("data.liked").description("좋아요 여부"), + fieldWithPath("data.profile.introduction").description("소개"), + fieldWithPath("data.profile.activityArea").description("활동 지역"), + fieldWithPath("data.profile.techStacks[]").description("기술 스택"), + fieldWithPath("data.profile.education.school").description("학교"), + fieldWithPath("data.profile.education.major").description("전공"), + fieldWithPath("data.profile.hashtags[]").description("해시태그"), + fieldWithPath("data.profile.portfolios[].url").description("포트폴리오 URL"), + fieldWithPath("data.profile.portfolios[].description").description("포트폴리오 설명") + }; + } +} \ No newline at end of file diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/description/GetCardPeoplePageQueryParametersDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/description/GetCardPeoplePageQueryParametersDescription.java new file mode 100644 index 00000000..4dca44da --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/description/GetCardPeoplePageQueryParametersDescription.java @@ -0,0 +1,31 @@ +package es.princip.getp.api.controller.people.query.description; + +import es.princip.getp.domain.member.model.MemberType; +import org.springframework.restdocs.request.ParameterDescriptor; + +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.snippet.Attributes.key; + +public class GetCardPeoplePageQueryParametersDescription { + + public static ParameterDescriptor[] getCardPeoplePageQueryParametersDescription(int page, int pageSize) { + return new ParameterDescriptor[] { + parameterWithName("page").description("페이지 번호") + .optional() + .attributes(key("default").value(String.valueOf(page))), + parameterWithName("size").description("페이지 크기") + .optional() + .attributes(key("default").value(String.valueOf(pageSize))), + parameterWithName("sort").description("정렬 조건") + .optional() + .attributes(key("default").value("peopleId,desc")), + parameterWithName("liked").description("좋아요한 피플만 보기 여부. 필터 설정 시 true로 설정") + .optional() + .attributes(key("default").value("null")) + .attributes(key("permission").value(MemberType.ROLE_CLIENT)), + parameterWithName("keyword").description("피플 닉네임으로 검색하기. 닉네임이 해당 키워드로 시작하는 피플을 검색해요.") + .optional() + .attributes(key("default").value("null")) + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/description/MyPeopleResponseDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/description/MyPeopleResponseDescription.java new file mode 100644 index 00000000..50446725 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/description/MyPeopleResponseDescription.java @@ -0,0 +1,24 @@ +package es.princip.getp.api.controller.people.query.description; + +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + +public class MyPeopleResponseDescription { + + public static FieldDescriptor[] myPeopleResponseDescription() { + return new FieldDescriptor[] { + statusField(), + fieldWithPath("data.peopleId").description("피플 ID"), + fieldWithPath("data.email").description("이메일"), + fieldWithPath("data.nickname").description("닉네임"), + fieldWithPath("data.phoneNumber").description("전화번호"), + fieldWithPath("data.profileImageUri").description("프로필 이미지 URI"), + fieldWithPath("data.completedProjectsCount").description("완수한 프로젝트 수"), + fieldWithPath("data.likesCount").description("받은 좋아요 수"), + fieldWithPath("data.createdAt").description("피플 정보 등록 일시"), + fieldWithPath("data.updatedAt").description("최근 피플 정보 수정 일시") + }; + } +} \ No newline at end of file diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/description/PagedCardPeopleResponseDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/description/PagedCardPeopleResponseDescription.java new file mode 100644 index 00000000..48a53217 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/description/PagedCardPeopleResponseDescription.java @@ -0,0 +1,24 @@ +package es.princip.getp.api.controller.people.query.description; + +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + + +public class PagedCardPeopleResponseDescription { + + public static FieldDescriptor[] pagedCardPeopleResponseDescription() { + return new FieldDescriptor[] { + statusField(), + fieldWithPath("data.content[].peopleId").description("피플 ID"), + fieldWithPath("data.content[].nickname").description("닉네임"), + fieldWithPath("data.content[].profileImageUri").description("프로필 이미지 URI"), + fieldWithPath("data.content[].completedProjectsCount").description("완수한 프로젝트 수"), + fieldWithPath("data.content[].likesCount").description("받은 좋아요 수"), + fieldWithPath("data.content[].profile.introduction").description("소개"), + fieldWithPath("data.content[].profile.activityArea").description("활동 지역"), + fieldWithPath("data.content[].profile.hashtags[]").description("해시태그") + }; + } +} \ No newline at end of file diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/description/PublicDetailPeopleResponseDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/description/PublicDetailPeopleResponseDescription.java new file mode 100644 index 00000000..d324af28 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/people/query/description/PublicDetailPeopleResponseDescription.java @@ -0,0 +1,22 @@ +package es.princip.getp.api.controller.people.query.description; + +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + + +public class PublicDetailPeopleResponseDescription { + + public static FieldDescriptor[] publicDetailPeopleResponseDescription() { + return new FieldDescriptor[] { + statusField(), + fieldWithPath("data.peopleId").description("피플 ID"), + fieldWithPath("data.nickname").description("닉네임"), + fieldWithPath("data.profileImageUri").description("프로필 이미지 URI"), + fieldWithPath("data.completedProjectsCount").description("완수한 프로젝트 수"), + fieldWithPath("data.likesCount").description("받은 좋아요 수"), + fieldWithPath("data.profile.hashtags[]").description("해시태그") + }; + } +} \ No newline at end of file diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/ApproveTeammateControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/ApproveTeammateControllerTest.java new file mode 100644 index 00000000..c03296c0 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/ApproveTeammateControllerTest.java @@ -0,0 +1,57 @@ +package es.princip.getp.api.controller.project.command; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; +import es.princip.getp.api.security.annotation.WithCustomMockUser; +import es.princip.getp.api.support.ControllerTest; +import es.princip.getp.application.project.apply.port.in.ApproveTeammateUseCase; +import es.princip.getp.domain.member.model.MemberType; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.web.servlet.ResultActions; + +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescription; +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class ApproveTeammateControllerTest extends ControllerTest { + + @Autowired private ApproveTeammateUseCase approveTeammateUseCase; + + private final ProjectApplicationId applicationId = new ProjectApplicationId(1L); + + private ResultActions perform() throws Exception { + return mockMvc.perform(get("/teammates/approve", applicationId.getValue()) + .header("Authorization", "Bearer ${ACCESS_TOKEN}") + .queryParam("token", "${TEAMMATE_APPROVAL_TOKEN}")); + } + + @Test + @WithCustomMockUser(memberType = MemberType.ROLE_PEOPLE) + void 피플은_팀원_신청을_승인할_수_있다() throws Exception{ + doNothing().when(approveTeammateUseCase).approve(any(String.class)); + + perform() + .andExpect(status().isOk()) + .andDo(document("project/approve-teammate", + ResourceSnippetParameters.builder() + .tag("프로젝트 지원") + .description("피플은 팀원 신청을 승인할 수 있다.") + .summary("프로젝트 팀원 신청 승인") + .responseSchema(Schema.schema("StatusResponse")), + requestHeaders(authorizationHeaderDescription()), + queryParameters(parameterWithName("token").description("팀원 승인 토큰")), + responseFields(statusField()) + )) + .andDo(print()); + } +} \ No newline at end of file diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/ProjectApplicationControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/ProjectApplicationControllerTest.java new file mode 100644 index 00000000..8acffff8 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/ProjectApplicationControllerTest.java @@ -0,0 +1,99 @@ +package es.princip.getp.api.controller.project.command; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; +import es.princip.getp.api.controller.project.command.dto.request.ApplyProjectRequest; +import es.princip.getp.api.security.annotation.WithCustomMockUser; +import es.princip.getp.api.support.ControllerTest; +import es.princip.getp.application.project.apply.dto.command.ApplyProjectCommand; +import es.princip.getp.application.project.apply.port.in.ApplyProjectUseCase; +import es.princip.getp.domain.member.model.MemberType; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; +import es.princip.getp.domain.project.commission.model.ProjectId; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.web.servlet.ResultActions; + +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.controller.project.command.description.ApplyProjectAsIndividualRequestDescription.applyProjectAsIndividualRequestDescription; +import static es.princip.getp.api.controller.project.command.description.ApplyProjectAsTeamRequestDescription.applyProjectAsTeamRequestDescription; +import static es.princip.getp.api.controller.project.command.description.ApplyProjectResponseDescription.applyProjectResponseDescription; +import static es.princip.getp.api.controller.project.command.fixture.ApplyProjectRequestFixture.applyProjectAsIndividualRequest; +import static es.princip.getp.api.controller.project.command.fixture.ApplyProjectRequestFixture.applyProjectAsTeamRequest; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescription; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class ProjectApplicationControllerTest extends ControllerTest { + + @Autowired private ApplyProjectUseCase applyProjectUseCase; + + @Nested + class 프로젝트_지원 { + + private final ProjectId projectId = new ProjectId(1L); + private final ProjectApplicationId applicationId = new ProjectApplicationId(1L); + + private ResultActions perform(final ApplyProjectRequest request) throws Exception { + return mockMvc.perform(post("/projects/{projectId}/applications", projectId.getValue()) + .header("Authorization", "Bearer ${ACCESS_TOKEN}") + .content(objectMapper.writeValueAsString(request))); + } + + @Test + @WithCustomMockUser(memberType = MemberType.ROLE_PEOPLE) + void 피플은_프로젝트에_개인으로_지원할_수_있다() throws Exception { + final ApplyProjectRequest request = applyProjectAsIndividualRequest(); + given(applyProjectUseCase.apply(any(ApplyProjectCommand.class))) + .willReturn(applicationId); + + perform(request) + .andExpect(status().isCreated()) + .andDo(document("project/apply-project-as-individual", + ResourceSnippetParameters.builder() + .tag("프로젝트 지원") + .description("피플은 프로젝트에 개인/팀으로 지원할 수 있다.") + .summary("프로젝트 지원") + .requestSchema(Schema.schema("ApplyProjectAsIndividualRequest")) + .responseSchema(Schema.schema("ApplyProjectResponse")), + requestHeaders(authorizationHeaderDescription()), + pathParameters(parameterWithName("projectId").description("프로젝트 ID")), + requestFields(applyProjectAsIndividualRequestDescription()), + responseFields(applyProjectResponseDescription()) + )) + .andDo(print()); + } + + @Test + @WithCustomMockUser(memberType = MemberType.ROLE_PEOPLE) + void 피플은_프로젝트에_팀으로_지원할_수_있다() throws Exception { + final ApplyProjectRequest request = applyProjectAsTeamRequest(); + given(applyProjectUseCase.apply(any(ApplyProjectCommand.class))) + .willReturn(applicationId); + + perform(request) + .andExpect(status().isCreated()) + .andDo(document("project/apply-project-as-team", + ResourceSnippetParameters.builder() + .tag("프로젝트 지원") + .description("피플은 프로젝트에 개인/팀으로 지원할 수 있다.") + .summary("프로젝트 지원") + .requestSchema(Schema.schema("ApplyProjectAsTeamRequest")) + .responseSchema(Schema.schema("ApplyProjectResponse")), + requestHeaders(authorizationHeaderDescription()), + pathParameters(parameterWithName("projectId").description("프로젝트 ID")), + requestFields(applyProjectAsTeamRequestDescription()), + responseFields(applyProjectResponseDescription()) + )) + .andDo(print()); + } + } +} \ No newline at end of file diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/ProjectCommissionControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/ProjectCommissionControllerTest.java new file mode 100644 index 00000000..5321e34a --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/ProjectCommissionControllerTest.java @@ -0,0 +1,67 @@ +package es.princip.getp.api.controller.project.command; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; +import es.princip.getp.api.controller.project.command.dto.request.CommissionProjectRequest; +import es.princip.getp.api.security.annotation.WithCustomMockUser; +import es.princip.getp.api.support.ControllerTest; +import es.princip.getp.application.project.commission.ProjectCommissionService; +import es.princip.getp.application.project.commission.dto.command.CommissionProjectCommand; +import es.princip.getp.domain.member.model.MemberType; +import es.princip.getp.domain.project.commission.model.ProjectId; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.web.servlet.ResultActions; + +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.controller.project.command.description.CommissionProjectRequestDescription.commissionProjectRequestDescription; +import static es.princip.getp.api.controller.project.command.description.CommissionProjectResponseDescription.commissionProjectResponseDescription; +import static es.princip.getp.api.controller.project.command.fixture.CommissionProjectRequestFixture.commissionProjectRequest; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescription; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class ProjectCommissionControllerTest extends ControllerTest { + + @Autowired private ProjectCommissionService projectCommissionService; + + @Nested + class 프로젝트_의뢰 { + + private final CommissionProjectRequest request = commissionProjectRequest(); + + private ResultActions perform() throws Exception { + return mockMvc.perform(post("/projects") + .header("Authorization", "Bearer ${ACCESS_TOKEN}") + .content(objectMapper.writeValueAsString(request))); + } + + @Test + @WithCustomMockUser(memberType = MemberType.ROLE_CLIENT) + void 의뢰자는_프로젝트를_의뢰할_수_있다() throws Exception { + given(projectCommissionService.commission(any(CommissionProjectCommand.class))) + .willReturn(new ProjectId(1L)); + + perform() + .andExpect(status().isCreated()) + .andDo(document("project/commission-project", + ResourceSnippetParameters.builder() + .tag("프로젝트 의뢰") + .description("의뢰자는 프로젝트를 의뢰할 수 있다.") + .summary("프로젝트 의뢰") + .requestSchema(Schema.schema("CommissionProjectRequest")) + .responseSchema(Schema.schema("CommissionProjectResponse")), + requestHeaders(authorizationHeaderDescription()), + requestFields(commissionProjectRequestDescription()), + responseFields(commissionProjectResponseDescription()) + )) + .andDo(print()); + } + } +} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/project/command/ProjectMeetingControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/ProjectMeetingControllerTest.java similarity index 53% rename from src/test/java/es/princip/getp/api/controller/project/command/ProjectMeetingControllerTest.java rename to get-p-api/src/test/java/es/princip/getp/api/controller/project/command/ProjectMeetingControllerTest.java index 05ce54ba..3a1e1134 100644 --- a/src/test/java/es/princip/getp/api/controller/project/command/ProjectMeetingControllerTest.java +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/ProjectMeetingControllerTest.java @@ -1,45 +1,41 @@ package es.princip.getp.api.controller.project.command; -import es.princip.getp.api.support.ControllerTest; -import es.princip.getp.api.controller.project.command.description.ScheduleMeetingRequestDescription; -import es.princip.getp.api.controller.project.command.description.ScheduleMeetingResponseDescription; +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; import es.princip.getp.api.controller.project.command.dto.request.ScheduleMeetingRequest; -import es.princip.getp.api.docs.PayloadDocumentationHelper; import es.princip.getp.api.security.annotation.WithCustomMockUser; +import es.princip.getp.api.support.ControllerTest; import es.princip.getp.application.project.meeting.ProjectMeetingService; -import es.princip.getp.application.project.meeting.command.ScheduleMeetingCommand; +import es.princip.getp.application.project.meeting.dto.command.ScheduleMeetingCommand; import es.princip.getp.domain.member.model.MemberType; -import org.junit.jupiter.api.DisplayName; +import es.princip.getp.domain.project.commission.model.ProjectId; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.restdocs.payload.PayloadDocumentation; import org.springframework.test.web.servlet.ResultActions; -import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescriptor; +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.controller.project.command.description.ScheduleMeetingRequestDescription.scheduleMeetingRequestDescription; +import static es.princip.getp.api.controller.project.command.description.ScheduleMeetingResponseDescription.scheduleMeetingResponseDescription; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescription; import static es.princip.getp.fixture.member.PhoneNumberFixture.PHONE_NUMBER; import static es.princip.getp.fixture.project.ProjectMeetingFixture.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; public class ProjectMeetingControllerTest extends ControllerTest { - @Autowired - private ProjectCommandMapper projectCommandMapper; - - @Autowired - private ProjectMeetingService projectMeetingService; + @Autowired private ProjectMeetingService projectMeetingService; @Nested - @DisplayName("프로젝트 미팅 신청") - class ScheduleMeeting { + class 프로젝트_미팅_신청 { - private final Long projectId = 1L; - private final Long memberId = 1L; + private final ProjectId projectId = new ProjectId(1L); private final Long meetingId = 1L; private final Long applicantId = 1L; @@ -52,29 +48,30 @@ class ScheduleMeeting { ); private ResultActions perform() throws Exception { - return mockMvc.perform(post("/projects/{projectId}/meetings", projectId) + return mockMvc.perform(post("/projects/{projectId}/meetings", projectId.getValue()) .header("Authorization", "Bearer ${ACCESS_TOKEN}") .content(objectMapper.writeValueAsString(request))); } @Test @WithCustomMockUser(memberType = MemberType.ROLE_CLIENT) - @DisplayName("의뢰자는 프로젝트 지원자에게 미팅 신청을 할 수 있다.") - void scheduleMeeting() throws Exception { - given(projectCommandMapper.mapToCommand(memberId, projectId, request)) - .willReturn(mock(ScheduleMeetingCommand.class)); + void 의뢰자는_프로젝트_지원자에게_미팅_신청을_할_수_있다() throws Exception { given(projectMeetingService.scheduleMeeting(any(ScheduleMeetingCommand.class))) .willReturn(meetingId); perform() .andExpect(status().isCreated()) - .andDo( - restDocs.document( - requestHeaders(authorizationHeaderDescriptor()), - PayloadDocumentation.requestFields(ScheduleMeetingRequestDescription.description()), - PayloadDocumentationHelper.responseFields(ScheduleMeetingResponseDescription.description()) - ) - ) + .andDo(document("project/schedule-meeting", + ResourceSnippetParameters.builder() + .tag("프로젝트 관리") + .description("의뢰자는 프로젝트 지원자에게 미팅 신청을 할 수 있다.") + .summary("프로젝트 미팅 신청") + .requestSchema(Schema.schema("ScheduleMeetingRequest")) + .responseSchema(Schema.schema("ScheduleMeetingResponse")), + requestHeaders(authorizationHeaderDescription()), + requestFields(scheduleMeetingRequestDescription()), + responseFields(scheduleMeetingResponseDescription()) + )) .andDo(print()); } } diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/description/ApplyProjectAsIndividualRequestDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/description/ApplyProjectAsIndividualRequestDescription.java new file mode 100644 index 00000000..ed496d6b --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/description/ApplyProjectAsIndividualRequestDescription.java @@ -0,0 +1,22 @@ +package es.princip.getp.api.controller.project.command.description; + +import es.princip.getp.api.controller.project.command.dto.request.ApplyProjectAsIndividualRequest; +import es.princip.getp.domain.project.apply.model.ProjectApplicationType; +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.ConstraintDescriptor.fieldWithConstraint; +import static es.princip.getp.api.docs.EnumDescriptor.fieldWithEnum; + +public class ApplyProjectAsIndividualRequestDescription { + + public static FieldDescriptor[] applyProjectAsIndividualRequestDescription() { + final Class clazz = ApplyProjectAsIndividualRequest.class; + return new FieldDescriptor[] { + fieldWithEnum(ProjectApplicationType.class).withPath("type").description("지원 유형. 개인으로 지원할 경우 INDIVIDUAL로 설정"), + fieldWithConstraint("expectedDuration.startDate", clazz).description("예상 작업 시작 기간"), + fieldWithConstraint("expectedDuration.endDate", clazz).description("예상 작업 종료 기간"), + fieldWithConstraint("description", clazz).description("지원 내용"), + fieldWithConstraint("attachmentFiles", clazz).description("첨부 파일") + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/description/ApplyProjectAsTeamRequestDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/description/ApplyProjectAsTeamRequestDescription.java new file mode 100644 index 00000000..75440aa8 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/description/ApplyProjectAsTeamRequestDescription.java @@ -0,0 +1,23 @@ +package es.princip.getp.api.controller.project.command.description; + +import es.princip.getp.api.controller.project.command.dto.request.ApplyProjectAsTeamRequest; +import es.princip.getp.domain.project.apply.model.ProjectApplicationType; +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.ConstraintDescriptor.fieldWithConstraint; +import static es.princip.getp.api.docs.EnumDescriptor.fieldWithEnum; + +public class ApplyProjectAsTeamRequestDescription { + + public static FieldDescriptor[] applyProjectAsTeamRequestDescription() { + final Class clazz = ApplyProjectAsTeamRequest.class; + return new FieldDescriptor[] { + fieldWithEnum(ProjectApplicationType.class).withPath("type").description("지원 유형. 팀으로 지원할 경우 TEAM으로 설정"), + fieldWithConstraint("expectedDuration.startDate", clazz).description("예상 작업 시작 기간"), + fieldWithConstraint("expectedDuration.endDate", clazz).description("예상 작업 종료 기간"), + fieldWithConstraint("description", clazz).description("지원 내용"), + fieldWithConstraint("attachmentFiles", clazz).description("첨부 파일"), + fieldWithConstraint("teammates", clazz).description("팀원 정보. 각 팀원의 피플 ID 리스트") + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/description/ApplyProjectResponseDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/description/ApplyProjectResponseDescription.java new file mode 100644 index 00000000..e0c59973 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/description/ApplyProjectResponseDescription.java @@ -0,0 +1,16 @@ +package es.princip.getp.api.controller.project.command.description; + +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + +public class ApplyProjectResponseDescription { + + public static FieldDescriptor[] applyProjectResponseDescription() { + return new FieldDescriptor[] { + statusField(), + fieldWithPath("data.applicationId").description("프로젝트 지원 ID"), + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/description/CommissionProjectRequestDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/description/CommissionProjectRequestDescription.java new file mode 100644 index 00000000..92348155 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/description/CommissionProjectRequestDescription.java @@ -0,0 +1,30 @@ +package es.princip.getp.api.controller.project.command.description; + +import es.princip.getp.api.controller.project.command.dto.request.CommissionProjectRequest; +import es.princip.getp.domain.project.commission.model.MeetingType; +import es.princip.getp.domain.project.commission.model.ProjectCategory; +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.ConstraintDescriptor.fieldWithConstraint; +import static es.princip.getp.api.docs.EnumDescriptor.fieldWithEnum; + +public class CommissionProjectRequestDescription { + + public static FieldDescriptor[] commissionProjectRequestDescription() { + final Class clazz = CommissionProjectRequest.class; + return new FieldDescriptor[]{ + fieldWithConstraint("title", clazz).description("제목"), + fieldWithConstraint("payment", clazz).description("성공 보수"), + fieldWithConstraint("recruitmentCount", clazz).description("모집 인원"), + fieldWithConstraint("applicationDuration.startDate", clazz).description("지원자 모집 시작 기간"), + fieldWithConstraint("applicationDuration.endDate", clazz).description("지원자 모집 종료 기간"), + fieldWithConstraint("estimatedDuration.startDate", clazz).description("예상 작업 시작 기간"), + fieldWithConstraint("estimatedDuration.endDate", clazz).description("예상 작업 종료 기간"), + fieldWithConstraint("description", clazz).description("상세 설명"), + fieldWithEnum(MeetingType.class).withPath("meetingType").description("미팅 방식"), + fieldWithEnum(ProjectCategory.class).withPath("category").description("카테고리"), + fieldWithConstraint("attachmentFiles[]", clazz).description("첨부 파일"), + fieldWithConstraint("hashtags[]", clazz).description("해시태그"), + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/description/CommissionProjectResponseDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/description/CommissionProjectResponseDescription.java new file mode 100644 index 00000000..87865e27 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/description/CommissionProjectResponseDescription.java @@ -0,0 +1,16 @@ +package es.princip.getp.api.controller.project.command.description; + +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + +public class CommissionProjectResponseDescription { + + public static FieldDescriptor[] commissionProjectResponseDescription() { + return new FieldDescriptor[] { + statusField(), + fieldWithPath("data.projectId").description("프로젝트 ID") + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/description/ScheduleMeetingRequestDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/description/ScheduleMeetingRequestDescription.java new file mode 100644 index 00000000..fef0b5a0 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/description/ScheduleMeetingRequestDescription.java @@ -0,0 +1,22 @@ +package es.princip.getp.api.controller.project.command.description; + +import es.princip.getp.api.controller.project.command.dto.request.ScheduleMeetingRequest; +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.ConstraintDescriptor.fieldWithConstraint; + +public class ScheduleMeetingRequestDescription { + + public static FieldDescriptor[] scheduleMeetingRequestDescription() { + final Class clazz = ScheduleMeetingRequest.class; + return new FieldDescriptor[] { + fieldWithConstraint("applicantId", clazz).description("지원자 ID"), + fieldWithConstraint("location", clazz).description("미팅 장소"), + fieldWithConstraint("schedule.date", clazz).description("미팅 날짜"), + fieldWithConstraint("schedule.startTime", clazz).description("미팅 시작 시간"), + fieldWithConstraint("schedule.endTime", clazz).description("미팅 종료 시간"), + fieldWithConstraint("phoneNumber", clazz).description("연락처"), + fieldWithConstraint("description", clazz).description("요구사항"), + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/description/ScheduleMeetingResponseDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/description/ScheduleMeetingResponseDescription.java new file mode 100644 index 00000000..f175988a --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/description/ScheduleMeetingResponseDescription.java @@ -0,0 +1,16 @@ +package es.princip.getp.api.controller.project.command.description; + +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + +public class ScheduleMeetingResponseDescription { + + public static FieldDescriptor[] scheduleMeetingResponseDescription() { + return new FieldDescriptor[] { + statusField(), + fieldWithPath("data.meetingId").description("미팅 ID"), + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/fixture/ApplyProjectRequestFixture.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/fixture/ApplyProjectRequestFixture.java new file mode 100644 index 00000000..db53bf4d --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/fixture/ApplyProjectRequestFixture.java @@ -0,0 +1,48 @@ +package es.princip.getp.api.controller.project.command.fixture; + +import es.princip.getp.api.controller.project.command.dto.request.ApplyProjectAsIndividualRequest; +import es.princip.getp.api.controller.project.command.dto.request.ApplyProjectAsTeamRequest; +import es.princip.getp.api.controller.project.command.dto.request.ApplyProjectRequest; +import es.princip.getp.domain.common.model.Duration; +import es.princip.getp.domain.project.apply.model.IndividualProjectApplication; +import es.princip.getp.domain.project.apply.model.TeamProjectApplication; + +import java.time.LocalDate; +import java.util.List; +import java.util.Set; + +import static es.princip.getp.fixture.project.ProjectApplicationFixture.DESCRIPTION; + +public class ApplyProjectRequestFixture { + + public static ApplyProjectRequest applyProjectAsIndividualRequest() { + return new ApplyProjectAsIndividualRequest( + IndividualProjectApplication.TYPE, + Duration.of( + LocalDate.of(2024, 7, 1), + LocalDate.of(2024, 7, 31) + ), + DESCRIPTION, + List.of( + "https://example.com/attachment1", + "https://example.com/attachment2" + ) + ); + } + + public static ApplyProjectRequest applyProjectAsTeamRequest() { + return new ApplyProjectAsTeamRequest( + TeamProjectApplication.TYPE, + Duration.of( + LocalDate.of(2024, 7, 1), + LocalDate.of(2024, 7, 31) + ), + DESCRIPTION, + List.of( + "https://example.com/attachment1", + "https://example.com/attachment2" + ), + Set.of(1L, 2L, 3L, 4L) + ); + } +} diff --git a/src/test/java/es/princip/getp/api/controller/project/command/CommissionProjectRequestFixture.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/fixture/CommissionProjectRequestFixture.java similarity index 67% rename from src/test/java/es/princip/getp/api/controller/project/command/CommissionProjectRequestFixture.java rename to get-p-api/src/test/java/es/princip/getp/api/controller/project/command/fixture/CommissionProjectRequestFixture.java index f058054d..74d840cd 100644 --- a/src/test/java/es/princip/getp/api/controller/project/command/CommissionProjectRequestFixture.java +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/command/fixture/CommissionProjectRequestFixture.java @@ -1,4 +1,4 @@ -package es.princip.getp.api.controller.project.command; +package es.princip.getp.api.controller.project.command.fixture; import es.princip.getp.api.controller.project.command.dto.request.CommissionProjectRequest; import es.princip.getp.domain.common.model.Duration; @@ -8,14 +8,16 @@ import java.time.LocalDate; import java.util.List; -import static es.princip.getp.fixture.common.HashtagFixture.hashtagsRequest; +import static es.princip.getp.api.controller.common.fixture.HashtagDtoFixture.hashtagsRequest; +import static es.princip.getp.fixture.project.ProjectFixture.*; -class CommissionProjectRequestFixture { +public class CommissionProjectRequestFixture { - static CommissionProjectRequest registerProjectRequest() { + public static CommissionProjectRequest commissionProjectRequest() { return new CommissionProjectRequest( - "프로젝트 제목", - 1_000_000L, + TITLE, + PAYMENT, + RECRUITMENT_COUNT, Duration.of( LocalDate.of(2024, 7, 1), LocalDate.of(2024, 7, 31) @@ -24,7 +26,7 @@ static CommissionProjectRequest registerProjectRequest() { LocalDate.of(2024, 8, 1), LocalDate.of(2024, 8, 31) ), - "프로젝트 설명", + DESCRIPTION, MeetingType.IN_PERSON, ProjectCategory.BACKEND, List.of( diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/ProjectApplicantQueryControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/ProjectApplicantQueryControllerTest.java new file mode 100644 index 00000000..a86e454c --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/ProjectApplicantQueryControllerTest.java @@ -0,0 +1,115 @@ +package es.princip.getp.api.controller.project.query; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; +import es.princip.getp.api.security.annotation.WithCustomMockUser; +import es.princip.getp.api.support.ControllerTest; +import es.princip.getp.application.project.apply.dto.response.ProjectApplicantResponse; +import es.princip.getp.application.project.apply.dto.response.ProjectApplicantTeammateResponse; +import es.princip.getp.application.project.apply.port.in.GetApplicantQuery; +import es.princip.getp.application.support.CursorPageable; +import es.princip.getp.application.support.dto.SliceResponse; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.project.commission.model.ProjectId; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.SliceImpl; + +import java.util.List; + +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.controller.project.query.description.PagedProjectApplicantResponseFields.pagedProjectApplicantResponseFields; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescription; +import static es.princip.getp.api.docs.PaginationDescription.cursorPaginationParameters; +import static es.princip.getp.api.docs.SliceResponseDescription.sliceResponseDescription; +import static es.princip.getp.domain.member.model.MemberType.ROLE_CLIENT; +import static es.princip.getp.domain.project.apply.model.ProjectApplicationStatus.COMPLETED; +import static es.princip.getp.fixture.member.NicknameFixture.NICKNAME; +import static es.princip.getp.fixture.member.ProfileImageFixture.profileImage; +import static es.princip.getp.fixture.people.EducationFixture.education; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class ProjectApplicantQueryControllerTest extends ControllerTest { + + @Autowired private GetApplicantQuery getApplicantQuery; + + private ProjectApplicantResponse projectApplicantResponse( + final Long id, + final List teammates + ) { + return new ProjectApplicantResponse( + id, + NICKNAME + id, + profileImage(new MemberId(id)).getUrl(), + education(), + COMPLETED, + teammates + ); + } + + private ProjectApplicantTeammateResponse projectApplicantTeammateResponse(final Long id) { + return new ProjectApplicantTeammateResponse( + id, + NICKNAME + id, + profileImage(new MemberId(id)).getUrl() + ); + } + + @Test + @WithCustomMockUser(memberType = ROLE_CLIENT) + void 의뢰자는_프로젝트_지원자_목록을_조회할_수_있다() throws Exception { + final var projectId = new ProjectId(1L); + final var pageSize = 5; + final var content = List.of( + projectApplicantResponse(1L, null), + projectApplicantResponse(2L, null), + projectApplicantResponse(3L, null), + projectApplicantResponse(4L, List.of( + projectApplicantTeammateResponse(5L), + projectApplicantTeammateResponse(6L) + )), + projectApplicantResponse(5L, List.of( + projectApplicantTeammateResponse(6L), + projectApplicantTeammateResponse(7L) + )) + ); + final var response = SliceResponse.of( + new SliceImpl<>(content, PageRequest.of(0, pageSize), true), + "eyJpZCI6IDIwfQ==" + ); + given(getApplicantQuery.getApplicantsBy( + any(CursorPageable.class), + any(Member.class), + eq(projectId) + )) + .willReturn(response); + + mockMvc.perform(get("/projects/{projectId}/applicants", projectId.getValue()) + .header("Authorization", "Bearer ${ACCESS_TOKEN}") + .queryParam("size", String.valueOf(pageSize)) + .queryParam("cursor", "eyJpZCI6IDEwfQ==")) + .andExpect(status().isOk()) + .andDo(document("project/get-applicants", + ResourceSnippetParameters.builder() + .tag("의뢰자 프로젝트 관리") + .description("의뢰자는 프로젝트 지원자 목록을 조회할 수 있다.") + .summary("프로젝트 지원자 목록 조회") + .responseSchema(Schema.schema("PagedProjectApplicantResponse")), + requestHeaders(authorizationHeaderDescription().optional()), + pathParameters(parameterWithName("projectId").description("프로젝트 ID")), + queryParameters(cursorPaginationParameters(10)), + responseFields(pagedProjectApplicantResponseFields()) + .and(sliceResponseDescription()) + )) + .andDo(print()); + } +} \ No newline at end of file diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/ProjectApplicationDetailFormControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/ProjectApplicationDetailFormControllerTest.java new file mode 100644 index 00000000..2a120f4d --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/ProjectApplicationDetailFormControllerTest.java @@ -0,0 +1,117 @@ +package es.princip.getp.api.controller.project.query; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; +import es.princip.getp.api.security.annotation.WithCustomMockUser; +import es.princip.getp.api.support.ControllerTest; +import es.princip.getp.application.project.apply.dto.response.ProjectApplicationDetailTeammateResponse; +import es.princip.getp.application.project.apply.dto.response.ProjectApplicationFormResponse; +import es.princip.getp.application.project.apply.port.in.GetApplicationFormQuery; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; +import es.princip.getp.domain.project.apply.model.TeammateStatus; +import es.princip.getp.domain.project.commission.model.ProjectId; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; + +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.controller.common.fixture.TechStackDtoFixture.techStacksResponse; +import static es.princip.getp.api.controller.people.fixture.PortfolioFixture.portfoliosResponse; +import static es.princip.getp.api.controller.project.query.description.ProjectApplicationFormResponseDescription.projectApplicationFormResponseDescription; +import static es.princip.getp.api.controller.project.query.fixture.ProjectDetailResponseFixture.projectDetailResponse; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescription; +import static es.princip.getp.domain.member.model.MemberType.ROLE_CLIENT; +import static es.princip.getp.domain.project.apply.model.ProjectApplicationStatus.COMPLETED; +import static es.princip.getp.domain.project.apply.model.ProjectApplicationType.TEAM; +import static es.princip.getp.domain.project.apply.model.TeammateStatus.APPROVED; +import static es.princip.getp.domain.project.apply.model.TeammateStatus.PENDING; +import static es.princip.getp.fixture.member.NicknameFixture.NICKNAME; +import static es.princip.getp.fixture.member.ProfileImageFixture.profileImage; +import static es.princip.getp.fixture.people.ActivityAreaFixture.ACTIVITY_AREA; +import static es.princip.getp.fixture.people.EducationFixture.education; +import static es.princip.getp.fixture.people.IntroductionFixture.INTRODUCTION; +import static es.princip.getp.fixture.project.ProjectApplicationFixture.DESCRIPTION; +import static es.princip.getp.fixture.project.ProjectApplicationFixture.expectedDuration; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class ProjectApplicationDetailFormControllerTest extends ControllerTest { + + @Autowired private GetApplicationFormQuery query; + + private ProjectApplicationDetailTeammateResponse teammateResponse( + final Long peopleId, + final TeammateStatus status + ) { + return new ProjectApplicationDetailTeammateResponse( + peopleId, + NICKNAME + peopleId, + status, + profileImage(new MemberId(peopleId)).getUrl() + ); + } + + private ProjectApplicationFormResponse applicationDetailResponse( + final ProjectId projectId, + final ProjectApplicationId applicationId + ) { + return new ProjectApplicationFormResponse( + applicationId.getValue(), + TEAM, + projectDetailResponse(projectId), + NICKNAME, + techStacksResponse(), + ACTIVITY_AREA, + education(), + INTRODUCTION, + portfoliosResponse(), + expectedDuration(), + COMPLETED, + DESCRIPTION, + List.of( + "https://example.com/attachment1", + "https://example.com/attachment2" + ), + List.of( + teammateResponse(2L, APPROVED), + teammateResponse(3L, PENDING), + teammateResponse(4L, APPROVED) + ) + ); + } + + @Test + @WithCustomMockUser(memberType = ROLE_CLIENT) + void 의뢰자는_프로젝트_지원자의_지원서를_조회할_수_있다() throws Exception { + final var projectId = new ProjectId(1L); + final var applicationId = new ProjectApplicationId(1L); + final var response = applicationDetailResponse(projectId, applicationId); + given(query.getApplicationFormBy(any(Member.class), eq(applicationId))) + .willReturn(response); + + mockMvc.perform(get("/applications/{applicationId}", applicationId.getValue()) + .header("Authorization", "Bearer ${ACCESS_TOKEN}")) + .andExpect(status().isOk()) + .andDo(document("project/get-application-form", + ResourceSnippetParameters.builder() + .tag("프로젝트 관리") + .description("의뢰자는 프로젝트 지원자의 지원서를 조회할 수 있다.") + .summary("프로젝트 지원서 조회") + .responseSchema(Schema.schema("ProjectApplicationFormResponse")), + requestHeaders(authorizationHeaderDescription().optional()), + pathParameters(parameterWithName("applicationId").description("프로젝트 지원 ID")), + responseFields(projectApplicationFormResponseDescription()) + )) + .andDo(print()); + } +} \ No newline at end of file diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/ProjectApplicationDetailQueryControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/ProjectApplicationDetailQueryControllerTest.java new file mode 100644 index 00000000..9e94a0d3 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/ProjectApplicationDetailQueryControllerTest.java @@ -0,0 +1,106 @@ +package es.princip.getp.api.controller.project.query; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; +import es.princip.getp.application.project.apply.dto.response.ProjectApplicationDetailResponse; +import es.princip.getp.application.project.apply.dto.response.ProjectApplicationDetailTeammateResponse; +import es.princip.getp.api.security.annotation.WithCustomMockUser; +import es.princip.getp.api.support.ControllerTest; +import es.princip.getp.application.project.apply.port.in.GetApplicationDetailQuery; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; +import es.princip.getp.domain.project.apply.model.TeammateStatus; +import es.princip.getp.domain.project.commission.model.ProjectId; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; + +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.controller.project.query.description.ProjectApplicationDetailResponseDescription.projectApplicationDetailResponseDescription; +import static es.princip.getp.api.controller.project.query.fixture.ProjectDetailResponseFixture.projectDetailResponse; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescription; +import static es.princip.getp.domain.member.model.MemberType.ROLE_PEOPLE; +import static es.princip.getp.domain.project.apply.model.ProjectApplicationStatus.COMPLETED; +import static es.princip.getp.domain.project.apply.model.ProjectApplicationType.TEAM; +import static es.princip.getp.domain.project.apply.model.TeammateStatus.APPROVED; +import static es.princip.getp.domain.project.apply.model.TeammateStatus.PENDING; +import static es.princip.getp.fixture.member.NicknameFixture.NICKNAME; +import static es.princip.getp.fixture.member.ProfileImageFixture.profileImage; +import static es.princip.getp.fixture.project.ProjectApplicationFixture.DESCRIPTION; +import static es.princip.getp.fixture.project.ProjectApplicationFixture.expectedDuration; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class ProjectApplicationDetailQueryControllerTest extends ControllerTest { + + @Autowired private GetApplicationDetailQuery query; + + private ProjectApplicationDetailTeammateResponse teammateResponse( + final Long peopleId, + final TeammateStatus status + ) { + return new ProjectApplicationDetailTeammateResponse( + peopleId, + NICKNAME + peopleId, + status, + profileImage(new MemberId(peopleId)).getUrl() + ); + } + + private ProjectApplicationDetailResponse applicationDetailResponse( + final ProjectId projectId, + final ProjectApplicationId applicationId + ) { + return new ProjectApplicationDetailResponse( + applicationId.getValue(), + TEAM, + projectDetailResponse(projectId), + expectedDuration(), + COMPLETED, + DESCRIPTION, + List.of( + "https://example.com/attachment1", + "https://example.com/attachment2" + ), + List.of( + teammateResponse(2L, APPROVED), + teammateResponse(3L, PENDING), + teammateResponse(4L, APPROVED) + ) + ); + } + + @Test + @WithCustomMockUser(memberType = ROLE_PEOPLE) + void 피플은_자신의_프로젝트_지원_내역을_조회할_수_있다() throws Exception { + final var projectId = new ProjectId(1L); + final var applicationId = new ProjectApplicationId(1L); + final var response = applicationDetailResponse(projectId, applicationId); + given(query.getApplicationDetailBy(any(Member.class), eq(applicationId))) + .willReturn(response); + + mockMvc.perform(get("/applications/me/{applicationId}", applicationId.getValue()) + .header("Authorization", "Bearer ${ACCESS_TOKEN}")) + .andExpect(status().isOk()) + .andDo(document("project/get-application-detail", + ResourceSnippetParameters.builder() + .tag("프로젝트 지원") + .description("피플은 자신의 프로젝트 지원 내역을 조회할 수 있다.") + .summary("프로젝트 지원 내역 조회") + .responseSchema(Schema.schema("ProjectApplicationDetailResponse")), + requestHeaders(authorizationHeaderDescription().optional()), + pathParameters(parameterWithName("applicationId").description("프로젝트 지원 ID")), + responseFields(projectApplicationDetailResponseDescription()) + )) + .andDo(print()); + } +} \ No newline at end of file diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/ProjectQueryControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/ProjectQueryControllerTest.java new file mode 100644 index 00000000..ba98ab03 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/ProjectQueryControllerTest.java @@ -0,0 +1,125 @@ +package es.princip.getp.api.controller.project.query; + + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; +import es.princip.getp.application.project.commission.dto.response.ProjectCardResponse; +import es.princip.getp.application.project.commission.dto.response.ProjectDetailResponse; +import es.princip.getp.api.security.annotation.WithCustomMockUser; +import es.princip.getp.api.support.ControllerTest; +import es.princip.getp.application.project.commission.dto.command.GetProjectCommand; +import es.princip.getp.application.project.commission.port.in.GetProjectQuery; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.project.commission.model.ProjectId; +import es.princip.getp.fixture.member.MemberFixture; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.*; +import org.springframework.test.web.servlet.ResultActions; + +import java.util.List; + +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.controller.project.query.description.DetailProjectResponseDescription.detailProjectResponseDescription; +import static es.princip.getp.api.controller.project.query.description.GetProjectsQueryParametersDescription.getProjectsQueryParametersDescription; +import static es.princip.getp.api.controller.project.query.description.PagedCardProjectResponseDescription.pagedCardProjectResponseDescription; +import static es.princip.getp.api.controller.project.query.fixture.ProjectCardResponseFixture.projectCardResponse; +import static es.princip.getp.api.controller.project.query.fixture.ProjectDetailResponseFixture.projectDetailResponse; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescription; +import static es.princip.getp.api.docs.PageResponseDescriptor.pageResponseFieldDescriptors; +import static es.princip.getp.domain.member.model.MemberType.ROLE_PEOPLE; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.spy; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class ProjectQueryControllerTest extends ControllerTest { + + @Autowired + private GetProjectQuery getProjectQuery; + + @Nested + class 프로젝트_목록_조회 { + + final int page = 0; + final int pageSize = 10; + final Sort sort = Sort.by(Sort.Order.desc("projectId")); + final Pageable pageable = PageRequest.of(page, pageSize, sort); + + private ResultActions perform() throws Exception { + return mockMvc.perform(get("/projects") + .header("Authorization", "Bearer ${ACCESS_TOKEN}") + .queryParam("page", String.valueOf(page)) + .queryParam("size", String.valueOf(pageSize)) + .queryParam("sort", "projectId,desc")); + } + + @Test + void 사용자는_프로젝트_목록을_조회할_수_있다() throws Exception { + final List content = List.of( + projectCardResponse(new ProjectId(1L)), + projectCardResponse(new ProjectId(2L)) + ); + final Page response = new PageImpl<>(content, pageable, content.size()); + given(getProjectQuery.getPagedCards(any(GetProjectCommand.class))) + .willReturn(response); + + perform() + .andExpect(status().isOk()) + .andDo(document("project/get-projects", + ResourceSnippetParameters.builder() + .tag("프로젝트") + .description("사용자는 프로젝트 목록을 조회할 수 있다.") + .summary("프로젝트 목록 조회") + .responseSchema(Schema.schema("PagedCardProjectResponse")), + requestHeaders(authorizationHeaderDescription().optional()), + queryParameters(getProjectsQueryParametersDescription(page, pageSize)), + responseFields(pagedCardProjectResponseDescription()) + .and(pageResponseFieldDescriptors()) + )) + .andDo(print()); + } + } + + @Nested + class 프로젝트_상세_조회 { + + private final Member member = spy(MemberFixture.member(ROLE_PEOPLE)); + private final MemberId memberId = new MemberId(1L); + private final ProjectId projectId = new ProjectId(1L); + + private ResultActions perform() throws Exception { + return mockMvc.perform(get("/projects/{projectId}", projectId.getValue()) + .header("Authorization", "Bearer ${ACCESS_TOKEN}")); + } + + @Test + @WithCustomMockUser(memberType = ROLE_PEOPLE) + void 사용자는_프로젝트의_상세_정보를_조회할_수_있다() throws Exception { + final ProjectDetailResponse response = projectDetailResponse(projectId); + + given(member.getId()).willReturn(memberId); + given(getProjectQuery.getDetailBy(any(Member.class), any(ProjectId.class))).willReturn(response); + + perform() + .andExpect(status().isOk()) + .andDo(document("project/get-project", + ResourceSnippetParameters.builder() + .tag("프로젝트") + .description("사용자는 프로젝트의 상세 정보를 조회할 수 있다.") + .summary("프로젝트 상세 조회") + .responseSchema(Schema.schema("DetailProjectResponse")), + requestHeaders(authorizationHeaderDescription().optional()), + pathParameters(parameterWithName("projectId").description("프로젝트 ID")), + responseFields(detailProjectResponseDescription()) + )) + .andDo(print()); + } + } +} \ No newline at end of file diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/SearchTeammateControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/SearchTeammateControllerTest.java new file mode 100644 index 00000000..42eae5c1 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/SearchTeammateControllerTest.java @@ -0,0 +1,88 @@ +package es.princip.getp.api.controller.project.query; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; +import es.princip.getp.api.support.ControllerTest; +import es.princip.getp.application.people.dto.command.SearchTeammateCommand; +import es.princip.getp.application.project.apply.dto.response.SearchTeammateResponse; +import es.princip.getp.application.project.apply.port.in.SearchTeammateQuery; +import es.princip.getp.application.support.dto.SliceResponse; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.project.commission.model.ProjectId; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.SliceImpl; +import org.springframework.test.web.servlet.ResultActions; + +import java.util.List; +import java.util.stream.LongStream; + +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.controller.project.query.description.SearchTeammateQueryParametersDescription.searchTeammateQueryParametersDescription; +import static es.princip.getp.api.controller.project.query.description.SearchTeammateResponseDescription.searchTeammateResponseDescription; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescription; +import static es.princip.getp.api.docs.SliceResponseDescription.sliceResponseDescription; +import static es.princip.getp.fixture.member.NicknameFixture.NICKNAME; +import static es.princip.getp.fixture.member.ProfileImageFixture.profileImage; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class SearchTeammateControllerTest extends ControllerTest { + + @Autowired private SearchTeammateQuery searchTeammateQuery; + + @Nested + class 프로젝트_팀원_검색 { + private final ProjectId projectId = new ProjectId(1L); + private final int pageSize = 5; + + private ResultActions perform() throws Exception { + return mockMvc.perform(get("/projects/{projectId}/teammates", projectId.getValue()) + .header("Authorization", "Bearer ${ACCESS_TOKEN}") + .queryParam("size", String.valueOf(pageSize)) + .queryParam("nickname", NICKNAME) + .queryParam("cursor", "eyJpZCI6IDEwfQ==")); + } + + @Test + void 피플은_프로젝트_지원_시_팀원을_검색할_수_있다() throws Exception { + final List content = LongStream.iterate(pageSize * 2, i -> i - 1) + .limit(pageSize) + .mapToObj(i -> new SearchTeammateResponse( + i, + NICKNAME + i, + profileImage(new MemberId(i)).getUrl() + )) + .toList(); + final SliceResponse response = SliceResponse.of( + new SliceImpl<>(content, PageRequest.of(0, pageSize), true), + "eyJpZCI6IDIwfQ==" + ); + given(searchTeammateQuery.search(any(SearchTeammateCommand.class))) + .willReturn(response); + + perform() + .andExpect(status().isOk()) + .andDo(document("project/search-teammates", + ResourceSnippetParameters.builder() + .tag("프로젝트 지원") + .description("피플은 프로젝트 지원 시 팀원을 검색할 수 있다.") + .summary("프로젝트 팀원 검색") + .responseSchema(Schema.schema("SearchTeammateResponse")), + requestHeaders(authorizationHeaderDescription().optional()), + pathParameters(parameterWithName("projectId").description("프로젝트 ID")), + queryParameters(searchTeammateQueryParametersDescription(pageSize)), + responseFields(searchTeammateResponseDescription()) + .and(sliceResponseDescription()) + )) + .andDo(print()); + } + } +} \ No newline at end of file diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/DetailProjectResponseDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/DetailProjectResponseDescription.java new file mode 100644 index 00000000..ee84cccd --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/DetailProjectResponseDescription.java @@ -0,0 +1,41 @@ +package es.princip.getp.api.controller.project.query.description; + +import es.princip.getp.domain.project.commission.model.MeetingType; +import es.princip.getp.domain.project.commission.model.ProjectCategory; +import es.princip.getp.domain.project.commission.model.ProjectStatus; +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.EnumDescriptor.fieldWithEnum; +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + +public class DetailProjectResponseDescription { + + public static FieldDescriptor[] detailProjectResponseDescription() { + return new FieldDescriptor[] { + statusField(), + fieldWithPath("data.projectId").description("프로젝트 ID"), + fieldWithPath("data.title").description("제목"), + fieldWithPath("data.payment").description("성공 보수"), + fieldWithPath("data.recruitmentCount").description("모집 인원"), + fieldWithPath("data.applicantsCount").description("지원자 수"), + fieldWithPath("data.applicationDuration.startDate").description("지원자 모집 시작 날짜"), + fieldWithPath("data.applicationDuration.endDate").description("지원자 모집 종료 날짜"), + fieldWithPath("data.estimatedDuration.startDate").description("예상 작업 시작 날짜"), + fieldWithPath("data.estimatedDuration.endDate").description("예상 작업 종료 날짜"), + fieldWithPath("data.description").description("상세 설명"), + fieldWithEnum(MeetingType.class).withPath("data.meetingType").description("미팅 방식"), + fieldWithEnum(ProjectCategory.class).withPath("data.category").description("카테고리"), + fieldWithEnum(ProjectStatus.class).withPath("data.status").description("프로젝트 상태"), + fieldWithPath("data.attachmentFiles[]").description("첨부 파일"), + fieldWithPath("data.hashtags[]").description("해시태그"), + fieldWithPath("data.likesCount").description("좋아요 수"), + fieldWithPath("data.liked").description("좋아요 여부"), + fieldWithPath("data.client.clientId").description("의뢰자 ID"), + fieldWithPath("data.client.nickname").description("의뢰자 닉네임"), + fieldWithPath("data.client.address.zipcode").description("의뢰자 우편번호"), + fieldWithPath("data.client.address.street").description("의뢰자 도로명"), + fieldWithPath("data.client.address.detail").description("의뢰자 상세 주소") + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/GetProjectsQueryParametersDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/GetProjectsQueryParametersDescription.java new file mode 100644 index 00000000..458fa24c --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/GetProjectsQueryParametersDescription.java @@ -0,0 +1,39 @@ +package es.princip.getp.api.controller.project.query.description; + +import es.princip.getp.domain.member.model.MemberType; +import org.springframework.restdocs.request.ParameterDescriptor; + +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.snippet.Attributes.key; + +public class GetProjectsQueryParametersDescription { + + public static ParameterDescriptor[] getProjectsQueryParametersDescription(int page, int pageSize) { + return new ParameterDescriptor[] { + parameterWithName("page").description("페이지 번호") + .optional() + .attributes(key("default").value(String.valueOf(page))), + parameterWithName("size").description("페이지 크기") + .optional() + .attributes(key("default").value(String.valueOf(pageSize))), + parameterWithName("sort").description("정렬 조건") + .optional() + .attributes(key("default").value("projectId,desc")), + parameterWithName("liked").description("좋아요한 프로젝트만 보기 여부. 필터 설정 시 true로 설정") + .optional() + .attributes(key("default").value("null")) + .attributes(key("permission").value(MemberType.ROLE_PEOPLE)), + parameterWithName("commissioned").description("의뢰한 프로젝트만 보기 여부. 필터 설정 시 true로 설정") + .optional() + .attributes(key("default").value("null")) + .attributes(key("permission").value(MemberType.ROLE_CLIENT)), + parameterWithName("applied").description("지원한 프로젝트만 보기 여부. 필터 설정 시 true로 설정") + .optional() + .attributes(key("default").value("null")) + .attributes(key("permission").value(MemberType.ROLE_PEOPLE)), + parameterWithName("closed").description("모집 마감된 프로젝트만 보기 여부. 필터 설정 시 true로 설정") + .optional() + .attributes(key("default").value("null")) + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/PagedCardProjectResponseDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/PagedCardProjectResponseDescription.java new file mode 100644 index 00000000..63949658 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/PagedCardProjectResponseDescription.java @@ -0,0 +1,28 @@ +package es.princip.getp.api.controller.project.query.description; + +import es.princip.getp.domain.project.commission.model.ProjectStatus; +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.EnumDescriptor.fieldWithEnum; +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + +public class PagedCardProjectResponseDescription { + + public static FieldDescriptor[] pagedCardProjectResponseDescription() { + return new FieldDescriptor[] { + statusField(), + fieldWithPath("data.content[].projectId").description("프로젝트 ID"), + fieldWithPath("data.content[].title").description("제목"), + fieldWithPath("data.content[].payment").description("성공 보수"), + fieldWithPath("data.content[].recruitmentCount").description("모집 인원"), + fieldWithPath("data.content[].applicantsCount").description("지원자 수"), + fieldWithPath("data.content[].estimatedDays").description("예상 작업 일수"), + fieldWithPath("data.content[].applicationDuration.startDate").description("지원자 모집 시작 기간"), + fieldWithPath("data.content[].applicationDuration.endDate").description("지원자 모집 종료 기간"), + fieldWithPath("data.content[].hashtags[]").description("해시태그"), + fieldWithPath("data.content[].description").description("상세 설명"), + fieldWithEnum(ProjectStatus.class).withPath("data.content[].status").description("프로젝트 상태") + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/PagedProjectApplicantResponseFields.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/PagedProjectApplicantResponseFields.java new file mode 100644 index 00000000..29301837 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/PagedProjectApplicantResponseFields.java @@ -0,0 +1,39 @@ +package es.princip.getp.api.controller.project.query.description; + +import es.princip.getp.domain.project.apply.model.ProjectApplicationStatus; +import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.restdocs.payload.JsonFieldType; + +import static es.princip.getp.api.docs.EnumDescriptor.fieldWithEnum; +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath; + +public class PagedProjectApplicantResponseFields { + + public static FieldDescriptor[] pagedProjectApplicantResponseFields() { + return new FieldDescriptor[] { + statusField(), + fieldWithPath("data.content[].peopleId").description("지원자의 피플 ID"), + fieldWithPath("data.content[].nickname").description("지원자의 닉네임"), + fieldWithPath("data.content[].profileImageUrl").description("지원자의 프로필 이미지 URL"), + fieldWithPath("data.content[].education").description("학력"), + fieldWithPath("data.content[].education.school").description("학교명"), + fieldWithPath("data.content[].education.major").description("전공명"), + fieldWithEnum(ProjectApplicationStatus.class).withPath("data.content[].status") + .description("프로젝트 지원 상태"), + subsectionWithPath("data.content[].teammates") + .optional() + .description("팀원 목록"), + fieldWithPath("data.content[].teammates[].peopleId") + .type(JsonFieldType.NUMBER) + .description("팀원의 피플 ID"), + fieldWithPath("data.content[].teammates[].nickname") + .type(JsonFieldType.STRING) + .description("팀원의 닉네임"), + fieldWithPath("data.content[].teammates[].profileImageUrl") + .type(JsonFieldType.STRING) + .description("팀원의 프로필 이미지 URL"), + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/ProjectApplicationDetailResponseDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/ProjectApplicationDetailResponseDescription.java new file mode 100644 index 00000000..e1adfdc9 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/ProjectApplicationDetailResponseDescription.java @@ -0,0 +1,57 @@ +package es.princip.getp.api.controller.project.query.description; + +import es.princip.getp.domain.project.apply.model.ProjectApplicationStatus; +import es.princip.getp.domain.project.apply.model.ProjectApplicationType; +import es.princip.getp.domain.project.apply.model.TeammateStatus; +import es.princip.getp.domain.project.commission.model.MeetingType; +import es.princip.getp.domain.project.commission.model.ProjectCategory; +import es.princip.getp.domain.project.commission.model.ProjectStatus; +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.EnumDescriptor.fieldWithEnum; +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + +public class ProjectApplicationDetailResponseDescription { + + public static FieldDescriptor[] projectApplicationDetailResponseDescription() { + return new FieldDescriptor[] { + statusField(), + fieldWithPath("data.applicationId").description("프로젝트 지원 ID"), + fieldWithEnum(ProjectApplicationType.class).withPath("data.type").description("프로젝트 지원 타입"), + fieldWithPath("data.project").description("지원한 프로젝트"), + fieldWithPath("data.project.projectId").description("지원한 프로젝트 ID"), + fieldWithPath("data.project.title").description("지원한 프로젝트 제목"), + fieldWithPath("data.project.payment").description("지원한 프로젝트 성공 보수"), + fieldWithPath("data.project.recruitmentCount").description("지원한 프로젝트 모집 인원"), + fieldWithPath("data.project.applicantsCount").description("지원한 프로젝트 지원자 수"), + fieldWithPath("data.project.applicationDuration.startDate").description("지원한 프로젝트 지원자 모집 시작 날짜"), + fieldWithPath("data.project.applicationDuration.endDate").description("지원한 프로젝트 지원자 모집 종료 날짜"), + fieldWithPath("data.project.estimatedDuration.startDate").description("지원한 프로젝트 예상 작업 시작 날짜"), + fieldWithPath("data.project.estimatedDuration.endDate").description("지원한 프로젝트 예상 작업 종료 날짜"), + fieldWithPath("data.project.description").description("지원한 프로젝트 상세 설명"), + fieldWithEnum(MeetingType.class).withPath("data.project.meetingType").description("지원한 프로젝트 미팅 방식"), + fieldWithEnum(ProjectCategory.class).withPath("data.project.category").description("지원한 프로젝트 카테고리"), + fieldWithEnum(ProjectStatus.class).withPath("data.project.status").description("지원한 프로젝트 프로젝트 상태"), + fieldWithPath("data.project.attachmentFiles[]").description("지원한 프로젝트 첨부 파일"), + fieldWithPath("data.project.hashtags[]").description("지원한 프로젝트 해시태그"), + fieldWithPath("data.project.likesCount").description("지원한 프로젝트 좋아요 수"), + fieldWithPath("data.project.liked").description("지원한 프로젝트 좋아요 여부"), + fieldWithPath("data.project.client.clientId").description("지원한 프로젝트 의뢰자 ID"), + fieldWithPath("data.project.client.nickname").description("지원한 프로젝트 의뢰자 닉네임"), + fieldWithPath("data.project.client.address.zipcode").description("지원한 프로젝트 의뢰자 우편번호"), + fieldWithPath("data.project.client.address.street").description("지원한 프로젝트 의뢰자 도로명"), + fieldWithPath("data.project.client.address.detail").description("지원한 프로젝트 의뢰자 상세 주소"), + fieldWithPath("data.expectedDuration.startDate").description("예상 작업 시작 날짜"), + fieldWithPath("data.expectedDuration.endDate").description("예상 작업 종료 날짜"), + fieldWithEnum(ProjectApplicationStatus.class).withPath("data.status").description("프로젝트 지원 상태"), + fieldWithPath("data.description").description("지원 내용"), + fieldWithPath("data.attachmentFiles[]").description("첨부 파일"), + fieldWithPath("data.teammates[]").optional().description("팀원 목록"), + fieldWithPath("data.teammates[].peopleId").optional().description("팀원 피플 ID"), + fieldWithPath("data.teammates[].nickname").optional().description("팀원 닉네임"), + fieldWithEnum(TeammateStatus.class).withPath("data.teammates[].status").optional().description("팀원 상태"), + fieldWithPath("data.teammates[].profileImageUrl").optional().description("팀원 프로필 이미지 URL") + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/ProjectApplicationFormResponseDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/ProjectApplicationFormResponseDescription.java new file mode 100644 index 00000000..a8482068 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/ProjectApplicationFormResponseDescription.java @@ -0,0 +1,64 @@ +package es.princip.getp.api.controller.project.query.description; + +import es.princip.getp.domain.project.apply.model.ProjectApplicationStatus; +import es.princip.getp.domain.project.apply.model.ProjectApplicationType; +import es.princip.getp.domain.project.apply.model.TeammateStatus; +import es.princip.getp.domain.project.commission.model.MeetingType; +import es.princip.getp.domain.project.commission.model.ProjectCategory; +import es.princip.getp.domain.project.commission.model.ProjectStatus; +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.EnumDescriptor.fieldWithEnum; +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + +public class ProjectApplicationFormResponseDescription { + + public static FieldDescriptor[] projectApplicationFormResponseDescription() { + return new FieldDescriptor[] { + statusField(), + fieldWithPath("data.applicationId").description("프로젝트 지원 ID"), + fieldWithEnum(ProjectApplicationType.class).withPath("data.type").description("프로젝트 지원 타입"), + fieldWithPath("data.project.projectId").description("지원한 프로젝트 ID"), + fieldWithPath("data.project.title").description("지원한 프로젝트 제목"), + fieldWithPath("data.project.payment").description("지원한 프로젝트 성공 보수"), + fieldWithPath("data.project.recruitmentCount").description("지원한 프로젝트 모집 인원"), + fieldWithPath("data.project.applicantsCount").description("지원한 프로젝트 지원자 수"), + fieldWithPath("data.project.applicationDuration.startDate").description("지원한 프로젝트 지원자 모집 시작 날짜"), + fieldWithPath("data.project.applicationDuration.endDate").description("지원한 프로젝트 지원자 모집 종료 날짜"), + fieldWithPath("data.project.estimatedDuration.startDate").description("지원한 프로젝트 예상 작업 시작 날짜"), + fieldWithPath("data.project.estimatedDuration.endDate").description("지원한 프로젝트 예상 작업 종료 날짜"), + fieldWithPath("data.project.description").description("지원한 프로젝트 상세 설명"), + fieldWithEnum(MeetingType.class).withPath("data.project.meetingType").description("지원한 프로젝트 미팅 방식"), + fieldWithEnum(ProjectCategory.class).withPath("data.project.category").description("지원한 프로젝트 카테고리"), + fieldWithEnum(ProjectStatus.class).withPath("data.project.status").description("지원한 프로젝트 프로젝트 상태"), + fieldWithPath("data.project.attachmentFiles[]").description("지원한 프로젝트 첨부 파일"), + fieldWithPath("data.project.hashtags[]").description("지원한 프로젝트 해시태그"), + fieldWithPath("data.project.likesCount").description("지원한 프로젝트 좋아요 수"), + fieldWithPath("data.project.liked").description("지원한 프로젝트 좋아요 여부"), + fieldWithPath("data.project.client.clientId").description("지원한 프로젝트 의뢰자 ID"), + fieldWithPath("data.project.client.nickname").description("지원한 프로젝트 의뢰자 닉네임"), + fieldWithPath("data.project.client.address.zipcode").description("지원한 프로젝트 의뢰자 우편번호"), + fieldWithPath("data.project.client.address.street").description("지원한 프로젝트 의뢰자 도로명"), + fieldWithPath("data.project.client.address.detail").description("지원한 프로젝트 의뢰자 상세 주소"), + fieldWithPath("data.nickname").description("지원자 닉네임"), + fieldWithPath("data.techStacks[]").description("지원자 기술 스택"), + fieldWithPath("data.activityArea").description("지원자 활동 지역"), + fieldWithPath("data.education.school").description("지원자 학교"), + fieldWithPath("data.education.major").description("지원자 전공"), + fieldWithPath("data.introduction").description("지원자 자기 소개"), + fieldWithPath("data.portfolios[].description").description("지원자 포트폴리오 설명"), + fieldWithPath("data.portfolios[].url").description("지원자 포트폴리오 링크"), + fieldWithPath("data.expectedDuration.startDate").description("예상 작업 시작 날짜"), + fieldWithPath("data.expectedDuration.endDate").description("예상 작업 종료 날짜"), + fieldWithEnum(ProjectApplicationStatus.class).withPath("data.status").description("프로젝트 지원 상태"), + fieldWithPath("data.description").description("지원 내용"), + fieldWithPath("data.attachmentFiles[]").description("첨부 파일"), + fieldWithPath("data.teammates[]").optional().description("팀원 목록"), + fieldWithPath("data.teammates[].peopleId").optional().description("팀원 피플 ID"), + fieldWithPath("data.teammates[].nickname").optional().description("팀원 닉네임"), + fieldWithEnum(TeammateStatus.class).withPath("data.teammates[].status").optional().description("팀원 상태"), + fieldWithPath("data.teammates[].profileImageUrl").optional().description("팀원 프로필 이미지 URL") + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/SearchTeammateQueryParametersDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/SearchTeammateQueryParametersDescription.java new file mode 100644 index 00000000..9cc44748 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/SearchTeammateQueryParametersDescription.java @@ -0,0 +1,23 @@ +package es.princip.getp.api.controller.project.query.description; + +import org.springframework.restdocs.request.ParameterDescriptor; + +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.snippet.Attributes.key; + +public class SearchTeammateQueryParametersDescription { + + public static ParameterDescriptor[] searchTeammateQueryParametersDescription(int pageSize) { + return new ParameterDescriptor[] { + parameterWithName("size").description("페이지 크기") + .optional() + .attributes(key("default").value(String.valueOf(pageSize))), + parameterWithName("nickname").description("주어진 닉네임으로 시작하는 피플을 검색해요.") + .optional() + .attributes(key("default").value("null")), + parameterWithName("cursor").description("페이지에 대한 커서. 첫 페이지를 조회할 땐 생략할 수 있어요.") + .optional() + .attributes(key("default").value("null")) + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/SearchTeammateResponseDescription.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/SearchTeammateResponseDescription.java new file mode 100644 index 00000000..fe72d215 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/description/SearchTeammateResponseDescription.java @@ -0,0 +1,18 @@ +package es.princip.getp.api.controller.project.query.description; + +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.StatusFieldDescriptor.statusField; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + +public class SearchTeammateResponseDescription { + + public static FieldDescriptor[] searchTeammateResponseDescription() { + return new FieldDescriptor[] { + statusField(), + fieldWithPath("data.content[].peopleId").description("피플 ID"), + fieldWithPath("data.content[].nickname").description("닉네임"), + fieldWithPath("data.content[].profileImageUri").description("프로필 이미지 URI") + }; + } +} \ No newline at end of file diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/fixture/ProjectCardResponseFixture.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/fixture/ProjectCardResponseFixture.java new file mode 100644 index 00000000..9a4126b3 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/fixture/ProjectCardResponseFixture.java @@ -0,0 +1,32 @@ +package es.princip.getp.api.controller.project.query.fixture; + +import es.princip.getp.application.project.commission.dto.response.ProjectCardResponse; +import es.princip.getp.domain.common.model.Duration; +import es.princip.getp.domain.project.commission.model.ProjectId; +import es.princip.getp.domain.project.commission.model.ProjectStatus; + +import java.time.LocalDate; + +import static es.princip.getp.api.controller.common.fixture.HashtagDtoFixture.hashtagsResponse; +import static es.princip.getp.fixture.project.ProjectFixture.*; + +public class ProjectCardResponseFixture { + + public static ProjectCardResponse projectCardResponse(final ProjectId projectId) { + return new ProjectCardResponse( + projectId.getValue(), + TITLE, + PAYMENT, + RECRUITMENT_COUNT, + 5L, + 10L, + Duration.of( + LocalDate.of(2024, 7, 1), + LocalDate.of(2024, 7, 7) + ), + hashtagsResponse(), + DESCRIPTION, + ProjectStatus.APPLICATION_OPENED + ); + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/fixture/ProjectDetailResponseFixture.java b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/fixture/ProjectDetailResponseFixture.java new file mode 100644 index 00000000..986cd33d --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/project/query/fixture/ProjectDetailResponseFixture.java @@ -0,0 +1,54 @@ +package es.princip.getp.api.controller.project.query.fixture; + +import es.princip.getp.application.project.commission.dto.response.ProjectClientResponse; +import es.princip.getp.application.project.commission.dto.response.ProjectDetailResponse; +import es.princip.getp.domain.common.model.Duration; +import es.princip.getp.domain.project.commission.model.MeetingType; +import es.princip.getp.domain.project.commission.model.ProjectCategory; +import es.princip.getp.domain.project.commission.model.ProjectId; +import es.princip.getp.domain.project.commission.model.ProjectStatus; + +import java.time.LocalDate; +import java.util.List; + +import static es.princip.getp.api.controller.common.fixture.AddressResponseFixture.addressResponse; +import static es.princip.getp.api.controller.common.fixture.HashtagDtoFixture.hashtagsResponse; +import static es.princip.getp.fixture.member.NicknameFixture.NICKNAME; +import static es.princip.getp.fixture.project.ProjectFixture.*; + +public class ProjectDetailResponseFixture { + + public static ProjectDetailResponse projectDetailResponse(final ProjectId projectId) { + return new ProjectDetailResponse( + projectId.getValue(), + TITLE, + PAYMENT, + RECRUITMENT_COUNT, + 5L, + Duration.of( + LocalDate.of(2024, 7, 1), + LocalDate.of(2024, 7, 7) + ), + Duration.of( + LocalDate.of(2024, 7, 14), + LocalDate.of(2024, 7, 21) + ), + DESCRIPTION, + MeetingType.IN_PERSON, + ProjectCategory.BACKEND, + ProjectStatus.APPLICATION_OPENED, + List.of( + "https://example.com/attachment1", + "https://example.com/attachment2" + ), + hashtagsResponse(), + 5L, + true, + new ProjectClientResponse( + 1L, + NICKNAME, + addressResponse() + ) + ); + } +} diff --git a/src/test/java/es/princip/getp/api/controller/storage/FileStorageControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/controller/storage/FileStorageControllerTest.java similarity index 57% rename from src/test/java/es/princip/getp/api/controller/storage/FileStorageControllerTest.java rename to get-p-api/src/test/java/es/princip/getp/api/controller/storage/FileStorageControllerTest.java index 4850bed4..b55e84ac 100644 --- a/src/test/java/es/princip/getp/api/controller/storage/FileStorageControllerTest.java +++ b/get-p-api/src/test/java/es/princip/getp/api/controller/storage/FileStorageControllerTest.java @@ -1,10 +1,11 @@ package es.princip.getp.api.controller.storage; -import es.princip.getp.api.support.ControllerTest; +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; import es.princip.getp.api.security.annotation.WithCustomMockUser; -import es.princip.getp.application.storage.command.UploadFileCommand; +import es.princip.getp.api.support.ControllerTest; +import es.princip.getp.application.storage.dto.command.UploadFileCommand; import es.princip.getp.application.storage.port.in.UploadFileUseCase; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -13,12 +14,13 @@ import java.net.URI; -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; -import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescriptor; -import static es.princip.getp.api.docs.PayloadDocumentationHelper.responseFields; +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescription; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; import static org.springframework.restdocs.request.RequestDocumentation.partWithName; import static org.springframework.restdocs.request.RequestDocumentation.requestParts; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; @@ -30,8 +32,7 @@ class FileStorageControllerTest extends ControllerTest { private UploadFileUseCase uploadFileUseCase; @Nested - @DisplayName("파일 업로드") - class UploadFile { + class 파일_업로드 { final MockMultipartFile file = new MockMultipartFile("file", "dummy".getBytes()); @@ -43,22 +44,26 @@ private ResultActions perform() throws Exception { @Test @WithCustomMockUser - @DisplayName("사용자는 파일을 업로드할 수 있다.") - void uploadFile() throws Exception { + void 사용자는_파일을_업로드할_수_있다() throws Exception { given(uploadFileUseCase.upload(any(UploadFileCommand.class))) .willReturn(URI.create("https://storage.principes.xyz/1/files/1/test.pdf")); perform() .andExpect(status().isCreated()) - .andDo( - restDocs.document( - requestHeaders(authorizationHeaderDescriptor()), - requestParts(partWithName("file").description("업로드할 파일")), - responseFields( - getDescriptor("fileUri", "업로드된 파일 URI") - ) + .andDo(document("storage/upload-file", + ResourceSnippetParameters.builder() + .tag("스토리지") + .description("사용자는 파일을 업로드할 수 있다.") + .summary("파일 업로드") + .requestSchema(Schema.schema("UploadFileRequest")) + .responseSchema(Schema.schema("UploadFileResponse")), + requestHeaders(authorizationHeaderDescription()), + requestParts(partWithName("file").description("업로드할 파일")), + responseFields( + fieldWithPath("status").description("응답 상태"), + fieldWithPath("data.fileUri").description("업로드된 파일 URI") ) - ) + )) .andDo(print()); } } diff --git a/get-p-api/src/test/java/es/princip/getp/api/docs/ConstraintDescriptor.java b/get-p-api/src/test/java/es/princip/getp/api/docs/ConstraintDescriptor.java new file mode 100644 index 00000000..7e709cfc --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/docs/ConstraintDescriptor.java @@ -0,0 +1,71 @@ +package es.princip.getp.api.docs; + +import org.springframework.restdocs.constraints.Constraint; +import org.springframework.restdocs.constraints.ConstraintDescriptionResolver; +import org.springframework.restdocs.constraints.ResourceBundleConstraintDescriptionResolver; +import org.springframework.restdocs.constraints.ValidatorConstraintResolver; +import org.springframework.restdocs.payload.FieldDescriptor; + +import java.util.*; +import java.util.stream.Collectors; + +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.snippet.Attributes.key; + +public class ConstraintDescriptor { + + private static final ResourceBundle resourceBundle = ResourceBundle.getBundle("messages/validation"); + private static final ValidatorConstraintResolver constraintResolver = new ValidatorConstraintResolver(); + private static final ConstraintDescriptionResolver descriptionResolver = new ResourceBundleConstraintDescriptionResolver(); + + private static final String CONSTRAINT_KEY = "constraints"; + private static final String OAS_CONSTRAINT_KEY = "validationConstraints"; + + public static FieldDescriptor fieldWithConstraint(final String path, final Class clazz) { + final List constraints = getConstraintsForOAS(path, clazz); + final String message = getConstraintsForDocs(path, clazz); + return fieldWithPath(path) + .attributes(key(CONSTRAINT_KEY).value(message)) + .attributes(key(OAS_CONSTRAINT_KEY).value(constraints)); + } + + private static List getConstraintsForOAS(final String path, final Class clazz) { + final String[] properties = path.split("\\."); + final String property = properties[properties.length - 1]; + return constraintResolver.resolveForProperty(property, clazz).stream() + .map(constraint -> { + final Map configuration = new HashMap<>(constraint.getConfiguration()); + configuration.put("message", getConstraintMessage(constraint)); + return new Constraint(constraint.getName(), configuration); + }) + .collect(Collectors.toList()); + } + + private static String getConstraintsForDocs(final String path, final Class clazz) { + final String[] properties = path.split("\\."); + final String property = properties[properties.length - 1]; + return constraintResolver.resolveForProperty(property, clazz).stream() + .map(ConstraintDescriptor::getConstraintMessage) + .collect(Collectors.joining(", ")); + } + + private static String getConstraintMessage(final Constraint constraint) { + try { + return getConstraintMessageFromResourceBundle(constraint); + } catch (MissingResourceException e) { + return getDefaultConstraintMessage(constraint); + } + } + + private static String getConstraintMessageFromResourceBundle(final Constraint constraint) + throws MissingResourceException { + final String key = ((String) constraint.getConfiguration().get("message")) + .replace("{", "") + .replace("}", ""); + return resourceBundle.getString(key); + } + + private static String getDefaultConstraintMessage(final Constraint constraint) { + return descriptionResolver.resolveDescription(constraint); + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/docs/EnumDescriptor.java b/get-p-api/src/test/java/es/princip/getp/api/docs/EnumDescriptor.java new file mode 100644 index 00000000..a6d6a09b --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/docs/EnumDescriptor.java @@ -0,0 +1,13 @@ +package es.princip.getp.api.docs; + +import com.epages.restdocs.apispec.EnumFields; + +public class EnumDescriptor { + + public static EnumFields fieldWithEnum(final Class clazz) { + if (!clazz.isEnum()) { + throw new IllegalArgumentException("The class must be an enum type."); + } + return new EnumFields(clazz); + } +} diff --git a/src/test/java/es/princip/getp/api/docs/HeaderDescriptorHelper.java b/get-p-api/src/test/java/es/princip/getp/api/docs/HeaderDescriptorHelper.java similarity index 75% rename from src/test/java/es/princip/getp/api/docs/HeaderDescriptorHelper.java rename to get-p-api/src/test/java/es/princip/getp/api/docs/HeaderDescriptorHelper.java index 2fa2a584..e5549e09 100644 --- a/src/test/java/es/princip/getp/api/docs/HeaderDescriptorHelper.java +++ b/get-p-api/src/test/java/es/princip/getp/api/docs/HeaderDescriptorHelper.java @@ -6,11 +6,11 @@ public class HeaderDescriptorHelper { - public static HeaderDescriptor authorizationHeaderDescriptor() { + public static HeaderDescriptor authorizationHeaderDescription() { return headerWithName("Authorization").description("Bearer ${ACCESS_TOKEN}"); } - public static HeaderDescriptor refreshTokenHeaderDescriptor() { + public static HeaderDescriptor refreshTokenHeaderDescription() { return headerWithName("Refresh-Token").description("Bearer ${REFRESH_TOKEN}"); } } diff --git a/get-p-api/src/test/java/es/princip/getp/api/docs/PageResponseDescriptor.java b/get-p-api/src/test/java/es/princip/getp/api/docs/PageResponseDescriptor.java new file mode 100644 index 00000000..099e308f --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/docs/PageResponseDescriptor.java @@ -0,0 +1,25 @@ +package es.princip.getp.api.docs; + +import org.springframework.data.domain.Sort; +import org.springframework.restdocs.payload.FieldDescriptor; + +import static es.princip.getp.api.docs.EnumDescriptor.fieldWithEnum; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + +public class PageResponseDescriptor { + + public static FieldDescriptor[] pageResponseFieldDescriptors() { + return new FieldDescriptor[] { + fieldWithPath("data.pageInfo.totalPages").description("전체 페이지 수"), + fieldWithPath("data.pageInfo.totalElements").description("전체 요소 수"), + fieldWithPath("data.pageInfo.size").description("페이지 크기"), + fieldWithPath("data.pageInfo.number").description("현재 페이지 번호"), + fieldWithPath("data.pageInfo.numberOfElements").description("현재 페이지 요소 수"), + fieldWithPath("data.pageInfo.first").description("첫 페이지 여부"), + fieldWithPath("data.pageInfo.last").description("마지막 페이지 여부"), + fieldWithPath("data.pageInfo.empty").description("비어있는 페이지 여부"), + fieldWithPath("data.pageInfo.sort.property").description("정렬 속성"), + fieldWithEnum(Sort.Direction.class).withPath("data.pageInfo.sort.direction").description("정렬 방향"), + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/docs/PaginationDescription.java b/get-p-api/src/test/java/es/princip/getp/api/docs/PaginationDescription.java new file mode 100644 index 00000000..24dcfb49 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/docs/PaginationDescription.java @@ -0,0 +1,21 @@ +package es.princip.getp.api.docs; + + +import org.springframework.restdocs.request.ParameterDescriptor; + +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.snippet.Attributes.key; + +public class PaginationDescription { + + public static ParameterDescriptor[] cursorPaginationParameters(final int size) { + return new ParameterDescriptor[] { + parameterWithName("size").description("페이지 크기") + .optional() + .attributes(key("default").value(String.valueOf(size))), + parameterWithName("cursor").description("페이지에 대한 커서. 첫 페이지를 조회할 땐 생략할 수 있어요.") + .optional() + .attributes(key("default").value("null")) + }; + } +} diff --git a/src/test/java/es/princip/getp/api/docs/ParameterDescriptorHelper.java b/get-p-api/src/test/java/es/princip/getp/api/docs/ParameterDescriptorHelper.java similarity index 100% rename from src/test/java/es/princip/getp/api/docs/ParameterDescriptorHelper.java rename to get-p-api/src/test/java/es/princip/getp/api/docs/ParameterDescriptorHelper.java diff --git a/get-p-api/src/test/java/es/princip/getp/api/docs/SliceResponseDescription.java b/get-p-api/src/test/java/es/princip/getp/api/docs/SliceResponseDescription.java new file mode 100644 index 00000000..965f12ed --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/docs/SliceResponseDescription.java @@ -0,0 +1,31 @@ +package es.princip.getp.api.docs; + +import org.springframework.data.domain.Sort; +import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.restdocs.payload.JsonFieldType; + +import static es.princip.getp.api.docs.EnumDescriptor.fieldWithEnum; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + +public class SliceResponseDescription { + + public static FieldDescriptor[] sliceResponseDescription() { + return new FieldDescriptor[] { + fieldWithPath("data.sliceInfo.size").description("페이지 크기"), + fieldWithPath("data.sliceInfo.numberOfElements").description("현재 페이지 요소 수"), + fieldWithPath("data.sliceInfo.first").description("첫 페이지 여부"), + fieldWithPath("data.sliceInfo.last").description("마지막 페이지 여부"), + fieldWithPath("data.sliceInfo.empty").description("비어있는 페이지 여부"), + fieldWithPath("data.sliceInfo.cursor") + .description("다음 페이지에 대한 커서. 다음 페이지를 요청할 때 이전 페이지 응답에서 받은 커서 값을 넣어주세요."), + fieldWithPath("data.sliceInfo.sort").description("정렬 정보").ignored(), + fieldWithPath("data.sliceInfo.sort.property") + .description("정렬 속성") + .optional() + .type(JsonFieldType.STRING), + fieldWithEnum(Sort.Direction.class).withPath("data.sliceInfo.sort.direction") + .description("정렬 방향") + .optional() + }; + } +} diff --git a/get-p-api/src/test/java/es/princip/getp/api/docs/StatusFieldDescriptor.java b/get-p-api/src/test/java/es/princip/getp/api/docs/StatusFieldDescriptor.java new file mode 100644 index 00000000..0a858686 --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/docs/StatusFieldDescriptor.java @@ -0,0 +1,12 @@ +package es.princip.getp.api.docs; + +import org.springframework.restdocs.payload.FieldDescriptor; + +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + +public class StatusFieldDescriptor { + + public static FieldDescriptor statusField() { + return fieldWithPath("status").description("응답 상태"); + } +} diff --git a/src/test/java/es/princip/getp/api/security/PrincipalDetailsParameterResolver.java b/get-p-api/src/test/java/es/princip/getp/api/security/PrincipalDetailsParameterResolver.java similarity index 94% rename from src/test/java/es/princip/getp/api/security/PrincipalDetailsParameterResolver.java rename to get-p-api/src/test/java/es/princip/getp/api/security/PrincipalDetailsParameterResolver.java index a00f8b55..81511f84 100644 --- a/src/test/java/es/princip/getp/api/security/PrincipalDetailsParameterResolver.java +++ b/get-p-api/src/test/java/es/princip/getp/api/security/PrincipalDetailsParameterResolver.java @@ -1,6 +1,6 @@ package es.princip.getp.api.security; -import es.princip.getp.api.security.details.PrincipalDetails; +import es.princip.getp.application.auth.service.PrincipalDetails; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.api.extension.ParameterResolutionException; diff --git a/src/test/java/es/princip/getp/api/security/annotation/WithCustomMockUser.java b/get-p-api/src/test/java/es/princip/getp/api/security/annotation/WithCustomMockUser.java similarity index 100% rename from src/test/java/es/princip/getp/api/security/annotation/WithCustomMockUser.java rename to get-p-api/src/test/java/es/princip/getp/api/security/annotation/WithCustomMockUser.java diff --git a/src/test/java/es/princip/getp/api/security/annotation/WithCustomMockUserSecurityContextFactory.java b/get-p-api/src/test/java/es/princip/getp/api/security/annotation/WithCustomMockUserSecurityContextFactory.java similarity index 84% rename from src/test/java/es/princip/getp/api/security/annotation/WithCustomMockUserSecurityContextFactory.java rename to get-p-api/src/test/java/es/princip/getp/api/security/annotation/WithCustomMockUserSecurityContextFactory.java index 1b42bcba..a1848ce4 100644 --- a/src/test/java/es/princip/getp/api/security/annotation/WithCustomMockUserSecurityContextFactory.java +++ b/get-p-api/src/test/java/es/princip/getp/api/security/annotation/WithCustomMockUserSecurityContextFactory.java @@ -1,6 +1,6 @@ package es.princip.getp.api.security.annotation; -import es.princip.getp.api.security.details.PrincipalDetails; +import es.princip.getp.application.auth.service.PrincipalDetails; import es.princip.getp.domain.common.model.Email; import es.princip.getp.domain.common.model.PhoneNumber; import es.princip.getp.domain.member.model.*; @@ -22,16 +22,17 @@ public class WithCustomMockUserSecurityContextFactory implements WithSecurityCon @Override public SecurityContext createSecurityContext(final WithCustomMockUser annotation) { + final MemberId memberId = new MemberId(annotation.memberId()); final Email email = Email.from(annotation.email()); final Password password = Password.from(annotation.password()); final Nickname nickname = Nickname.from(annotation.nickname()); final PhoneNumber phoneNumber = PhoneNumber.from(annotation.phoneNumber()); - final ProfileImage profileImage = profileImage(annotation.memberId()); + final ProfileImage profileImage = profileImage(memberId); final MemberType memberType = annotation.memberType(); final LocalDateTime now = LocalDateTime.now(); final Member member = spy(Member.of(email, password, memberType)); - given(member.getMemberId()).willReturn(annotation.memberId()); + given(member.getId()).willReturn(memberId); given(member.getNickname()).willReturn(nickname); given(member.getPhoneNumber()).willReturn(phoneNumber); given(member.getProfileImage()).willReturn(profileImage); @@ -39,8 +40,11 @@ public SecurityContext createSecurityContext(final WithCustomMockUser annotation given(member.getUpdatedAt()).willReturn(now); final String role = annotation.memberType().name(); - final Authentication auth = new UsernamePasswordAuthenticationToken(new PrincipalDetails(member), "", - List.of(new SimpleGrantedAuthority(role))); + final Authentication auth = new UsernamePasswordAuthenticationToken( + new PrincipalDetails(member), + "", + List.of(new SimpleGrantedAuthority(role)) + ); final SecurityContext context = SecurityContextHolder.getContext(); context.setAuthentication(auth); diff --git a/src/test/java/es/princip/getp/api/support/ControllerTest.java b/get-p-api/src/test/java/es/princip/getp/api/support/ControllerTest.java similarity index 79% rename from src/test/java/es/princip/getp/api/support/ControllerTest.java rename to get-p-api/src/test/java/es/princip/getp/api/support/ControllerTest.java index dc7dedcb..a6fce910 100644 --- a/src/test/java/es/princip/getp/api/support/ControllerTest.java +++ b/get-p-api/src/test/java/es/princip/getp/api/support/ControllerTest.java @@ -1,12 +1,9 @@ package es.princip.getp.api.support; import com.fasterxml.jackson.databind.ObjectMapper; -import es.princip.getp.api.config.MockCommandMapperBeanFactoryPostProcessor; import es.princip.getp.api.config.MockDaoBeanFactoryPostProcessor; import es.princip.getp.api.config.MockServiceBeanFactoryPostProcessor; -import es.princip.getp.api.docs.SpringRestDocsConfig; import es.princip.getp.api.security.PrincipalDetailsParameterResolver; -import es.princip.getp.api.security.SecurityConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.extension.ExtendWith; @@ -15,15 +12,13 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; import org.springframework.restdocs.RestDocumentationContextProvider; import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; -import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; -import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; @@ -32,19 +27,16 @@ import org.springframework.web.filter.CharacterEncodingFilter; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; @WebMvcTest -@ActiveProfiles("test") +@ComponentScan("es.princip.getp.api") @Execution(ExecutionMode.SAME_THREAD) @TestInstance(TestInstance.Lifecycle.PER_CLASS) @Import({ SecurityConfig.class, - SpringRestDocsConfig.class, MockDaoBeanFactoryPostProcessor.class, - MockCommandMapperBeanFactoryPostProcessor.class, - MockServiceBeanFactoryPostProcessor.class + MockServiceBeanFactoryPostProcessor.class, }) @ExtendWith({RestDocumentationExtension.class, PrincipalDetailsParameterResolver.class}) public abstract class ControllerTest { @@ -78,9 +70,6 @@ protected MockMultipartHttpServletRequestBuilder multipart(final String uri, fin .contextPath(contextPath); } - @Autowired - protected RestDocumentationResultHandler restDocs; - @Autowired protected MockMvc mockMvc; @@ -90,20 +79,18 @@ protected MockMultipartHttpServletRequestBuilder multipart(final String uri, fin @BeforeEach void setUp(final WebApplicationContext context, final RestDocumentationContextProvider restDocumentation) { this.mockMvc = MockMvcBuilders.webAppContextSetup(context) - .apply(documentationConfiguration(restDocumentation).uris() + .apply(documentationConfiguration(restDocumentation) + .operationPreprocessors() + .withRequestDefaults(prettyPrint()) + .withResponseDefaults(prettyPrint()) + .and() + .uris() .withScheme("https") .withHost("api.principes.xyz") .withPort(443) ) .alwaysDo(MockMvcResultHandlers.print()) - .alwaysDo(restDocs) .addFilters(new CharacterEncodingFilter("UTF-8", true)) .build(); } - - protected static void expectForbidden(final ResultActions result) throws Exception { - result - .andExpect(status().isForbidden()) - .andDo(print()); - } } diff --git a/get-p-api/src/test/java/es/princip/getp/api/support/CursorParserTest.java b/get-p-api/src/test/java/es/princip/getp/api/support/CursorParserTest.java new file mode 100644 index 00000000..748bce4a --- /dev/null +++ b/get-p-api/src/test/java/es/princip/getp/api/support/CursorParserTest.java @@ -0,0 +1,54 @@ +package es.princip.getp.api.support; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import es.princip.getp.application.support.Cursor; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Base64; + +import static org.assertj.core.api.Assertions.assertThat; + +class CursorParserTest extends ControllerTest { + + @Autowired CursorParser cursorParser; + @Autowired ObjectMapper objectMapper; + + @Test + void 커서를_파싱한다() throws JsonProcessingException { + final Cursor cursor = new Cursor(1L); + final String cursorJson = objectMapper.writeValueAsString(cursor); + final String cursorString = Base64.getEncoder().encodeToString(cursorJson.getBytes()); + + final Cursor parsed = cursorParser.parse(Cursor.class, cursorString); + + assertThat(parsed).isEqualTo(cursor); + } + + @Nested + class 커서_문자열이_올바르지_않으면_null을_반환한다 { + + @Test + void 커서_문자열이_null이면_null을_반환한다() { + final Cursor parsed = cursorParser.parse(Cursor.class, null); + + assertThat(parsed).isNull(); + } + + @Test + void 커서_문자열이_빈_문자열이면_null을_반환한다() { + final Cursor parsed = cursorParser.parse(Cursor.class, ""); + + assertThat(parsed).isNull(); + } + + @Test + void 커서_문자열이_올바르지_않은_인코딩이면_null을_반환한다() { + final Cursor parsed = cursorParser.parse(Cursor.class, "invalid"); + + assertThat(parsed).isNull(); + } + } +} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/config/ObjectMapperConfig.java b/get-p-api/src/test/java/es/princip/getp/config/ObjectMapperConfig.java similarity index 92% rename from src/test/java/es/princip/getp/api/config/ObjectMapperConfig.java rename to get-p-api/src/test/java/es/princip/getp/config/ObjectMapperConfig.java index 6b5ab04d..08266cdb 100644 --- a/src/test/java/es/princip/getp/api/config/ObjectMapperConfig.java +++ b/get-p-api/src/test/java/es/princip/getp/config/ObjectMapperConfig.java @@ -1,4 +1,4 @@ -package es.princip.getp.api.config; +package es.princip.getp.config; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; diff --git a/get-p-api/src/test/resources/application.yml b/get-p-api/src/test/resources/application.yml new file mode 100644 index 00000000..ffe4b429 --- /dev/null +++ b/get-p-api/src/test/resources/application.yml @@ -0,0 +1,13 @@ +server: + servlet: + context-path: ${BASE_PATH} + +spring: + messages: + basename: messages/validation, messages/error + +logging: + level: + org: + springframework: + security: DEBUG \ No newline at end of file diff --git a/get-p-api/src/test/resources/org/springframework/restdocs/templates/asciidoctor/query-parameters.snippet b/get-p-api/src/test/resources/org/springframework/restdocs/templates/asciidoctor/query-parameters.snippet new file mode 100644 index 00000000..b9f63d23 --- /dev/null +++ b/get-p-api/src/test/resources/org/springframework/restdocs/templates/asciidoctor/query-parameters.snippet @@ -0,0 +1,11 @@ +|=== +|Parameter|Required|Default|Permissions|Description + +{{#parameters}} +|{{#tableCellContent}}`+{{name}}+`{{/tableCellContent}} +|{{#tableCellContent}}{{#optional}}False{{/optional}}{{^optional}}True{{/optional}}{{/tableCellContent}} +|{{#tableCellContent}}{{#default}}{{.}}{{/default}}{{/tableCellContent}} +|{{#tableCellContent}}{{#permission}}{{permission}}{{/permission}}{{/tableCellContent}} +|{{#tableCellContent}}{{description}}{{/tableCellContent}} +{{/parameters}} +|=== \ No newline at end of file diff --git a/get-p-api/src/test/resources/org/springframework/restdocs/templates/asciidoctor/request-fields.snippet b/get-p-api/src/test/resources/org/springframework/restdocs/templates/asciidoctor/request-fields.snippet new file mode 100644 index 00000000..669394c8 --- /dev/null +++ b/get-p-api/src/test/resources/org/springframework/restdocs/templates/asciidoctor/request-fields.snippet @@ -0,0 +1,12 @@ +|=== +|Path|Type|Required|Enum|Constraints|Description + +{{#fields}} +|{{#tableCellContent}}`+{{path}}+`{{/tableCellContent}} +|{{#tableCellContent}}`+{{type}}+`{{/tableCellContent}} +|{{#tableCellContent}}{{#optional}}False{{/optional}}{{^optional}}True{{/optional}}{{/tableCellContent}} +|{{#tableCellContent}}{{#enumValues}}{{.}} {{/enumValues}}{{/tableCellContent}} +|{{#tableCellContent}}{{#constraints}}{{.}}{{/constraints}}{{/tableCellContent}} +|{{#tableCellContent}}{{description}}{{/tableCellContent}} +{{/fields}} +|=== \ No newline at end of file diff --git a/src/test/resources/org/springframework/restdocs/templates/asciidoctor/response-fields.snippet b/get-p-api/src/test/resources/org/springframework/restdocs/templates/asciidoctor/response-fields.snippet similarity index 65% rename from src/test/resources/org/springframework/restdocs/templates/asciidoctor/response-fields.snippet rename to get-p-api/src/test/resources/org/springframework/restdocs/templates/asciidoctor/response-fields.snippet index ea0d0dfe..d436804a 100644 --- a/src/test/resources/org/springframework/restdocs/templates/asciidoctor/response-fields.snippet +++ b/get-p-api/src/test/resources/org/springframework/restdocs/templates/asciidoctor/response-fields.snippet @@ -1,11 +1,10 @@ |=== -|Path|Type|Description|Format +|Path|Type|Enum|Description {{#fields}} |{{#tableCellContent}}`+{{path}}+`{{/tableCellContent}} |{{#tableCellContent}}`+{{type}}+`{{/tableCellContent}} +|{{#tableCellContent}}{{#enumValues}}{{.}} {{/enumValues}}{{/tableCellContent}} |{{#tableCellContent}}{{description}}{{/tableCellContent}} -|{{#tableCellContent}}{{#format}}{{.}}{{/format}}{{/tableCellContent}} - {{/fields}} |=== \ No newline at end of file diff --git a/get-p-application/build.gradle b/get-p-application/build.gradle new file mode 100644 index 00000000..7f0ef650 --- /dev/null +++ b/get-p-application/build.gradle @@ -0,0 +1,42 @@ +dependencies { + // 모듈 의존성 + implementation(project(':get-p-domain')) + testImplementation(testFixtures(project(':get-p-domain'))) + + // Spring AOP + implementation 'org.springframework.boot:spring-boot-starter-aop:3.3.5' + + // Spring Web + implementation 'org.springframework.boot:spring-boot-starter-web:3.3.5' + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.3.5' + + // Spring Validation + implementation 'org.springframework.boot:spring-boot-starter-validation:3.3.5' + + // Spring Data + implementation 'org.springframework.data:spring-data-commons' + + // Spring Transaction + implementation 'org.springframework:spring-tx' + + // Spring Security + implementation 'org.springframework.boot:spring-boot-starter-security:3.3.5' + testImplementation 'org.springframework.security:spring-security-test:6.3.4' + + // JWT + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' + implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' + + // 메일 + implementation 'com.sun.mail:jakarta.mail:2.0.1' + implementation 'org.springframework.boot:spring-boot-starter-mail:3.2.1' +} + +bootJar { + enabled = false +} + +jar { + enabled = true +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/application/auth/command/SignUpCommand.java b/get-p-application/src/main/java/es/princip/getp/application/auth/dto/command/SignUpCommand.java similarity index 88% rename from src/main/java/es/princip/getp/application/auth/command/SignUpCommand.java rename to get-p-application/src/main/java/es/princip/getp/application/auth/dto/command/SignUpCommand.java index 14b3736f..ab9a72d0 100644 --- a/src/main/java/es/princip/getp/application/auth/command/SignUpCommand.java +++ b/get-p-application/src/main/java/es/princip/getp/application/auth/dto/command/SignUpCommand.java @@ -1,4 +1,4 @@ -package es.princip.getp.application.auth.command; +package es.princip.getp.application.auth.dto.command; import es.princip.getp.domain.common.model.Email; import es.princip.getp.domain.member.model.MemberType; diff --git a/src/main/java/es/princip/getp/api/controller/auth/dto/response/Token.java b/get-p-application/src/main/java/es/princip/getp/application/auth/dto/response/Token.java similarity index 62% rename from src/main/java/es/princip/getp/api/controller/auth/dto/response/Token.java rename to get-p-application/src/main/java/es/princip/getp/application/auth/dto/response/Token.java index 942c8d96..b1318b73 100644 --- a/src/main/java/es/princip/getp/api/controller/auth/dto/response/Token.java +++ b/get-p-application/src/main/java/es/princip/getp/application/auth/dto/response/Token.java @@ -1,4 +1,4 @@ -package es.princip.getp.api.controller.auth.dto.response; +package es.princip.getp.application.auth.dto.response; public record Token( String grantType, diff --git a/src/main/java/es/princip/getp/application/auth/exception/DuplicatedEmailException.java b/get-p-application/src/main/java/es/princip/getp/application/auth/exception/DuplicatedEmailException.java similarity index 100% rename from src/main/java/es/princip/getp/application/auth/exception/DuplicatedEmailException.java rename to get-p-application/src/main/java/es/princip/getp/application/auth/exception/DuplicatedEmailException.java diff --git a/src/main/java/es/princip/getp/api/security/exception/ExpiredTokenException.java b/get-p-application/src/main/java/es/princip/getp/application/auth/exception/ExpiredTokenException.java similarity index 86% rename from src/main/java/es/princip/getp/api/security/exception/ExpiredTokenException.java rename to get-p-application/src/main/java/es/princip/getp/application/auth/exception/ExpiredTokenException.java index 3a8c6ca7..b1742fab 100644 --- a/src/main/java/es/princip/getp/api/security/exception/ExpiredTokenException.java +++ b/get-p-application/src/main/java/es/princip/getp/application/auth/exception/ExpiredTokenException.java @@ -1,4 +1,4 @@ -package es.princip.getp.api.security.exception; +package es.princip.getp.application.auth.exception; import es.princip.getp.domain.support.ErrorDescription; diff --git a/src/main/java/es/princip/getp/application/auth/exception/FailedVerificationCodeSendingException.java b/get-p-application/src/main/java/es/princip/getp/application/auth/exception/FailedVerificationCodeSendingException.java similarity index 100% rename from src/main/java/es/princip/getp/application/auth/exception/FailedVerificationCodeSendingException.java rename to get-p-application/src/main/java/es/princip/getp/application/auth/exception/FailedVerificationCodeSendingException.java diff --git a/src/main/java/es/princip/getp/application/auth/exception/IncorrectEmailOrPasswordException.java b/get-p-application/src/main/java/es/princip/getp/application/auth/exception/IncorrectEmailOrPasswordException.java similarity index 100% rename from src/main/java/es/princip/getp/application/auth/exception/IncorrectEmailOrPasswordException.java rename to get-p-application/src/main/java/es/princip/getp/application/auth/exception/IncorrectEmailOrPasswordException.java diff --git a/src/main/java/es/princip/getp/application/auth/exception/IncorrectVerificationCodeException.java b/get-p-application/src/main/java/es/princip/getp/application/auth/exception/IncorrectVerificationCodeException.java similarity index 100% rename from src/main/java/es/princip/getp/application/auth/exception/IncorrectVerificationCodeException.java rename to get-p-application/src/main/java/es/princip/getp/application/auth/exception/IncorrectVerificationCodeException.java diff --git a/src/main/java/es/princip/getp/api/security/exception/InvalidTokenException.java b/get-p-application/src/main/java/es/princip/getp/application/auth/exception/InvalidTokenException.java similarity index 87% rename from src/main/java/es/princip/getp/api/security/exception/InvalidTokenException.java rename to get-p-application/src/main/java/es/princip/getp/application/auth/exception/InvalidTokenException.java index 83158564..9af399fa 100644 --- a/src/main/java/es/princip/getp/api/security/exception/InvalidTokenException.java +++ b/get-p-application/src/main/java/es/princip/getp/application/auth/exception/InvalidTokenException.java @@ -1,4 +1,4 @@ -package es.princip.getp.api.security.exception; +package es.princip.getp.application.auth.exception; import es.princip.getp.domain.support.ErrorDescription; diff --git a/src/main/java/es/princip/getp/api/security/exception/JwtTokenException.java b/get-p-application/src/main/java/es/princip/getp/application/auth/exception/JwtTokenException.java similarity index 85% rename from src/main/java/es/princip/getp/api/security/exception/JwtTokenException.java rename to get-p-application/src/main/java/es/princip/getp/application/auth/exception/JwtTokenException.java index 23f1d9f6..c0bdb690 100644 --- a/src/main/java/es/princip/getp/api/security/exception/JwtTokenException.java +++ b/get-p-application/src/main/java/es/princip/getp/application/auth/exception/JwtTokenException.java @@ -1,4 +1,4 @@ -package es.princip.getp.api.security.exception; +package es.princip.getp.application.auth.exception; import es.princip.getp.domain.support.DomainLogicException; import es.princip.getp.domain.support.ErrorDescription; diff --git a/src/main/java/es/princip/getp/application/auth/exception/NotFoundVerificationException.java b/get-p-application/src/main/java/es/princip/getp/application/auth/exception/NotFoundVerificationException.java similarity index 88% rename from src/main/java/es/princip/getp/application/auth/exception/NotFoundVerificationException.java rename to get-p-application/src/main/java/es/princip/getp/application/auth/exception/NotFoundVerificationException.java index 97ce74fc..373bb87d 100644 --- a/src/main/java/es/princip/getp/application/auth/exception/NotFoundVerificationException.java +++ b/get-p-application/src/main/java/es/princip/getp/application/auth/exception/NotFoundVerificationException.java @@ -1,7 +1,7 @@ package es.princip.getp.application.auth.exception; +import es.princip.getp.application.support.NotFoundException; import es.princip.getp.domain.support.ErrorDescription; -import es.princip.getp.persistence.support.NotFoundException; public class NotFoundVerificationException extends NotFoundException { diff --git a/src/main/java/es/princip/getp/application/auth/infra/EmailVerificationCodeSender.java b/get-p-application/src/main/java/es/princip/getp/application/auth/infra/EmailVerificationCodeSender.java similarity index 88% rename from src/main/java/es/princip/getp/application/auth/infra/EmailVerificationCodeSender.java rename to get-p-application/src/main/java/es/princip/getp/application/auth/infra/EmailVerificationCodeSender.java index bfc9b264..4472fd46 100644 --- a/src/main/java/es/princip/getp/application/auth/infra/EmailVerificationCodeSender.java +++ b/get-p-application/src/main/java/es/princip/getp/application/auth/infra/EmailVerificationCodeSender.java @@ -5,13 +5,14 @@ import es.princip.getp.application.mail.command.SendMailCommand; import es.princip.getp.application.mail.port.in.SendMailUseCase; import es.princip.getp.domain.common.model.Email; +import jakarta.mail.MessagingException; import lombok.RequiredArgsConstructor; import org.springframework.mail.MailException; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor -public class EmailVerificationCodeSender implements VerificationCodeSender { +class EmailVerificationCodeSender implements VerificationCodeSender { private final SendMailUseCase sendMailUseCase; @@ -39,7 +40,7 @@ public void send(final Email email, final String verificationCode) { ); try { sendMailUseCase.send(command); - } catch (MailException exception) { + } catch (MailException | MessagingException exception) { throw new FailedVerificationCodeSendingException(); } } diff --git a/get-p-application/src/main/java/es/princip/getp/application/auth/infra/SecurityPasswordEncoder.java b/get-p-application/src/main/java/es/princip/getp/application/auth/infra/SecurityPasswordEncoder.java new file mode 100644 index 00000000..951797a5 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/auth/infra/SecurityPasswordEncoder.java @@ -0,0 +1,22 @@ +package es.princip.getp.application.auth.infra; + +import es.princip.getp.domain.member.PasswordEncoder; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +class SecurityPasswordEncoder implements PasswordEncoder { + + private final org.springframework.security.crypto.password.PasswordEncoder passwordEncoder; + + @Override + public String encode(CharSequence rawPassword) { + return passwordEncoder.encode(rawPassword); + } + + @Override + public boolean matches(CharSequence rawPassword, String encodedPassword) { + return passwordEncoder.matches(rawPassword, encodedPassword); + } +} diff --git a/src/main/java/es/princip/getp/application/auth/service/AccessTokenService.java b/get-p-application/src/main/java/es/princip/getp/application/auth/service/AccessTokenService.java similarity index 100% rename from src/main/java/es/princip/getp/application/auth/service/AccessTokenService.java rename to get-p-application/src/main/java/es/princip/getp/application/auth/service/AccessTokenService.java diff --git a/src/main/java/es/princip/getp/application/auth/service/AuthService.java b/get-p-application/src/main/java/es/princip/getp/application/auth/service/AuthService.java similarity index 85% rename from src/main/java/es/princip/getp/application/auth/service/AuthService.java rename to get-p-application/src/main/java/es/princip/getp/application/auth/service/AuthService.java index 52ba1eb0..5ac2bdc3 100644 --- a/src/main/java/es/princip/getp/application/auth/service/AuthService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/auth/service/AuthService.java @@ -1,8 +1,6 @@ package es.princip.getp.application.auth.service; -import es.princip.getp.api.controller.auth.dto.request.LoginRequest; -import es.princip.getp.api.controller.auth.dto.response.Token; -import es.princip.getp.api.security.details.PrincipalDetails; +import es.princip.getp.application.auth.dto.response.Token; import es.princip.getp.application.auth.exception.IncorrectEmailOrPasswordException; import es.princip.getp.domain.member.model.Member; import jakarta.servlet.http.HttpServletRequest; @@ -22,10 +20,7 @@ public class AuthService { private final RefreshTokenService refreshTokenService; @Transactional - public Token login(final LoginRequest request) { - final String email = request.email(); - final String password = request.password(); - + public Token login(final String email, final String password) { try { final UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(email, password); diff --git a/get-p-application/src/main/java/es/princip/getp/application/auth/service/EmailVerificationRepository.java b/get-p-application/src/main/java/es/princip/getp/application/auth/service/EmailVerificationRepository.java new file mode 100644 index 00000000..6db4ba2f --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/auth/service/EmailVerificationRepository.java @@ -0,0 +1,10 @@ +package es.princip.getp.application.auth.service; + +import es.princip.getp.domain.auth.EmailVerification; + +public interface EmailVerificationRepository { + + void deleteById(String email); + void save(EmailVerification verification); + EmailVerification findById(String email); +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/application/auth/service/JwtTokenService.java b/get-p-application/src/main/java/es/princip/getp/application/auth/service/JwtTokenService.java similarity index 95% rename from src/main/java/es/princip/getp/application/auth/service/JwtTokenService.java rename to get-p-application/src/main/java/es/princip/getp/application/auth/service/JwtTokenService.java index 7f84459a..e4cdc64a 100644 --- a/src/main/java/es/princip/getp/application/auth/service/JwtTokenService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/auth/service/JwtTokenService.java @@ -1,8 +1,7 @@ package es.princip.getp.application.auth.service; -import es.princip.getp.api.security.details.PrincipalDetails; -import es.princip.getp.api.security.exception.ExpiredTokenException; -import es.princip.getp.api.security.exception.InvalidTokenException; +import es.princip.getp.application.auth.exception.ExpiredTokenException; +import es.princip.getp.application.auth.exception.InvalidTokenException; import es.princip.getp.application.member.port.out.LoadMemberPort; import es.princip.getp.domain.common.model.Email; import es.princip.getp.domain.member.model.Member; diff --git a/src/main/java/es/princip/getp/api/security/details/PrincipalDetails.java b/get-p-application/src/main/java/es/princip/getp/application/auth/service/PrincipalDetails.java similarity index 96% rename from src/main/java/es/princip/getp/api/security/details/PrincipalDetails.java rename to get-p-application/src/main/java/es/princip/getp/application/auth/service/PrincipalDetails.java index 446d5d50..613809e4 100644 --- a/src/main/java/es/princip/getp/api/security/details/PrincipalDetails.java +++ b/get-p-application/src/main/java/es/princip/getp/application/auth/service/PrincipalDetails.java @@ -1,4 +1,4 @@ -package es.princip.getp.api.security.details; +package es.princip.getp.application.auth.service; import es.princip.getp.domain.member.model.Member; import org.springframework.security.core.GrantedAuthority; diff --git a/src/main/java/es/princip/getp/api/security/details/PrincipalDetailsService.java b/get-p-application/src/main/java/es/princip/getp/application/auth/service/PrincipalDetailsService.java similarity index 93% rename from src/main/java/es/princip/getp/api/security/details/PrincipalDetailsService.java rename to get-p-application/src/main/java/es/princip/getp/application/auth/service/PrincipalDetailsService.java index ea0eaf46..1d29cb75 100644 --- a/src/main/java/es/princip/getp/api/security/details/PrincipalDetailsService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/auth/service/PrincipalDetailsService.java @@ -1,4 +1,4 @@ -package es.princip.getp.api.security.details; +package es.princip.getp.application.auth.service; import es.princip.getp.application.member.port.out.LoadMemberPort; import es.princip.getp.domain.common.model.Email; diff --git a/get-p-application/src/main/java/es/princip/getp/application/auth/service/RefreshTokenRepository.java b/get-p-application/src/main/java/es/princip/getp/application/auth/service/RefreshTokenRepository.java new file mode 100644 index 00000000..1472c69d --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/auth/service/RefreshTokenRepository.java @@ -0,0 +1,9 @@ +package es.princip.getp.application.auth.service; + +import es.princip.getp.domain.auth.RefreshToken; + +public interface RefreshTokenRepository { + + boolean existsByRefreshToken(String refreshToken); + void save(RefreshToken refreshToken); +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/application/auth/service/RefreshTokenService.java b/get-p-application/src/main/java/es/princip/getp/application/auth/service/RefreshTokenService.java similarity index 83% rename from src/main/java/es/princip/getp/application/auth/service/RefreshTokenService.java rename to get-p-application/src/main/java/es/princip/getp/application/auth/service/RefreshTokenService.java index cecbe61f..41e4b1fa 100644 --- a/src/main/java/es/princip/getp/application/auth/service/RefreshTokenService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/auth/service/RefreshTokenService.java @@ -1,7 +1,9 @@ package es.princip.getp.application.auth.service; -import es.princip.getp.api.security.exception.InvalidTokenException; +import es.princip.getp.application.auth.exception.InvalidTokenException; import es.princip.getp.application.member.port.out.LoadMemberPort; +import es.princip.getp.domain.auth.RefreshToken; +import es.princip.getp.domain.member.model.MemberId; import io.jsonwebtoken.Claims; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -25,7 +27,7 @@ public RefreshTokenService( this.refreshTokenRepository = refreshTokenRepository; } - public void cacheRefreshToken(final Long memberId, final String refreshToken) { + public void cacheRefreshToken(final MemberId memberId, final String refreshToken) { refreshTokenRepository.save(new RefreshToken(memberId, refreshToken, expireTime)); } diff --git a/src/main/java/es/princip/getp/application/auth/service/SignUpService.java b/get-p-application/src/main/java/es/princip/getp/application/auth/service/SignUpService.java similarity index 90% rename from src/main/java/es/princip/getp/application/auth/service/SignUpService.java rename to get-p-application/src/main/java/es/princip/getp/application/auth/service/SignUpService.java index d25e7d8c..113db7be 100644 --- a/src/main/java/es/princip/getp/application/auth/service/SignUpService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/auth/service/SignUpService.java @@ -1,18 +1,18 @@ package es.princip.getp.application.auth.service; -import es.princip.getp.application.auth.command.SignUpCommand; +import es.princip.getp.application.auth.dto.command.SignUpCommand; import es.princip.getp.application.auth.exception.DuplicatedEmailException; import es.princip.getp.application.member.port.out.CheckMemberPort; import es.princip.getp.application.member.port.out.SaveMemberPort; import es.princip.getp.application.serviceTerm.port.out.CheckServiceTermPort; import es.princip.getp.application.serviceTerm.port.out.LoadServiceTermPort; import es.princip.getp.domain.common.model.Email; +import es.princip.getp.domain.member.PasswordEncoder; import es.princip.getp.domain.member.model.Member; import es.princip.getp.domain.member.model.ServiceTermAgreementData; import es.princip.getp.domain.serviceTerm.model.ServiceTerm; import es.princip.getp.domain.serviceTerm.model.ServiceTermTag; import lombok.RequiredArgsConstructor; -import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -35,7 +35,7 @@ public class SignUpService { @Transactional public void sendEmailVerificationCodeForSignUp(final Email email) { - if (checkMemberPort.existsByEmail(email)) { + if (checkMemberPort.existsBy(email)) { throw new DuplicatedEmailException(); } verificationService.sendVerificationCode(email); @@ -44,7 +44,7 @@ public void sendEmailVerificationCodeForSignUp(final Email email) { @Transactional public void signUp(final SignUpCommand command) { verificationService.verifyEmail(command.email(), command.verificationCode()); - if (checkMemberPort.existsByEmail(command.email())) { + if (checkMemberPort.existsBy(command.email())) { throw new DuplicatedEmailException(); } final Member member = Member.of(command.email(), command.password(), command.memberType()); diff --git a/src/main/java/es/princip/getp/application/auth/service/TokenFactory.java b/get-p-application/src/main/java/es/princip/getp/application/auth/service/TokenFactory.java similarity index 82% rename from src/main/java/es/princip/getp/application/auth/service/TokenFactory.java rename to get-p-application/src/main/java/es/princip/getp/application/auth/service/TokenFactory.java index e536b2f5..3b28d80c 100644 --- a/src/main/java/es/princip/getp/application/auth/service/TokenFactory.java +++ b/get-p-application/src/main/java/es/princip/getp/application/auth/service/TokenFactory.java @@ -1,6 +1,6 @@ package es.princip.getp.application.auth.service; -import es.princip.getp.api.controller.auth.dto.response.Token; +import es.princip.getp.application.auth.dto.response.Token; import es.princip.getp.domain.member.model.Member; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -16,7 +16,7 @@ public Token generateToken(final Member member) { final String accessToken = accessTokenService.generateJwtToken(member); final String refreshToken = refreshTokenService.generateJwtToken(member); - refreshTokenService.cacheRefreshToken(member.getMemberId(), refreshToken); + refreshTokenService.cacheRefreshToken(member.getId(), refreshToken); return new Token(JwtTokenService.BEARER_TYPE, accessToken, refreshToken); } diff --git a/src/main/java/es/princip/getp/application/auth/service/VerificationCodeSender.java b/get-p-application/src/main/java/es/princip/getp/application/auth/service/VerificationCodeSender.java similarity index 100% rename from src/main/java/es/princip/getp/application/auth/service/VerificationCodeSender.java rename to get-p-application/src/main/java/es/princip/getp/application/auth/service/VerificationCodeSender.java diff --git a/src/main/java/es/princip/getp/application/auth/service/VerificationService.java b/get-p-application/src/main/java/es/princip/getp/application/auth/service/VerificationService.java similarity index 91% rename from src/main/java/es/princip/getp/application/auth/service/VerificationService.java rename to get-p-application/src/main/java/es/princip/getp/application/auth/service/VerificationService.java index 4facfd31..0459b435 100644 --- a/src/main/java/es/princip/getp/application/auth/service/VerificationService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/auth/service/VerificationService.java @@ -1,7 +1,7 @@ package es.princip.getp.application.auth.service; import es.princip.getp.application.auth.exception.IncorrectVerificationCodeException; -import es.princip.getp.application.auth.exception.NotFoundVerificationException; +import es.princip.getp.domain.auth.EmailVerification; import es.princip.getp.domain.common.model.Email; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -44,12 +44,11 @@ public void sendVerificationCode(Email email) { @Transactional public void verifyEmail(final Email email, final String verificationCode) { - final EmailVerification verification = verificationRepository.findById(email.getValue()) - .orElseThrow(NotFoundVerificationException::new); + final EmailVerification verification = verificationRepository.findById(email.getValue()); if (!verification.verify(verificationCode)) { throw new IncorrectVerificationCodeException(); } - verificationRepository.delete(verification); + verificationRepository.deleteById(email.getValue()); } private static String generateRandomCode(int length) { diff --git a/src/main/java/es/princip/getp/application/client/command/EditClientCommand.java b/get-p-application/src/main/java/es/princip/getp/application/client/dto/command/EditClientCommand.java similarity index 69% rename from src/main/java/es/princip/getp/application/client/command/EditClientCommand.java rename to get-p-application/src/main/java/es/princip/getp/application/client/dto/command/EditClientCommand.java index 613bb6ed..4791355a 100644 --- a/src/main/java/es/princip/getp/application/client/command/EditClientCommand.java +++ b/get-p-application/src/main/java/es/princip/getp/application/client/dto/command/EditClientCommand.java @@ -1,17 +1,16 @@ -package es.princip.getp.application.client.command; +package es.princip.getp.application.client.dto.command; import es.princip.getp.domain.client.model.Address; -import es.princip.getp.domain.client.model.BankAccount; import es.princip.getp.domain.common.model.Email; import es.princip.getp.domain.common.model.PhoneNumber; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.member.model.Nickname; public record EditClientCommand( - Long memberId, + MemberId memberId, Nickname nickname, Email email, // 미입력 시 회원 가입 시 작성한 이메일 주소가 기본값 PhoneNumber phoneNumber, - Address address, - BankAccount bankAccount + Address address ) { } diff --git a/src/main/java/es/princip/getp/application/client/command/RegisterClientCommand.java b/get-p-application/src/main/java/es/princip/getp/application/client/dto/command/RegisterClientCommand.java similarity index 74% rename from src/main/java/es/princip/getp/application/client/command/RegisterClientCommand.java rename to get-p-application/src/main/java/es/princip/getp/application/client/dto/command/RegisterClientCommand.java index 0b0d0fce..aec5942a 100644 --- a/src/main/java/es/princip/getp/application/client/command/RegisterClientCommand.java +++ b/get-p-application/src/main/java/es/princip/getp/application/client/dto/command/RegisterClientCommand.java @@ -1,7 +1,6 @@ -package es.princip.getp.application.client.command; +package es.princip.getp.application.client.dto.command; import es.princip.getp.domain.client.model.Address; -import es.princip.getp.domain.client.model.BankAccount; import es.princip.getp.domain.common.model.Email; import es.princip.getp.domain.common.model.PhoneNumber; import es.princip.getp.domain.member.model.Member; @@ -12,7 +11,6 @@ public record RegisterClientCommand( Nickname nickname, Email email, // 미입력 시 회원 가입 시 작성한 이메일 주소가 기본값 PhoneNumber phoneNumber, - Address address, - BankAccount bankAccount + Address address ) { } diff --git a/src/main/java/es/princip/getp/api/controller/client/query/dto/ClientResponse.java b/get-p-application/src/main/java/es/princip/getp/application/client/dto/response/ClientResponse.java similarity index 68% rename from src/main/java/es/princip/getp/api/controller/client/query/dto/ClientResponse.java rename to get-p-application/src/main/java/es/princip/getp/application/client/dto/response/ClientResponse.java index ac13e0e1..e5353e1a 100644 --- a/src/main/java/es/princip/getp/api/controller/client/query/dto/ClientResponse.java +++ b/get-p-application/src/main/java/es/princip/getp/application/client/dto/response/ClientResponse.java @@ -1,7 +1,6 @@ -package es.princip.getp.api.controller.client.query.dto; +package es.princip.getp.application.client.dto.response; import es.princip.getp.domain.client.model.Address; -import es.princip.getp.domain.client.model.BankAccount; import java.time.LocalDateTime; @@ -12,7 +11,6 @@ public record ClientResponse( String email, String profileImageUri, Address address, - BankAccount bankAccount, LocalDateTime createdAt, LocalDateTime updatedAt ) { diff --git a/get-p-application/src/main/java/es/princip/getp/application/client/dto/response/RegisterMyClientResponse.java b/get-p-application/src/main/java/es/princip/getp/application/client/dto/response/RegisterMyClientResponse.java new file mode 100644 index 00000000..bd2aad27 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/client/dto/response/RegisterMyClientResponse.java @@ -0,0 +1,6 @@ +package es.princip.getp.application.client.dto.response; + +public record RegisterMyClientResponse( + Long clientId +) { +} diff --git a/src/main/java/es/princip/getp/application/client/exception/AlreadyExistsClientException.java b/get-p-application/src/main/java/es/princip/getp/application/client/exception/AlreadyExistsClientException.java similarity index 100% rename from src/main/java/es/princip/getp/application/client/exception/AlreadyExistsClientException.java rename to get-p-application/src/main/java/es/princip/getp/application/client/exception/AlreadyExistsClientException.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/client/NotFoundClientException.java b/get-p-application/src/main/java/es/princip/getp/application/client/exception/NotFoundClientException.java similarity index 54% rename from src/main/java/es/princip/getp/persistence/adapter/client/NotFoundClientException.java rename to get-p-application/src/main/java/es/princip/getp/application/client/exception/NotFoundClientException.java index 4bca1a05..8b073880 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/client/NotFoundClientException.java +++ b/get-p-application/src/main/java/es/princip/getp/application/client/exception/NotFoundClientException.java @@ -1,14 +1,14 @@ -package es.princip.getp.persistence.adapter.client; +package es.princip.getp.application.client.exception; import es.princip.getp.domain.support.ErrorDescription; -import es.princip.getp.persistence.support.NotFoundException; +import es.princip.getp.application.support.NotFoundException; -class NotFoundClientException extends NotFoundException { +public class NotFoundClientException extends NotFoundException { private static final String code = "NOT_FOUND_CLIENT"; private static final String message = "존재하지 않는 의뢰자입니다."; - NotFoundClientException() { + public NotFoundClientException() { super(ErrorDescription.of(code, message)); } } diff --git a/src/main/java/es/princip/getp/application/client/port/in/EditClientUseCase.java b/get-p-application/src/main/java/es/princip/getp/application/client/port/in/EditClientUseCase.java similarity index 65% rename from src/main/java/es/princip/getp/application/client/port/in/EditClientUseCase.java rename to get-p-application/src/main/java/es/princip/getp/application/client/port/in/EditClientUseCase.java index f780c3f8..a756f57a 100644 --- a/src/main/java/es/princip/getp/application/client/port/in/EditClientUseCase.java +++ b/get-p-application/src/main/java/es/princip/getp/application/client/port/in/EditClientUseCase.java @@ -1,6 +1,6 @@ package es.princip.getp.application.client.port.in; -import es.princip.getp.application.client.command.EditClientCommand; +import es.princip.getp.application.client.dto.command.EditClientCommand; public interface EditClientUseCase { diff --git a/get-p-application/src/main/java/es/princip/getp/application/client/port/in/RegisterClientUseCase.java b/get-p-application/src/main/java/es/princip/getp/application/client/port/in/RegisterClientUseCase.java new file mode 100644 index 00000000..b9686626 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/client/port/in/RegisterClientUseCase.java @@ -0,0 +1,9 @@ +package es.princip.getp.application.client.port.in; + +import es.princip.getp.application.client.dto.command.RegisterClientCommand; +import es.princip.getp.domain.client.model.ClientId; + +public interface RegisterClientUseCase { + + ClientId register(RegisterClientCommand command); +} diff --git a/src/main/java/es/princip/getp/application/client/port/in/RemoveClientUseCase.java b/get-p-application/src/main/java/es/princip/getp/application/client/port/in/RemoveClientUseCase.java similarity index 51% rename from src/main/java/es/princip/getp/application/client/port/in/RemoveClientUseCase.java rename to get-p-application/src/main/java/es/princip/getp/application/client/port/in/RemoveClientUseCase.java index 7c104452..4668b4b2 100644 --- a/src/main/java/es/princip/getp/application/client/port/in/RemoveClientUseCase.java +++ b/get-p-application/src/main/java/es/princip/getp/application/client/port/in/RemoveClientUseCase.java @@ -1,6 +1,8 @@ package es.princip.getp.application.client.port.in; +import es.princip.getp.domain.member.model.MemberId; + public interface RemoveClientUseCase { - void remove(Long memberId); + void remove(MemberId memberId); } diff --git a/get-p-application/src/main/java/es/princip/getp/application/client/port/out/CheckClientPort.java b/get-p-application/src/main/java/es/princip/getp/application/client/port/out/CheckClientPort.java new file mode 100644 index 00000000..cbaceb67 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/client/port/out/CheckClientPort.java @@ -0,0 +1,8 @@ +package es.princip.getp.application.client.port.out; + +import es.princip.getp.domain.member.model.MemberId; + +public interface CheckClientPort { + + boolean existsBy(MemberId memberId); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/client/port/out/ClientQuery.java b/get-p-application/src/main/java/es/princip/getp/application/client/port/out/ClientQuery.java new file mode 100644 index 00000000..9842a8f1 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/client/port/out/ClientQuery.java @@ -0,0 +1,13 @@ +package es.princip.getp.application.client.port.out; + +import es.princip.getp.application.client.dto.response.ClientResponse; +import es.princip.getp.application.project.commission.dto.response.ProjectClientResponse; +import es.princip.getp.domain.client.model.ClientId; +import es.princip.getp.domain.member.model.MemberId; + +public interface ClientQuery { + + ClientResponse findClientBy(final ClientId clientId); + ClientResponse findClientBy(final MemberId memberId); + ProjectClientResponse findProjectClientBy(final ClientId clientId); +} diff --git a/src/main/java/es/princip/getp/application/client/port/out/DeleteClientPort.java b/get-p-application/src/main/java/es/princip/getp/application/client/port/out/DeleteClientPort.java similarity index 100% rename from src/main/java/es/princip/getp/application/client/port/out/DeleteClientPort.java rename to get-p-application/src/main/java/es/princip/getp/application/client/port/out/DeleteClientPort.java diff --git a/src/main/java/es/princip/getp/application/client/port/out/LoadClientPort.java b/get-p-application/src/main/java/es/princip/getp/application/client/port/out/LoadClientPort.java similarity index 61% rename from src/main/java/es/princip/getp/application/client/port/out/LoadClientPort.java rename to get-p-application/src/main/java/es/princip/getp/application/client/port/out/LoadClientPort.java index c2a4082e..b259be9c 100644 --- a/src/main/java/es/princip/getp/application/client/port/out/LoadClientPort.java +++ b/get-p-application/src/main/java/es/princip/getp/application/client/port/out/LoadClientPort.java @@ -1,8 +1,9 @@ package es.princip.getp.application.client.port.out; import es.princip.getp.domain.client.model.Client; +import es.princip.getp.domain.member.model.MemberId; public interface LoadClientPort { - Client loadBy(Long memberId); + Client loadBy(MemberId memberId); } diff --git a/src/main/java/es/princip/getp/application/client/port/out/SaveClientPort.java b/get-p-application/src/main/java/es/princip/getp/application/client/port/out/SaveClientPort.java similarity index 62% rename from src/main/java/es/princip/getp/application/client/port/out/SaveClientPort.java rename to get-p-application/src/main/java/es/princip/getp/application/client/port/out/SaveClientPort.java index c96550cc..1a651a8f 100644 --- a/src/main/java/es/princip/getp/application/client/port/out/SaveClientPort.java +++ b/get-p-application/src/main/java/es/princip/getp/application/client/port/out/SaveClientPort.java @@ -1,8 +1,9 @@ package es.princip.getp.application.client.port.out; import es.princip.getp.domain.client.model.Client; +import es.princip.getp.domain.client.model.ClientId; public interface SaveClientPort { - Long save(Client client); + ClientId save(Client client); } diff --git a/src/main/java/es/princip/getp/application/client/port/out/UpdateClientPort.java b/get-p-application/src/main/java/es/princip/getp/application/client/port/out/UpdateClientPort.java similarity index 100% rename from src/main/java/es/princip/getp/application/client/port/out/UpdateClientPort.java rename to get-p-application/src/main/java/es/princip/getp/application/client/port/out/UpdateClientPort.java diff --git a/src/main/java/es/princip/getp/application/client/service/EditClientService.java b/get-p-application/src/main/java/es/princip/getp/application/client/service/EditClientService.java similarity index 83% rename from src/main/java/es/princip/getp/application/client/service/EditClientService.java rename to get-p-application/src/main/java/es/princip/getp/application/client/service/EditClientService.java index 67ebb226..694367dd 100644 --- a/src/main/java/es/princip/getp/application/client/service/EditClientService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/client/service/EditClientService.java @@ -1,10 +1,10 @@ package es.princip.getp.application.client.service; -import es.princip.getp.application.client.command.EditClientCommand; +import es.princip.getp.application.client.dto.command.EditClientCommand; import es.princip.getp.application.client.port.in.EditClientUseCase; import es.princip.getp.application.client.port.out.LoadClientPort; import es.princip.getp.application.client.port.out.UpdateClientPort; -import es.princip.getp.application.member.command.EditMemberCommand; +import es.princip.getp.application.member.dto.command.EditMemberCommand; import es.princip.getp.application.member.port.in.EditMemberUseCase; import es.princip.getp.domain.client.model.Client; import lombok.RequiredArgsConstructor; @@ -26,7 +26,7 @@ public class EditClientService implements EditClientUseCase { public void edit(EditClientCommand command) { editMemberUseCase.editMember(EditMemberCommand.from(command)); final Client client = loadClientPort.loadBy(command.memberId()); - client.edit(command.email(), command.address(), command.bankAccount()); + client.edit(command.email(), command.address()); updateClientPort.update(client); } } \ No newline at end of file diff --git a/src/main/java/es/princip/getp/application/client/service/RegisterClientService.java b/get-p-application/src/main/java/es/princip/getp/application/client/service/RegisterClientService.java similarity index 82% rename from src/main/java/es/princip/getp/application/client/service/RegisterClientService.java rename to get-p-application/src/main/java/es/princip/getp/application/client/service/RegisterClientService.java index 27a6305c..efecb651 100644 --- a/src/main/java/es/princip/getp/application/client/service/RegisterClientService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/client/service/RegisterClientService.java @@ -1,13 +1,14 @@ package es.princip.getp.application.client.service; -import es.princip.getp.application.client.command.RegisterClientCommand; +import es.princip.getp.application.client.dto.command.RegisterClientCommand; import es.princip.getp.application.client.exception.AlreadyExistsClientException; import es.princip.getp.application.client.port.in.RegisterClientUseCase; import es.princip.getp.application.client.port.out.CheckClientPort; import es.princip.getp.application.client.port.out.SaveClientPort; -import es.princip.getp.application.member.command.EditMemberCommand; +import es.princip.getp.application.member.dto.command.EditMemberCommand; import es.princip.getp.application.member.port.in.EditMemberUseCase; import es.princip.getp.domain.client.model.Client; +import es.princip.getp.domain.client.model.ClientId; import es.princip.getp.domain.common.model.Email; import es.princip.getp.domain.member.model.Member; import lombok.RequiredArgsConstructor; @@ -26,9 +27,9 @@ public class RegisterClientService implements RegisterClientUseCase { @Override @Transactional - public Long register(RegisterClientCommand command) { + public ClientId register(RegisterClientCommand command) { final Member member = command.member(); - if (checkClientPort.existsBy(member.getMemberId())) { + if (checkClientPort.existsBy(member.getId())) { throw new AlreadyExistsClientException(); } editMemberUseCase.editMember(EditMemberCommand.from(command)); @@ -36,9 +37,8 @@ public Long register(RegisterClientCommand command) { final Email email = command.email() == null ? member.getEmail() : command.email(); final Client client = Client.builder() .email(email) - .bankAccount(command.bankAccount()) .address(command.address()) - .memberId(member.getMemberId()) + .memberId(member.getId()) .build(); return saveClientPort.save(client); } diff --git a/src/main/java/es/princip/getp/application/client/service/RemoveRegisterClientService.java b/get-p-application/src/main/java/es/princip/getp/application/client/service/RemoveRegisterClientService.java similarity index 89% rename from src/main/java/es/princip/getp/application/client/service/RemoveRegisterClientService.java rename to get-p-application/src/main/java/es/princip/getp/application/client/service/RemoveRegisterClientService.java index 062b193d..a4959f9e 100644 --- a/src/main/java/es/princip/getp/application/client/service/RemoveRegisterClientService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/client/service/RemoveRegisterClientService.java @@ -4,6 +4,7 @@ import es.princip.getp.application.client.port.out.DeleteClientPort; import es.princip.getp.application.client.port.out.LoadClientPort; import es.princip.getp.domain.client.model.Client; +import es.princip.getp.domain.member.model.MemberId; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -18,7 +19,7 @@ public class RemoveRegisterClientService implements RemoveClientUseCase { @Override @Transactional - public void remove(Long memberId) { + public void remove(MemberId memberId) { final Client client = loadClientPort.loadBy(memberId); deleteClientPort.delete(client); } diff --git a/get-p-application/src/main/java/es/princip/getp/application/common/dto/response/AddressResponse.java b/get-p-application/src/main/java/es/princip/getp/application/common/dto/response/AddressResponse.java new file mode 100644 index 00000000..fcb9057c --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/common/dto/response/AddressResponse.java @@ -0,0 +1,8 @@ +package es.princip.getp.application.common.dto.response; + +public record AddressResponse( + String zipcode, + String street, + String detail +) { +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/application/config/AopConfig.java b/get-p-application/src/main/java/es/princip/getp/application/config/AopConfig.java similarity index 100% rename from src/main/java/es/princip/getp/application/config/AopConfig.java rename to get-p-application/src/main/java/es/princip/getp/application/config/AopConfig.java diff --git a/src/main/java/es/princip/getp/application/config/TimeTraceAop.java b/get-p-application/src/main/java/es/princip/getp/application/config/TimeTraceAop.java similarity index 100% rename from src/main/java/es/princip/getp/application/config/TimeTraceAop.java rename to get-p-application/src/main/java/es/princip/getp/application/config/TimeTraceAop.java diff --git a/src/main/java/es/princip/getp/application/like/exception/AlreadyLikedException.java b/get-p-application/src/main/java/es/princip/getp/application/like/exception/AlreadyLikedException.java similarity index 100% rename from src/main/java/es/princip/getp/application/like/exception/AlreadyLikedException.java rename to get-p-application/src/main/java/es/princip/getp/application/like/exception/AlreadyLikedException.java diff --git a/src/main/java/es/princip/getp/application/like/exception/NeverLikedException.java b/get-p-application/src/main/java/es/princip/getp/application/like/exception/NeverLikedException.java similarity index 100% rename from src/main/java/es/princip/getp/application/like/exception/NeverLikedException.java rename to get-p-application/src/main/java/es/princip/getp/application/like/exception/NeverLikedException.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/like/exception/NotFoundLikeException.java b/get-p-application/src/main/java/es/princip/getp/application/like/exception/NotFoundLikeException.java similarity index 75% rename from src/main/java/es/princip/getp/persistence/adapter/like/exception/NotFoundLikeException.java rename to get-p-application/src/main/java/es/princip/getp/application/like/exception/NotFoundLikeException.java index 5855d276..32be50fa 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/like/exception/NotFoundLikeException.java +++ b/get-p-application/src/main/java/es/princip/getp/application/like/exception/NotFoundLikeException.java @@ -1,7 +1,7 @@ -package es.princip.getp.persistence.adapter.like.exception; +package es.princip.getp.application.like.exception; import es.princip.getp.domain.support.ErrorDescription; -import es.princip.getp.persistence.support.NotFoundException; +import es.princip.getp.application.support.NotFoundException; public class NotFoundLikeException extends NotFoundException { diff --git a/get-p-application/src/main/java/es/princip/getp/application/like/people/port/in/LikePeopleUseCase.java b/get-p-application/src/main/java/es/princip/getp/application/like/people/port/in/LikePeopleUseCase.java new file mode 100644 index 00000000..a1c6ecf3 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/like/people/port/in/LikePeopleUseCase.java @@ -0,0 +1,9 @@ +package es.princip.getp.application.like.people.port.in; + +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.model.PeopleId; + +public interface LikePeopleUseCase { + + void like(MemberId memberId, PeopleId peopleId); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/like/people/port/in/UnlikePeopleUseCase.java b/get-p-application/src/main/java/es/princip/getp/application/like/people/port/in/UnlikePeopleUseCase.java new file mode 100644 index 00000000..6455689a --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/like/people/port/in/UnlikePeopleUseCase.java @@ -0,0 +1,9 @@ +package es.princip.getp.application.like.people.port.in; + +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.model.PeopleId; + +public interface UnlikePeopleUseCase { + + void unlike(MemberId memberId, PeopleId peopleId); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/like/people/port/out/CheckPeopleLikePort.java b/get-p-application/src/main/java/es/princip/getp/application/like/people/port/out/CheckPeopleLikePort.java new file mode 100644 index 00000000..33d28f03 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/like/people/port/out/CheckPeopleLikePort.java @@ -0,0 +1,12 @@ +package es.princip.getp.application.like.people.port.out; + +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.model.PeopleId; + +public interface CheckPeopleLikePort { + + boolean existsBy(MemberId memberId, PeopleId peopleId); + + Boolean existsBy(Member member, PeopleId peopleId); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/like/people/port/out/CountPeopleLikePort.java b/get-p-application/src/main/java/es/princip/getp/application/like/people/port/out/CountPeopleLikePort.java new file mode 100644 index 00000000..d588c42e --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/like/people/port/out/CountPeopleLikePort.java @@ -0,0 +1,12 @@ +package es.princip.getp.application.like.people.port.out; + +import es.princip.getp.domain.people.model.PeopleId; + +import java.util.Map; + +public interface CountPeopleLikePort { + + Long countBy(PeopleId peopleId); + + Map countBy(PeopleId... peopleIds); +} diff --git a/src/main/java/es/princip/getp/application/like/people/port/out/DeletePeopleLikePort.java b/get-p-application/src/main/java/es/princip/getp/application/like/people/port/out/DeletePeopleLikePort.java similarity index 100% rename from src/main/java/es/princip/getp/application/like/people/port/out/DeletePeopleLikePort.java rename to get-p-application/src/main/java/es/princip/getp/application/like/people/port/out/DeletePeopleLikePort.java diff --git a/get-p-application/src/main/java/es/princip/getp/application/like/people/port/out/LoadPeopleLikePort.java b/get-p-application/src/main/java/es/princip/getp/application/like/people/port/out/LoadPeopleLikePort.java new file mode 100644 index 00000000..fe9ca08f --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/like/people/port/out/LoadPeopleLikePort.java @@ -0,0 +1,10 @@ +package es.princip.getp.application.like.people.port.out; + +import es.princip.getp.domain.like.people.model.PeopleLike; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.model.PeopleId; + +public interface LoadPeopleLikePort { + + PeopleLike loadBy(MemberId memberId, PeopleId peopleId); +} diff --git a/src/main/java/es/princip/getp/application/like/people/port/out/SavePeopleLikePort.java b/get-p-application/src/main/java/es/princip/getp/application/like/people/port/out/SavePeopleLikePort.java similarity index 100% rename from src/main/java/es/princip/getp/application/like/people/port/out/SavePeopleLikePort.java rename to get-p-application/src/main/java/es/princip/getp/application/like/people/port/out/SavePeopleLikePort.java diff --git a/src/main/java/es/princip/getp/application/like/people/service/LikePeopleService.java b/get-p-application/src/main/java/es/princip/getp/application/like/people/service/LikePeopleService.java similarity index 56% rename from src/main/java/es/princip/getp/application/like/people/service/LikePeopleService.java rename to get-p-application/src/main/java/es/princip/getp/application/like/people/service/LikePeopleService.java index c9b5ab2b..0622a52e 100644 --- a/src/main/java/es/princip/getp/application/like/people/service/LikePeopleService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/like/people/service/LikePeopleService.java @@ -1,13 +1,14 @@ package es.princip.getp.application.like.people.service; -import es.princip.getp.application.client.port.out.LoadClientPort; import es.princip.getp.application.like.exception.AlreadyLikedException; +import es.princip.getp.application.like.people.port.in.LikePeopleUseCase; import es.princip.getp.application.like.people.port.out.CheckPeopleLikePort; import es.princip.getp.application.like.people.port.out.SavePeopleLikePort; import es.princip.getp.application.people.port.out.LoadPeoplePort; -import es.princip.getp.domain.client.model.Client; import es.princip.getp.domain.like.people.model.PeopleLike; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.people.model.PeopleId; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -15,32 +16,30 @@ @Service @RequiredArgsConstructor @Transactional(readOnly = true) -class LikePeopleService implements es.princip.getp.application.like.people.port.in.LikePeopleUseCase { +class LikePeopleService implements LikePeopleUseCase { - private final LoadClientPort loadClientPort; private final LoadPeoplePort loadPeoplePort; private final CheckPeopleLikePort checkPeopleLikePort; private final SavePeopleLikePort savePeopleLikePort; @Transactional - public void like(final Long memberId, final Long peopleId) { - final Client client = loadClientPort.loadBy(memberId); + public void like(final MemberId memberId, final PeopleId peopleId) { // TODO: 조회 성능 개선 필요 - final People people = loadPeoplePort.loadByPeopleId(peopleId); - checkAlreadyLiked(client.getClientId(), peopleId); - final PeopleLike peopleLike = buildPeopleLike(client.getClientId(), peopleId); + final People people = loadPeoplePort.loadBy(peopleId); + checkAlreadyLiked(memberId, peopleId); + final PeopleLike peopleLike = buildPeopleLike(memberId, peopleId); savePeopleLikePort.save(peopleLike); } - private void checkAlreadyLiked(final Long clientId, final Long peopleId) { - if (checkPeopleLikePort.existsBy(clientId, peopleId)) { + private void checkAlreadyLiked(final MemberId memberId, final PeopleId peopleId) { + if (checkPeopleLikePort.existsBy(memberId, peopleId)) { throw new AlreadyLikedException(); } } - private PeopleLike buildPeopleLike(final Long clientId, final Long peopleId) { + private PeopleLike buildPeopleLike(final MemberId memberId, final PeopleId peopleId) { return PeopleLike.builder() - .clientId(clientId) + .memberId(memberId) .peopleId(peopleId) .build(); } diff --git a/src/main/java/es/princip/getp/application/like/people/service/UnlikePeopleService.java b/get-p-application/src/main/java/es/princip/getp/application/like/people/service/UnlikePeopleService.java similarity index 69% rename from src/main/java/es/princip/getp/application/like/people/service/UnlikePeopleService.java rename to get-p-application/src/main/java/es/princip/getp/application/like/people/service/UnlikePeopleService.java index 020d1477..56a3d3b4 100644 --- a/src/main/java/es/princip/getp/application/like/people/service/UnlikePeopleService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/like/people/service/UnlikePeopleService.java @@ -1,15 +1,15 @@ package es.princip.getp.application.like.people.service; -import es.princip.getp.application.client.port.out.LoadClientPort; import es.princip.getp.application.like.exception.NeverLikedException; import es.princip.getp.application.like.people.port.in.UnlikePeopleUseCase; import es.princip.getp.application.like.people.port.out.CheckPeopleLikePort; import es.princip.getp.application.like.people.port.out.DeletePeopleLikePort; import es.princip.getp.application.like.people.port.out.LoadPeopleLikePort; import es.princip.getp.application.people.port.out.LoadPeoplePort; -import es.princip.getp.domain.client.model.Client; import es.princip.getp.domain.like.people.model.PeopleLike; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.people.model.PeopleId; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -19,24 +19,22 @@ @Transactional(readOnly = true) class UnlikePeopleService implements UnlikePeopleUseCase { - private final LoadClientPort loadClientPort; private final LoadPeoplePort loadPeoplePort; private final LoadPeopleLikePort loadPeopleLikePort; private final CheckPeopleLikePort checkPeopleLikePort; private final DeletePeopleLikePort deletePeopleLikePort; @Transactional - public void unlike(final Long memberId, final Long peopleId) { - final Client client = loadClientPort.loadBy(memberId); + public void unlike(final MemberId memberId, final PeopleId peopleId) { // TODO: 조회 성능 개선 필요 - final People people = loadPeoplePort.loadByPeopleId(peopleId); - final PeopleLike peopleLike = loadPeopleLikePort.loadBy(client.getClientId(), peopleId); - checkNeverLiked(client.getClientId(), peopleId); + final People people = loadPeoplePort.loadBy(peopleId); + final PeopleLike peopleLike = loadPeopleLikePort.loadBy(memberId, peopleId); + checkNeverLiked(memberId, peopleId); deletePeopleLikePort.delete(peopleLike); } - private void checkNeverLiked(final Long clientId, final Long peopleId) { - if (!checkPeopleLikePort.existsBy(clientId, peopleId)) { + private void checkNeverLiked(final MemberId memberId, final PeopleId peopleId) { + if (!checkPeopleLikePort.existsBy(memberId, peopleId)) { throw new NeverLikedException(); } } diff --git a/get-p-application/src/main/java/es/princip/getp/application/like/project/port/in/LikeProjectUseCase.java b/get-p-application/src/main/java/es/princip/getp/application/like/project/port/in/LikeProjectUseCase.java new file mode 100644 index 00000000..0258927a --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/like/project/port/in/LikeProjectUseCase.java @@ -0,0 +1,9 @@ +package es.princip.getp.application.like.project.port.in; + +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.project.commission.model.ProjectId; + +public interface LikeProjectUseCase { + + void like(MemberId memberId, ProjectId projectId); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/like/project/port/in/UnlikeProjectUseCase.java b/get-p-application/src/main/java/es/princip/getp/application/like/project/port/in/UnlikeProjectUseCase.java new file mode 100644 index 00000000..35ec6ae3 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/like/project/port/in/UnlikeProjectUseCase.java @@ -0,0 +1,9 @@ +package es.princip.getp.application.like.project.port.in; + +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.project.commission.model.ProjectId; + +public interface UnlikeProjectUseCase { + + void unlike(MemberId memberId, ProjectId projectId); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/like/project/port/out/CheckProjectLikePort.java b/get-p-application/src/main/java/es/princip/getp/application/like/project/port/out/CheckProjectLikePort.java new file mode 100644 index 00000000..fd3c7d27 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/like/project/port/out/CheckProjectLikePort.java @@ -0,0 +1,12 @@ +package es.princip.getp.application.like.project.port.out; + +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.project.commission.model.ProjectId; + +public interface CheckProjectLikePort { + + boolean existsBy(MemberId memberId, ProjectId projectId); + + Boolean existsBy(Member member, ProjectId projectId); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/like/project/port/out/CountProjectLikePort.java b/get-p-application/src/main/java/es/princip/getp/application/like/project/port/out/CountProjectLikePort.java new file mode 100644 index 00000000..44a11221 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/like/project/port/out/CountProjectLikePort.java @@ -0,0 +1,12 @@ +package es.princip.getp.application.like.project.port.out; + +import es.princip.getp.domain.project.commission.model.ProjectId; + +import java.util.Map; + +public interface CountProjectLikePort { + + Long countBy(ProjectId projectId); + + Map countBy(ProjectId... projectIds); +} diff --git a/src/main/java/es/princip/getp/application/like/project/port/out/DeleteProjectLikePort.java b/get-p-application/src/main/java/es/princip/getp/application/like/project/port/out/DeleteProjectLikePort.java similarity index 100% rename from src/main/java/es/princip/getp/application/like/project/port/out/DeleteProjectLikePort.java rename to get-p-application/src/main/java/es/princip/getp/application/like/project/port/out/DeleteProjectLikePort.java diff --git a/get-p-application/src/main/java/es/princip/getp/application/like/project/port/out/FindLikedProjectPort.java b/get-p-application/src/main/java/es/princip/getp/application/like/project/port/out/FindLikedProjectPort.java new file mode 100644 index 00000000..028425a2 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/like/project/port/out/FindLikedProjectPort.java @@ -0,0 +1,11 @@ +package es.princip.getp.application.like.project.port.out; + +import es.princip.getp.application.project.commission.dto.response.ProjectCardResponse; +import es.princip.getp.domain.member.model.MemberId; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +public interface FindLikedProjectPort { + + Page findBy(MemberId memberId, Pageable pageable); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/like/project/port/out/LoadProjectLikePort.java b/get-p-application/src/main/java/es/princip/getp/application/like/project/port/out/LoadProjectLikePort.java new file mode 100644 index 00000000..ae1d812d --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/like/project/port/out/LoadProjectLikePort.java @@ -0,0 +1,10 @@ +package es.princip.getp.application.like.project.port.out; + +import es.princip.getp.domain.like.project.model.ProjectLike; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.project.commission.model.ProjectId; + +public interface LoadProjectLikePort { + + ProjectLike loadBy(MemberId memberId, ProjectId projectId); +} diff --git a/src/main/java/es/princip/getp/application/like/project/port/out/SaveProjectLikePort.java b/get-p-application/src/main/java/es/princip/getp/application/like/project/port/out/SaveProjectLikePort.java similarity index 100% rename from src/main/java/es/princip/getp/application/like/project/port/out/SaveProjectLikePort.java rename to get-p-application/src/main/java/es/princip/getp/application/like/project/port/out/SaveProjectLikePort.java diff --git a/src/main/java/es/princip/getp/application/like/project/service/LikeProjectService.java b/get-p-application/src/main/java/es/princip/getp/application/like/project/service/LikeProjectService.java similarity index 68% rename from src/main/java/es/princip/getp/application/like/project/service/LikeProjectService.java rename to get-p-application/src/main/java/es/princip/getp/application/like/project/service/LikeProjectService.java index 58e6954e..b82a918a 100644 --- a/src/main/java/es/princip/getp/application/like/project/service/LikeProjectService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/like/project/service/LikeProjectService.java @@ -4,11 +4,11 @@ import es.princip.getp.application.like.project.port.in.LikeProjectUseCase; import es.princip.getp.application.like.project.port.out.CheckProjectLikePort; import es.princip.getp.application.like.project.port.out.SaveProjectLikePort; -import es.princip.getp.application.people.port.out.LoadPeoplePort; import es.princip.getp.application.project.commission.port.out.LoadProjectPort; import es.princip.getp.domain.like.project.model.ProjectLike; -import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.project.commission.model.Project; +import es.princip.getp.domain.project.commission.model.ProjectId; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -19,7 +19,6 @@ class LikeProjectService implements LikeProjectUseCase { private final LoadProjectPort loadProjectPort; - private final LoadPeoplePort loadPeoplePort; private final CheckProjectLikePort checkProjectLikePort; private final SaveProjectLikePort saveProjectLikePort; @@ -30,24 +29,23 @@ class LikeProjectService implements LikeProjectUseCase { * @param projectId 좋아요를 누를 프로젝트 ID */ @Transactional - public void like(final Long memberId, final Long projectId) { - final People people = loadPeoplePort.loadBy(memberId); + public void like(final MemberId memberId, final ProjectId projectId) { // TODO: 조회 성능 개선 필요 final Project project = loadProjectPort.loadBy(projectId); - checkAlreadyLiked(people.getId(), projectId); - final ProjectLike projectLike = buildProjectLike(people.getId(), projectId); + checkAlreadyLiked(memberId, projectId); + final ProjectLike projectLike = buildProjectLike(memberId, projectId); saveProjectLikePort.save(projectLike); } - private void checkAlreadyLiked(final Long peopleId, final Long projectId) { - if (checkProjectLikePort.existsBy(peopleId, projectId)) { + private void checkAlreadyLiked(final MemberId memberId, final ProjectId projectId) { + if (checkProjectLikePort.existsBy(memberId, projectId)) { throw new AlreadyLikedException(); } } - private ProjectLike buildProjectLike(final Long peopleId, final Long projectId) { + private ProjectLike buildProjectLike(final MemberId memberId, final ProjectId projectId) { return ProjectLike.builder() - .peopleId(peopleId) + .memberId(memberId) .projectId(projectId) .build(); } diff --git a/src/main/java/es/princip/getp/application/like/project/service/UnlikeProjectService.java b/get-p-application/src/main/java/es/princip/getp/application/like/project/service/UnlikeProjectService.java similarity index 73% rename from src/main/java/es/princip/getp/application/like/project/service/UnlikeProjectService.java rename to get-p-application/src/main/java/es/princip/getp/application/like/project/service/UnlikeProjectService.java index 38a4687f..eea5e5f1 100644 --- a/src/main/java/es/princip/getp/application/like/project/service/UnlikeProjectService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/like/project/service/UnlikeProjectService.java @@ -5,11 +5,11 @@ import es.princip.getp.application.like.project.port.out.CheckProjectLikePort; import es.princip.getp.application.like.project.port.out.DeleteProjectLikePort; import es.princip.getp.application.like.project.port.out.LoadProjectLikePort; -import es.princip.getp.application.people.port.out.LoadPeoplePort; import es.princip.getp.application.project.commission.port.out.LoadProjectPort; import es.princip.getp.domain.like.project.model.ProjectLike; -import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.project.commission.model.Project; +import es.princip.getp.domain.project.commission.model.ProjectId; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -20,23 +20,21 @@ class UnlikeProjectService implements UnlikeProjectUseCase { private final LoadProjectLikePort loadProjectLikePort; - private final LoadPeoplePort loadPeoplePort; private final LoadProjectPort loadProjectPort; private final CheckProjectLikePort checkProjectLikePort; private final DeleteProjectLikePort deleteProjectLikePort; @Transactional - public void unlike(final Long memberId, final Long projectId) { - final People people = loadPeoplePort.loadBy(memberId); + public void unlike(final MemberId memberId, final ProjectId projectId) { // TODO: 조회 성능 개선 필요 final Project project = loadProjectPort.loadBy(projectId); - checkNeverLiked(people.getId(), projectId); - final ProjectLike projectLike = loadProjectLikePort.loadBy(people.getId(), projectId); + checkNeverLiked(memberId, projectId); + final ProjectLike projectLike = loadProjectLikePort.loadBy(memberId, projectId); deleteProjectLikePort.delete(projectLike); } - private void checkNeverLiked(final Long peopleId, final Long projectId) { - if (!checkProjectLikePort.existsBy(peopleId, projectId)) { + private void checkNeverLiked(final MemberId memberId, final ProjectId projectId) { + if (!checkProjectLikePort.existsBy(memberId, projectId)) { throw new NeverLikedException(); } } diff --git a/get-p-application/src/main/java/es/princip/getp/application/mail/SendMailService.java b/get-p-application/src/main/java/es/princip/getp/application/mail/SendMailService.java new file mode 100644 index 00000000..6fb4b487 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/mail/SendMailService.java @@ -0,0 +1,39 @@ +package es.princip.getp.application.mail; + +import es.princip.getp.application.mail.command.SendMailCommand; +import es.princip.getp.application.mail.port.in.SendMailUseCase; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeMessage; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +class SendMailService implements SendMailUseCase { + + private final JavaMailSender mailSender; + + @Value("${spring.mail.templates.logo}") + private Resource logo; + + @Async + public void send(final SendMailCommand command) throws MessagingException { + final MimeMessage message = messageFrom(command); + mailSender.send(message); + } + + private MimeMessage messageFrom(final SendMailCommand command) throws MessagingException { + final MimeMessage mimeMessage = mailSender.createMimeMessage(); + final MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "utf-8"); + helper.setTo(command.email().getValue()); + helper.setSubject(command.title()); + helper.setText(command.text(), true); + helper.addInline("logo", logo); + return mimeMessage; + } +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/application/mail/command/SendMailCommand.java b/get-p-application/src/main/java/es/princip/getp/application/mail/command/SendMailCommand.java similarity index 100% rename from src/main/java/es/princip/getp/application/mail/command/SendMailCommand.java rename to get-p-application/src/main/java/es/princip/getp/application/mail/command/SendMailCommand.java diff --git a/src/main/java/es/princip/getp/application/mail/port/in/SendMailUseCase.java b/get-p-application/src/main/java/es/princip/getp/application/mail/port/in/SendMailUseCase.java similarity index 58% rename from src/main/java/es/princip/getp/application/mail/port/in/SendMailUseCase.java rename to get-p-application/src/main/java/es/princip/getp/application/mail/port/in/SendMailUseCase.java index ca00f7b3..267269a5 100644 --- a/src/main/java/es/princip/getp/application/mail/port/in/SendMailUseCase.java +++ b/get-p-application/src/main/java/es/princip/getp/application/mail/port/in/SendMailUseCase.java @@ -1,8 +1,9 @@ package es.princip.getp.application.mail.port.in; import es.princip.getp.application.mail.command.SendMailCommand; +import jakarta.mail.MessagingException; public interface SendMailUseCase { - void send(final SendMailCommand command); + void send(final SendMailCommand command) throws MessagingException; } diff --git a/src/main/java/es/princip/getp/application/member/command/EditMemberCommand.java b/get-p-application/src/main/java/es/princip/getp/application/member/dto/command/EditMemberCommand.java similarity index 71% rename from src/main/java/es/princip/getp/application/member/command/EditMemberCommand.java rename to get-p-application/src/main/java/es/princip/getp/application/member/dto/command/EditMemberCommand.java index e45d1fe3..c01ae64c 100644 --- a/src/main/java/es/princip/getp/application/member/command/EditMemberCommand.java +++ b/get-p-application/src/main/java/es/princip/getp/application/member/dto/command/EditMemberCommand.java @@ -1,14 +1,15 @@ -package es.princip.getp.application.member.command; +package es.princip.getp.application.member.dto.command; -import es.princip.getp.application.client.command.EditClientCommand; -import es.princip.getp.application.client.command.RegisterClientCommand; -import es.princip.getp.application.people.command.EditPeopleCommand; -import es.princip.getp.application.people.command.RegisterPeopleCommand; +import es.princip.getp.application.client.dto.command.EditClientCommand; +import es.princip.getp.application.client.dto.command.RegisterClientCommand; +import es.princip.getp.application.people.dto.command.EditPeopleCommand; +import es.princip.getp.application.people.dto.command.RegisterPeopleCommand; import es.princip.getp.domain.common.model.PhoneNumber; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.member.model.Nickname; public record EditMemberCommand( - Long memberId, + MemberId memberId, Nickname nickname, PhoneNumber phoneNumber ) { @@ -30,7 +31,7 @@ public static EditMemberCommand from(final EditPeopleCommand command) { public static EditMemberCommand from(final RegisterClientCommand command) { return new EditMemberCommand( - command.member().getMemberId(), + command.member().getId(), command.nickname(), command.phoneNumber() ); diff --git a/get-p-application/src/main/java/es/princip/getp/application/member/dto/command/RegisterProfileImageCommand.java b/get-p-application/src/main/java/es/princip/getp/application/member/dto/command/RegisterProfileImageCommand.java new file mode 100644 index 00000000..e57d9422 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/member/dto/command/RegisterProfileImageCommand.java @@ -0,0 +1,10 @@ +package es.princip.getp.application.member.dto.command; + +import es.princip.getp.domain.member.model.MemberId; +import org.springframework.web.multipart.MultipartFile; + +public record RegisterProfileImageCommand( + MemberId memberId, + MultipartFile image +) { +} diff --git a/src/main/java/es/princip/getp/api/controller/member/query/dto/response/MemberResponse.java b/get-p-application/src/main/java/es/princip/getp/application/member/dto/response/MemberResponse.java similarity index 90% rename from src/main/java/es/princip/getp/api/controller/member/query/dto/response/MemberResponse.java rename to get-p-application/src/main/java/es/princip/getp/application/member/dto/response/MemberResponse.java index 56f621a3..b10ddc12 100644 --- a/src/main/java/es/princip/getp/api/controller/member/query/dto/response/MemberResponse.java +++ b/get-p-application/src/main/java/es/princip/getp/application/member/dto/response/MemberResponse.java @@ -1,4 +1,4 @@ -package es.princip.getp.api.controller.member.query.dto.response; +package es.princip.getp.application.member.dto.response; import es.princip.getp.domain.member.model.Member; import es.princip.getp.domain.member.model.MemberType; @@ -19,7 +19,7 @@ public record MemberResponse( ) { public static MemberResponse from(final Member member) { return new MemberResponse( - member.getMemberId(), + member.getId().getValue(), member.getEmail().getValue(), Optional.ofNullable(member.getNickname()) .map(Nickname::getValue) diff --git a/get-p-application/src/main/java/es/princip/getp/application/member/dto/response/ProfileImageResponse.java b/get-p-application/src/main/java/es/princip/getp/application/member/dto/response/ProfileImageResponse.java new file mode 100644 index 00000000..b30604e4 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/member/dto/response/ProfileImageResponse.java @@ -0,0 +1,4 @@ +package es.princip.getp.application.member.dto.response; + +public record ProfileImageResponse(String profileImageUri) { +} diff --git a/src/main/java/es/princip/getp/persistence/adapter/member/NotFoundMemberException.java b/get-p-application/src/main/java/es/princip/getp/application/member/exception/NotFoundMemberException.java similarity index 75% rename from src/main/java/es/princip/getp/persistence/adapter/member/NotFoundMemberException.java rename to get-p-application/src/main/java/es/princip/getp/application/member/exception/NotFoundMemberException.java index 3ede4426..cfc041b9 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/member/NotFoundMemberException.java +++ b/get-p-application/src/main/java/es/princip/getp/application/member/exception/NotFoundMemberException.java @@ -1,7 +1,7 @@ -package es.princip.getp.persistence.adapter.member; +package es.princip.getp.application.member.exception; import es.princip.getp.domain.support.ErrorDescription; -import es.princip.getp.persistence.support.NotFoundException; +import es.princip.getp.application.support.NotFoundException; public class NotFoundMemberException extends NotFoundException { diff --git a/src/main/java/es/princip/getp/application/member/exception/NotSupportedProfileImageExtensionException.java b/get-p-application/src/main/java/es/princip/getp/application/member/exception/NotSupportedProfileImageExtensionException.java similarity index 100% rename from src/main/java/es/princip/getp/application/member/exception/NotSupportedProfileImageExtensionException.java rename to get-p-application/src/main/java/es/princip/getp/application/member/exception/NotSupportedProfileImageExtensionException.java diff --git a/src/main/java/es/princip/getp/application/member/port/in/EditMemberUseCase.java b/get-p-application/src/main/java/es/princip/getp/application/member/port/in/EditMemberUseCase.java similarity index 66% rename from src/main/java/es/princip/getp/application/member/port/in/EditMemberUseCase.java rename to get-p-application/src/main/java/es/princip/getp/application/member/port/in/EditMemberUseCase.java index 83c70cd0..c47944e0 100644 --- a/src/main/java/es/princip/getp/application/member/port/in/EditMemberUseCase.java +++ b/get-p-application/src/main/java/es/princip/getp/application/member/port/in/EditMemberUseCase.java @@ -1,6 +1,6 @@ package es.princip.getp.application.member.port.in; -import es.princip.getp.application.member.command.EditMemberCommand; +import es.princip.getp.application.member.dto.command.EditMemberCommand; public interface EditMemberUseCase { diff --git a/src/main/java/es/princip/getp/application/member/port/in/ProfileImageUseCase.java b/get-p-application/src/main/java/es/princip/getp/application/member/port/in/ProfileImageUseCase.java similarity index 66% rename from src/main/java/es/princip/getp/application/member/port/in/ProfileImageUseCase.java rename to get-p-application/src/main/java/es/princip/getp/application/member/port/in/ProfileImageUseCase.java index fb33c4a2..913adffb 100644 --- a/src/main/java/es/princip/getp/application/member/port/in/ProfileImageUseCase.java +++ b/get-p-application/src/main/java/es/princip/getp/application/member/port/in/ProfileImageUseCase.java @@ -1,6 +1,6 @@ package es.princip.getp.application.member.port.in; -import es.princip.getp.application.member.command.RegisterProfileImageCommand; +import es.princip.getp.application.member.dto.command.RegisterProfileImageCommand; public interface ProfileImageUseCase { diff --git a/src/main/java/es/princip/getp/application/member/port/out/CheckMemberPort.java b/get-p-application/src/main/java/es/princip/getp/application/member/port/out/CheckMemberPort.java similarity index 78% rename from src/main/java/es/princip/getp/application/member/port/out/CheckMemberPort.java rename to get-p-application/src/main/java/es/princip/getp/application/member/port/out/CheckMemberPort.java index 41c7a216..64a2cad8 100644 --- a/src/main/java/es/princip/getp/application/member/port/out/CheckMemberPort.java +++ b/get-p-application/src/main/java/es/princip/getp/application/member/port/out/CheckMemberPort.java @@ -4,5 +4,5 @@ public interface CheckMemberPort { - boolean existsByEmail(Email email); + boolean existsBy(Email email); } diff --git a/src/main/java/es/princip/getp/application/member/port/out/LoadMemberPort.java b/get-p-application/src/main/java/es/princip/getp/application/member/port/out/LoadMemberPort.java similarity index 71% rename from src/main/java/es/princip/getp/application/member/port/out/LoadMemberPort.java rename to get-p-application/src/main/java/es/princip/getp/application/member/port/out/LoadMemberPort.java index 4b9067fe..34cdcd1c 100644 --- a/src/main/java/es/princip/getp/application/member/port/out/LoadMemberPort.java +++ b/get-p-application/src/main/java/es/princip/getp/application/member/port/out/LoadMemberPort.java @@ -2,10 +2,10 @@ import es.princip.getp.domain.common.model.Email; import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; public interface LoadMemberPort { Member loadBy(Email email); - - Member loadBy(Long id); + Member loadBy(MemberId memberId); } diff --git a/src/main/java/es/princip/getp/application/member/port/out/SaveMemberPort.java b/get-p-application/src/main/java/es/princip/getp/application/member/port/out/SaveMemberPort.java similarity index 62% rename from src/main/java/es/princip/getp/application/member/port/out/SaveMemberPort.java rename to get-p-application/src/main/java/es/princip/getp/application/member/port/out/SaveMemberPort.java index e2c2e667..d651cd6f 100644 --- a/src/main/java/es/princip/getp/application/member/port/out/SaveMemberPort.java +++ b/get-p-application/src/main/java/es/princip/getp/application/member/port/out/SaveMemberPort.java @@ -1,8 +1,9 @@ package es.princip.getp.application.member.port.out; import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; public interface SaveMemberPort { - Long save(Member member); + MemberId save(Member member); } diff --git a/src/main/java/es/princip/getp/application/member/port/out/UpdateMemberPort.java b/get-p-application/src/main/java/es/princip/getp/application/member/port/out/UpdateMemberPort.java similarity index 100% rename from src/main/java/es/princip/getp/application/member/port/out/UpdateMemberPort.java rename to get-p-application/src/main/java/es/princip/getp/application/member/port/out/UpdateMemberPort.java diff --git a/src/main/java/es/princip/getp/application/member/service/MemberService.java b/get-p-application/src/main/java/es/princip/getp/application/member/service/MemberService.java similarity index 93% rename from src/main/java/es/princip/getp/application/member/service/MemberService.java rename to get-p-application/src/main/java/es/princip/getp/application/member/service/MemberService.java index af796f2a..af2da428 100644 --- a/src/main/java/es/princip/getp/application/member/service/MemberService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/member/service/MemberService.java @@ -1,6 +1,6 @@ package es.princip.getp.application.member.service; -import es.princip.getp.application.member.command.EditMemberCommand; +import es.princip.getp.application.member.dto.command.EditMemberCommand; import es.princip.getp.application.member.port.in.EditMemberUseCase; import es.princip.getp.application.member.port.out.LoadMemberPort; import es.princip.getp.application.member.port.out.UpdateMemberPort; diff --git a/src/main/java/es/princip/getp/application/member/service/ProfileImageService.java b/get-p-application/src/main/java/es/princip/getp/application/member/service/ProfileImageService.java similarity index 91% rename from src/main/java/es/princip/getp/application/member/service/ProfileImageService.java rename to get-p-application/src/main/java/es/princip/getp/application/member/service/ProfileImageService.java index f076198b..fa49119c 100644 --- a/src/main/java/es/princip/getp/application/member/service/ProfileImageService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/member/service/ProfileImageService.java @@ -1,6 +1,6 @@ package es.princip.getp.application.member.service; -import es.princip.getp.application.member.command.RegisterProfileImageCommand; +import es.princip.getp.application.member.dto.command.RegisterProfileImageCommand; import es.princip.getp.application.member.exception.NotSupportedProfileImageExtensionException; import es.princip.getp.application.member.port.in.ProfileImageUseCase; import es.princip.getp.application.member.port.out.LoadMemberPort; @@ -8,6 +8,7 @@ import es.princip.getp.application.storage.port.out.DeleteFilePort; import es.princip.getp.application.storage.port.out.StoreFilePort; import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.member.model.ProfileImage; import es.princip.getp.util.StringUtil; import lombok.RequiredArgsConstructor; @@ -44,7 +45,7 @@ class ProfileImageService implements ProfileImageUseCase { @Override @Transactional public String registerProfileImage(final RegisterProfileImageCommand command) { - final Long memberId = command.memberId(); + final MemberId memberId = command.memberId(); final Member member = loadMemberPort.loadBy(memberId); if (member.hasProfileImage()) { deleteProfileImage(member.getProfileImage()); @@ -66,7 +67,7 @@ private ProfileImage saveProfileImage(final Member member, final MultipartFile i } private Path getPathToSaveProfileImage(final Member member, final MultipartFile image) { - final String memberId = String.valueOf(member.getMemberId()); + final String memberId = String.valueOf(member.getId().getValue()); final String fileName = StringUtil.generateRandomFilename(image.getOriginalFilename()); return Paths.get(memberId).resolve(PROFILE_IMAGE_PREFIX).resolve(fileName); } diff --git a/src/main/java/es/princip/getp/application/people/command/EditPeopleCommand.java b/get-p-application/src/main/java/es/princip/getp/application/people/dto/command/EditPeopleCommand.java similarity index 56% rename from src/main/java/es/princip/getp/application/people/command/EditPeopleCommand.java rename to get-p-application/src/main/java/es/princip/getp/application/people/dto/command/EditPeopleCommand.java index a1417328..fe65d108 100644 --- a/src/main/java/es/princip/getp/application/people/command/EditPeopleCommand.java +++ b/get-p-application/src/main/java/es/princip/getp/application/people/dto/command/EditPeopleCommand.java @@ -1,15 +1,14 @@ -package es.princip.getp.application.people.command; +package es.princip.getp.application.people.dto.command; import es.princip.getp.domain.common.model.Email; import es.princip.getp.domain.common.model.PhoneNumber; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.member.model.Nickname; -import es.princip.getp.domain.people.model.PeopleType; public record EditPeopleCommand( - Long memberId, + MemberId memberId, Nickname nickname, Email email, - PhoneNumber phoneNumber, - PeopleType peopleType + PhoneNumber phoneNumber ) { } \ No newline at end of file diff --git a/src/main/java/es/princip/getp/application/people/command/EditPeopleProfileCommand.java b/get-p-application/src/main/java/es/princip/getp/application/people/dto/command/EditPeopleProfileCommand.java similarity index 77% rename from src/main/java/es/princip/getp/application/people/command/EditPeopleProfileCommand.java rename to get-p-application/src/main/java/es/princip/getp/application/people/dto/command/EditPeopleProfileCommand.java index dc4bfd06..36efafde 100644 --- a/src/main/java/es/princip/getp/application/people/command/EditPeopleProfileCommand.java +++ b/get-p-application/src/main/java/es/princip/getp/application/people/dto/command/EditPeopleProfileCommand.java @@ -1,14 +1,15 @@ -package es.princip.getp.application.people.command; +package es.princip.getp.application.people.dto.command; import es.princip.getp.domain.common.model.Hashtag; import es.princip.getp.domain.common.model.TechStack; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.people.model.Education; import es.princip.getp.domain.people.model.Portfolio; import java.util.List; public record EditPeopleProfileCommand( - Long memberId, + MemberId memberId, String introduction, String activityArea, Education education, diff --git a/get-p-application/src/main/java/es/princip/getp/application/people/dto/command/GetPeopleCommand.java b/get-p-application/src/main/java/es/princip/getp/application/people/dto/command/GetPeopleCommand.java new file mode 100644 index 00000000..0ee45a95 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/people/dto/command/GetPeopleCommand.java @@ -0,0 +1,11 @@ +package es.princip.getp.application.people.dto.command; + +import es.princip.getp.domain.member.model.Member; +import org.springframework.data.domain.Pageable; + +public record GetPeopleCommand( + Pageable pageable, + PeopleSearchFilter filter, + Member member +) { +} \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/people/dto/command/PeopleSearchFilter.java b/get-p-application/src/main/java/es/princip/getp/application/people/dto/command/PeopleSearchFilter.java new file mode 100644 index 00000000..2d4d43d4 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/people/dto/command/PeopleSearchFilter.java @@ -0,0 +1,15 @@ +package es.princip.getp.application.people.dto.command; + +import lombok.Getter; + +@Getter +public class PeopleSearchFilter { + + private final String keyword; + private final boolean liked; + + public PeopleSearchFilter(final String keyword, final String liked) { + this.keyword = keyword; + this.liked = Boolean.parseBoolean(liked); + } +} \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/people/dto/command/PeopleSearchOrder.java b/get-p-application/src/main/java/es/princip/getp/application/people/dto/command/PeopleSearchOrder.java new file mode 100644 index 00000000..3e48a36a --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/people/dto/command/PeopleSearchOrder.java @@ -0,0 +1,11 @@ +package es.princip.getp.application.people.dto.command; + +import es.princip.getp.util.StringUtil; + +public enum PeopleSearchOrder { + PEOPLE_ID, LIKES_COUNT, CREATED_AT; + + public static PeopleSearchOrder from(final String value) { + return PeopleSearchOrder.valueOf(StringUtil.camelToSnake(value).toUpperCase()); + } +} diff --git a/src/main/java/es/princip/getp/application/people/command/RegisterPeopleCommand.java b/get-p-application/src/main/java/es/princip/getp/application/people/dto/command/RegisterPeopleCommand.java similarity index 57% rename from src/main/java/es/princip/getp/application/people/command/RegisterPeopleCommand.java rename to get-p-application/src/main/java/es/princip/getp/application/people/dto/command/RegisterPeopleCommand.java index d4d63466..5fe70b6b 100644 --- a/src/main/java/es/princip/getp/application/people/command/RegisterPeopleCommand.java +++ b/get-p-application/src/main/java/es/princip/getp/application/people/dto/command/RegisterPeopleCommand.java @@ -1,15 +1,14 @@ -package es.princip.getp.application.people.command; +package es.princip.getp.application.people.dto.command; import es.princip.getp.domain.common.model.Email; import es.princip.getp.domain.common.model.PhoneNumber; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.member.model.Nickname; -import es.princip.getp.domain.people.model.PeopleType; public record RegisterPeopleCommand( - Long memberId, + MemberId memberId, Nickname nickname, Email email, - PhoneNumber phoneNumber, - PeopleType peopleType + PhoneNumber phoneNumber ) { } \ No newline at end of file diff --git a/src/main/java/es/princip/getp/application/people/command/RegisterPeopleProfileCommand.java b/get-p-application/src/main/java/es/princip/getp/application/people/dto/command/RegisterPeopleProfileCommand.java similarity index 77% rename from src/main/java/es/princip/getp/application/people/command/RegisterPeopleProfileCommand.java rename to get-p-application/src/main/java/es/princip/getp/application/people/dto/command/RegisterPeopleProfileCommand.java index 57309166..783d12d4 100644 --- a/src/main/java/es/princip/getp/application/people/command/RegisterPeopleProfileCommand.java +++ b/get-p-application/src/main/java/es/princip/getp/application/people/dto/command/RegisterPeopleProfileCommand.java @@ -1,14 +1,15 @@ -package es.princip.getp.application.people.command; +package es.princip.getp.application.people.dto.command; import es.princip.getp.domain.common.model.Hashtag; import es.princip.getp.domain.common.model.TechStack; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.people.model.Education; import es.princip.getp.domain.people.model.Portfolio; import java.util.List; public record RegisterPeopleProfileCommand( - Long memberId, + MemberId memberId, String introduction, String activityArea, Education education, diff --git a/get-p-application/src/main/java/es/princip/getp/application/people/dto/command/SearchTeammateCommand.java b/get-p-application/src/main/java/es/princip/getp/application/people/dto/command/SearchTeammateCommand.java new file mode 100644 index 00000000..f4c3ed7e --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/people/dto/command/SearchTeammateCommand.java @@ -0,0 +1,12 @@ +package es.princip.getp.application.people.dto.command; + +import es.princip.getp.application.support.Cursor; +import es.princip.getp.application.support.CursorPageable; +import es.princip.getp.domain.project.commission.model.ProjectId; + +public record SearchTeammateCommand( + ProjectId projectId, + CursorPageable pageable, + String nickname +) { +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/people/query/dto/people/CardPeopleResponse.java b/get-p-application/src/main/java/es/princip/getp/application/people/dto/response/people/CardPeopleResponse.java similarity index 52% rename from src/main/java/es/princip/getp/api/controller/people/query/dto/people/CardPeopleResponse.java rename to get-p-application/src/main/java/es/princip/getp/application/people/dto/response/people/CardPeopleResponse.java index f8ff8a7b..6f89de04 100644 --- a/src/main/java/es/princip/getp/api/controller/people/query/dto/people/CardPeopleResponse.java +++ b/get-p-application/src/main/java/es/princip/getp/application/people/dto/response/people/CardPeopleResponse.java @@ -1,13 +1,11 @@ -package es.princip.getp.api.controller.people.query.dto.people; +package es.princip.getp.application.people.dto.response.people; -import es.princip.getp.api.controller.people.query.dto.peopleProfile.CardPeopleProfileResponse; -import es.princip.getp.domain.people.model.PeopleType; +import es.princip.getp.application.people.dto.response.peopleProfile.CardPeopleProfileResponse; public record CardPeopleResponse( Long peopleId, String nickname, String profileImageUri, - PeopleType peopleType, long completedProjectsCount, long likesCount, CardPeopleProfileResponse profile diff --git a/src/main/java/es/princip/getp/api/controller/people/query/dto/people/MyPeopleResponse.java b/get-p-application/src/main/java/es/princip/getp/application/people/dto/response/people/MyPeopleResponse.java similarity index 66% rename from src/main/java/es/princip/getp/api/controller/people/query/dto/people/MyPeopleResponse.java rename to get-p-application/src/main/java/es/princip/getp/application/people/dto/response/people/MyPeopleResponse.java index b9d0ed55..e9fc4a27 100644 --- a/src/main/java/es/princip/getp/api/controller/people/query/dto/people/MyPeopleResponse.java +++ b/get-p-application/src/main/java/es/princip/getp/application/people/dto/response/people/MyPeopleResponse.java @@ -1,6 +1,4 @@ -package es.princip.getp.api.controller.people.query.dto.people; - -import es.princip.getp.domain.people.model.PeopleType; +package es.princip.getp.application.people.dto.response.people; import java.time.LocalDateTime; @@ -10,7 +8,6 @@ public record MyPeopleResponse( String nickname, String phoneNumber, String profileImageUri, - PeopleType peopleType, Integer completedProjectsCount, Integer likesCount, LocalDateTime createdAt, diff --git a/get-p-application/src/main/java/es/princip/getp/application/people/dto/response/people/PeopleDetailResponse.java b/get-p-application/src/main/java/es/princip/getp/application/people/dto/response/people/PeopleDetailResponse.java new file mode 100644 index 00000000..c5fee5a4 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/people/dto/response/people/PeopleDetailResponse.java @@ -0,0 +1,53 @@ +package es.princip.getp.application.people.dto.response.people; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.util.List; +import es.princip.getp.application.people.dto.response.peopleProfile.PeopleProfileDetailResponse; +import es.princip.getp.application.people.dto.response.peopleProfile.PortfolioResponse; +import es.princip.getp.domain.people.model.Education; +import lombok.Getter; + +@Getter +public class PeopleDetailResponse { + private final Long peopleId; + private final String nickname; + private final String profileImageUri; + private final long completedProjectsCount; + private final long likesCount; + @JsonInclude(NON_NULL) private final Boolean liked; + private PeopleProfileDetailResponse profile; + + public PeopleDetailResponse( + final Long peopleId, + final String nickname, + final String profileImageUri, + final long completedProjectsCount, + final long likesCount, + final Boolean liked, + final PeopleProfileDetailResponse profile + ) { + this.peopleId = peopleId; + this.nickname = nickname; + this.profileImageUri = profileImageUri; + this.completedProjectsCount = completedProjectsCount; + this.likesCount = likesCount; + this.liked = liked; + this.profile = profile; + } + + public PeopleDetailResponse mosaic( + final String introduction, + final String activityArea, + final Education education, + final List techStacks, + final List portfolios + ) { + if (profile != null) { + profile.mosaic(introduction, activityArea, education, techStacks, portfolios); + } + return this; + } +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/people/dto/response/people/RegisterPeopleResponse.java b/get-p-application/src/main/java/es/princip/getp/application/people/dto/response/people/RegisterPeopleResponse.java new file mode 100644 index 00000000..2ffab5cc --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/people/dto/response/people/RegisterPeopleResponse.java @@ -0,0 +1,4 @@ +package es.princip.getp.application.people.dto.response.people; + +public record RegisterPeopleResponse(Long peopleId) { +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/people/query/dto/peopleProfile/CardPeopleProfileResponse.java b/get-p-application/src/main/java/es/princip/getp/application/people/dto/response/peopleProfile/CardPeopleProfileResponse.java similarity index 58% rename from src/main/java/es/princip/getp/api/controller/people/query/dto/peopleProfile/CardPeopleProfileResponse.java rename to get-p-application/src/main/java/es/princip/getp/application/people/dto/response/peopleProfile/CardPeopleProfileResponse.java index f65c7843..8042d6e0 100644 --- a/src/main/java/es/princip/getp/api/controller/people/query/dto/peopleProfile/CardPeopleProfileResponse.java +++ b/get-p-application/src/main/java/es/princip/getp/application/people/dto/response/peopleProfile/CardPeopleProfileResponse.java @@ -1,19 +1,22 @@ -package es.princip.getp.api.controller.people.query.dto.peopleProfile; +package es.princip.getp.application.people.dto.response.peopleProfile; -import es.princip.getp.api.controller.common.dto.HashtagsResponse; +import es.princip.getp.domain.common.model.Hashtag; import es.princip.getp.domain.people.model.PeopleProfile; +import java.util.List; + public record CardPeopleProfileResponse( String introduction, String activityArea, - HashtagsResponse hashtags + List hashtags ) { - public static CardPeopleProfileResponse from(final PeopleProfile profile) { return new CardPeopleProfileResponse( profile.getIntroduction(), profile.getActivityArea(), - HashtagsResponse.from(profile.getHashtags()) + profile.getHashtags().stream() + .map(Hashtag::getValue) + .toList() ); } } diff --git a/get-p-application/src/main/java/es/princip/getp/application/people/dto/response/peopleProfile/PeopleProfileDetailResponse.java b/get-p-application/src/main/java/es/princip/getp/application/people/dto/response/peopleProfile/PeopleProfileDetailResponse.java new file mode 100644 index 00000000..864ed46f --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/people/dto/response/peopleProfile/PeopleProfileDetailResponse.java @@ -0,0 +1,47 @@ +package es.princip.getp.application.people.dto.response.peopleProfile; + +import java.util.List; + +import es.princip.getp.domain.people.model.Education; +import lombok.Getter; + +@Getter +public class PeopleProfileDetailResponse { + private String introduction; + private String activityArea; + private Education education; + private List techStacks; + private final List hashtags; + private List portfolios; + + public PeopleProfileDetailResponse( + final String introduction, + final String activityArea, + final Education education, + final List techStacks, + final List hashtags, + final List portfolios + ) { + this.introduction = introduction; + this.activityArea = activityArea; + this.education = education; + this.techStacks = techStacks; + this.hashtags = hashtags; + this.portfolios = portfolios; + } + + public PeopleProfileDetailResponse mosaic( + final String introduction, + final String activityArea, + final Education education, + final List techStacks, + final List portfolios + ) { + this.introduction = introduction; + this.activityArea = activityArea; + this.education = education; + this.techStacks = techStacks; + this.portfolios = portfolios; + return this; + } +} diff --git a/src/main/java/es/princip/getp/api/controller/people/query/dto/peopleProfile/PortfolioResponse.java b/get-p-application/src/main/java/es/princip/getp/application/people/dto/response/peopleProfile/PortfolioResponse.java similarity index 57% rename from src/main/java/es/princip/getp/api/controller/people/query/dto/peopleProfile/PortfolioResponse.java rename to get-p-application/src/main/java/es/princip/getp/application/people/dto/response/peopleProfile/PortfolioResponse.java index a308dd4f..368819d7 100644 --- a/src/main/java/es/princip/getp/api/controller/people/query/dto/peopleProfile/PortfolioResponse.java +++ b/get-p-application/src/main/java/es/princip/getp/application/people/dto/response/peopleProfile/PortfolioResponse.java @@ -1,4 +1,4 @@ -package es.princip.getp.api.controller.people.query.dto.peopleProfile; +package es.princip.getp.application.people.dto.response.peopleProfile; public record PortfolioResponse( String description, diff --git a/src/main/java/es/princip/getp/application/people/exception/AlreadyExistsPeopleException.java b/get-p-application/src/main/java/es/princip/getp/application/people/exception/AlreadyExistsPeopleException.java similarity index 100% rename from src/main/java/es/princip/getp/application/people/exception/AlreadyExistsPeopleException.java rename to get-p-application/src/main/java/es/princip/getp/application/people/exception/AlreadyExistsPeopleException.java diff --git a/get-p-application/src/main/java/es/princip/getp/application/people/exception/NotFoundPeopleException.java b/get-p-application/src/main/java/es/princip/getp/application/people/exception/NotFoundPeopleException.java new file mode 100644 index 00000000..61917343 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/people/exception/NotFoundPeopleException.java @@ -0,0 +1,28 @@ +package es.princip.getp.application.people.exception; + +import es.princip.getp.application.support.NotFoundException; +import es.princip.getp.domain.support.ErrorDescription; + +import java.util.Collection; +import java.util.stream.Collectors; + +public class NotFoundPeopleException extends NotFoundException { + + private static final String code = "NOT_FOUND_PEOPLE"; + private static final String message = "존재하지 않는 피플입니다."; + + public NotFoundPeopleException() { + super(ErrorDescription.of(code, message)); + } + + public NotFoundPeopleException(final String ids) { + super(ErrorDescription.of(code, ids + "(은)는" + message)); + } + + public static NotFoundPeopleException from(final Collection peopleIds) { + final String ids = peopleIds.stream() + .map(String::valueOf) + .collect(Collectors.joining(", ")); + return new NotFoundPeopleException(ids); + } +} diff --git a/src/main/java/es/princip/getp/application/people/mapper/PeopleDataMapper.java b/get-p-application/src/main/java/es/princip/getp/application/people/mapper/PeopleDataMapper.java similarity index 71% rename from src/main/java/es/princip/getp/application/people/mapper/PeopleDataMapper.java rename to get-p-application/src/main/java/es/princip/getp/application/people/mapper/PeopleDataMapper.java index acafc5f1..771f6b07 100644 --- a/src/main/java/es/princip/getp/application/people/mapper/PeopleDataMapper.java +++ b/get-p-application/src/main/java/es/princip/getp/application/people/mapper/PeopleDataMapper.java @@ -1,7 +1,7 @@ package es.princip.getp.application.people.mapper; -import es.princip.getp.application.people.command.EditPeopleProfileCommand; -import es.princip.getp.application.people.command.RegisterPeopleProfileCommand; +import es.princip.getp.application.people.dto.command.EditPeopleProfileCommand; +import es.princip.getp.application.people.dto.command.RegisterPeopleProfileCommand; import es.princip.getp.domain.people.model.PeopleProfileData; import org.mapstruct.Mapper; diff --git a/get-p-application/src/main/java/es/princip/getp/application/people/mapper/PeopleMapper.java b/get-p-application/src/main/java/es/princip/getp/application/people/mapper/PeopleMapper.java new file mode 100644 index 00000000..2708678a --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/people/mapper/PeopleMapper.java @@ -0,0 +1,18 @@ +package es.princip.getp.application.people.mapper; + +import es.princip.getp.domain.common.model.Email; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.model.People; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper(componentModel = "spring") +public interface PeopleMapper { + + @Mapping(source = "email", target = "info.email") + @Mapping(target = "id", ignore = true) + @Mapping(target = "profile", ignore = true) + @Mapping(target = "createdAt", ignore = true) + @Mapping(target = "updatedAt", ignore = true) + People mapToPeople(MemberId memberId, Email email); +} diff --git a/src/main/java/es/princip/getp/application/people/port/in/EditPeopleProfileUseCase.java b/get-p-application/src/main/java/es/princip/getp/application/people/port/in/EditPeopleProfileUseCase.java similarity index 65% rename from src/main/java/es/princip/getp/application/people/port/in/EditPeopleProfileUseCase.java rename to get-p-application/src/main/java/es/princip/getp/application/people/port/in/EditPeopleProfileUseCase.java index f9055539..a7da1f42 100644 --- a/src/main/java/es/princip/getp/application/people/port/in/EditPeopleProfileUseCase.java +++ b/get-p-application/src/main/java/es/princip/getp/application/people/port/in/EditPeopleProfileUseCase.java @@ -1,6 +1,6 @@ package es.princip.getp.application.people.port.in; -import es.princip.getp.application.people.command.EditPeopleProfileCommand; +import es.princip.getp.application.people.dto.command.EditPeopleProfileCommand; public interface EditPeopleProfileUseCase { diff --git a/src/main/java/es/princip/getp/application/people/port/in/EditPeopleUseCase.java b/get-p-application/src/main/java/es/princip/getp/application/people/port/in/EditPeopleUseCase.java similarity index 65% rename from src/main/java/es/princip/getp/application/people/port/in/EditPeopleUseCase.java rename to get-p-application/src/main/java/es/princip/getp/application/people/port/in/EditPeopleUseCase.java index 1016c75f..d08d87ea 100644 --- a/src/main/java/es/princip/getp/application/people/port/in/EditPeopleUseCase.java +++ b/get-p-application/src/main/java/es/princip/getp/application/people/port/in/EditPeopleUseCase.java @@ -1,6 +1,6 @@ package es.princip.getp.application.people.port.in; -import es.princip.getp.application.people.command.EditPeopleCommand; +import es.princip.getp.application.people.dto.command.EditPeopleCommand; public interface EditPeopleUseCase { diff --git a/get-p-application/src/main/java/es/princip/getp/application/people/port/in/GetMyPeopleQuery.java b/get-p-application/src/main/java/es/princip/getp/application/people/port/in/GetMyPeopleQuery.java new file mode 100644 index 00000000..da4d4131 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/people/port/in/GetMyPeopleQuery.java @@ -0,0 +1,12 @@ +package es.princip.getp.application.people.port.in; + +import es.princip.getp.application.people.dto.response.people.MyPeopleResponse; +import es.princip.getp.application.people.dto.response.peopleProfile.PeopleProfileDetailResponse; +import es.princip.getp.domain.member.model.MemberId; + +public interface GetMyPeopleQuery { + + MyPeopleResponse getBy(MemberId memberId); + + PeopleProfileDetailResponse getDetailProfileBy(MemberId memberId); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/people/port/in/GetPeopleQuery.java b/get-p-application/src/main/java/es/princip/getp/application/people/port/in/GetPeopleQuery.java new file mode 100644 index 00000000..0a447b5a --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/people/port/in/GetPeopleQuery.java @@ -0,0 +1,15 @@ +package es.princip.getp.application.people.port.in; + +import es.princip.getp.application.people.dto.response.people.CardPeopleResponse; +import es.princip.getp.application.people.dto.response.people.PeopleDetailResponse; +import es.princip.getp.application.people.dto.command.GetPeopleCommand; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.people.model.PeopleId; +import org.springframework.data.domain.Page; + +public interface GetPeopleQuery { + + Page getPagedCards(GetPeopleCommand command); + + PeopleDetailResponse getDetailBy(Member member, PeopleId peopleId); +} diff --git a/src/main/java/es/princip/getp/application/people/port/in/RegisterPeopleProfileUseCase.java b/get-p-application/src/main/java/es/princip/getp/application/people/port/in/RegisterPeopleProfileUseCase.java similarity index 65% rename from src/main/java/es/princip/getp/application/people/port/in/RegisterPeopleProfileUseCase.java rename to get-p-application/src/main/java/es/princip/getp/application/people/port/in/RegisterPeopleProfileUseCase.java index 86db1282..2ca49284 100644 --- a/src/main/java/es/princip/getp/application/people/port/in/RegisterPeopleProfileUseCase.java +++ b/get-p-application/src/main/java/es/princip/getp/application/people/port/in/RegisterPeopleProfileUseCase.java @@ -1,6 +1,6 @@ package es.princip.getp.application.people.port.in; -import es.princip.getp.application.people.command.RegisterPeopleProfileCommand; +import es.princip.getp.application.people.dto.command.RegisterPeopleProfileCommand; public interface RegisterPeopleProfileUseCase { diff --git a/get-p-application/src/main/java/es/princip/getp/application/people/port/in/RegisterPeopleUseCase.java b/get-p-application/src/main/java/es/princip/getp/application/people/port/in/RegisterPeopleUseCase.java new file mode 100644 index 00000000..c6541d6a --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/people/port/in/RegisterPeopleUseCase.java @@ -0,0 +1,9 @@ +package es.princip.getp.application.people.port.in; + +import es.princip.getp.application.people.dto.command.RegisterPeopleCommand; +import es.princip.getp.domain.people.model.PeopleId; + +public interface RegisterPeopleUseCase { + + PeopleId register(RegisterPeopleCommand command); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/people/port/out/CheckPeoplePort.java b/get-p-application/src/main/java/es/princip/getp/application/people/port/out/CheckPeoplePort.java new file mode 100644 index 00000000..a0b38673 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/people/port/out/CheckPeoplePort.java @@ -0,0 +1,8 @@ +package es.princip.getp.application.people.port.out; + +import es.princip.getp.domain.member.model.MemberId; + +public interface CheckPeoplePort { + + boolean existsBy(MemberId memberId); +} diff --git a/src/main/java/es/princip/getp/application/people/port/out/DeletePeoplePort.java b/get-p-application/src/main/java/es/princip/getp/application/people/port/out/DeletePeoplePort.java similarity index 50% rename from src/main/java/es/princip/getp/application/people/port/out/DeletePeoplePort.java rename to get-p-application/src/main/java/es/princip/getp/application/people/port/out/DeletePeoplePort.java index 39b8ba7c..53214dee 100644 --- a/src/main/java/es/princip/getp/application/people/port/out/DeletePeoplePort.java +++ b/get-p-application/src/main/java/es/princip/getp/application/people/port/out/DeletePeoplePort.java @@ -1,6 +1,8 @@ package es.princip.getp.application.people.port.out; +import es.princip.getp.domain.people.model.PeopleId; + public interface DeletePeoplePort { - void delete(Long peopleId); + void delete(PeopleId peopleId); } diff --git a/get-p-application/src/main/java/es/princip/getp/application/people/port/out/FindMyPeoplePort.java b/get-p-application/src/main/java/es/princip/getp/application/people/port/out/FindMyPeoplePort.java new file mode 100644 index 00000000..639c48c1 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/people/port/out/FindMyPeoplePort.java @@ -0,0 +1,12 @@ +package es.princip.getp.application.people.port.out; + +import es.princip.getp.application.people.dto.response.people.MyPeopleResponse; +import es.princip.getp.application.people.dto.response.peopleProfile.PeopleProfileDetailResponse; +import es.princip.getp.domain.member.model.MemberId; + +public interface FindMyPeoplePort { + + MyPeopleResponse findBy(MemberId memberId); + + PeopleProfileDetailResponse findDetailProfileBy(MemberId memberId); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/people/port/out/FindPeoplePort.java b/get-p-application/src/main/java/es/princip/getp/application/people/port/out/FindPeoplePort.java new file mode 100644 index 00000000..592c368b --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/people/port/out/FindPeoplePort.java @@ -0,0 +1,17 @@ +package es.princip.getp.application.people.port.out; + +import es.princip.getp.application.people.dto.response.people.CardPeopleResponse; +import es.princip.getp.application.people.dto.response.people.PeopleDetailResponse; +import es.princip.getp.application.people.dto.command.PeopleSearchFilter; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.model.PeopleId; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +public interface FindPeoplePort { + + Page findCardBy(Pageable pageable, PeopleSearchFilter filter, MemberId memberId); + + PeopleDetailResponse findDetailBy(Member member, PeopleId peopleId); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/people/port/out/LoadPeoplePort.java b/get-p-application/src/main/java/es/princip/getp/application/people/port/out/LoadPeoplePort.java new file mode 100644 index 00000000..7e28c478 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/people/port/out/LoadPeoplePort.java @@ -0,0 +1,14 @@ +package es.princip.getp.application.people.port.out; + +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.people.model.PeopleId; + +import java.util.Set; + +public interface LoadPeoplePort { + + People loadBy(MemberId memberId); + People loadBy(PeopleId peopleId); + Set loadBy(Set peopleIds); +} diff --git a/src/main/java/es/princip/getp/application/people/port/out/SavePeoplePort.java b/get-p-application/src/main/java/es/princip/getp/application/people/port/out/SavePeoplePort.java similarity index 62% rename from src/main/java/es/princip/getp/application/people/port/out/SavePeoplePort.java rename to get-p-application/src/main/java/es/princip/getp/application/people/port/out/SavePeoplePort.java index 6dba54fd..6a422bd8 100644 --- a/src/main/java/es/princip/getp/application/people/port/out/SavePeoplePort.java +++ b/get-p-application/src/main/java/es/princip/getp/application/people/port/out/SavePeoplePort.java @@ -1,8 +1,9 @@ package es.princip.getp.application.people.port.out; import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.people.model.PeopleId; public interface SavePeoplePort { - Long save(People people); + PeopleId save(People people); } diff --git a/src/main/java/es/princip/getp/application/people/port/out/UpdatePeoplePort.java b/get-p-application/src/main/java/es/princip/getp/application/people/port/out/UpdatePeoplePort.java similarity index 100% rename from src/main/java/es/princip/getp/application/people/port/out/UpdatePeoplePort.java rename to get-p-application/src/main/java/es/princip/getp/application/people/port/out/UpdatePeoplePort.java diff --git a/src/main/java/es/princip/getp/application/people/service/DeletePeopleService.java b/get-p-application/src/main/java/es/princip/getp/application/people/service/DeletePeopleService.java similarity index 87% rename from src/main/java/es/princip/getp/application/people/service/DeletePeopleService.java rename to get-p-application/src/main/java/es/princip/getp/application/people/service/DeletePeopleService.java index 1e3f15ea..ad5181dd 100644 --- a/src/main/java/es/princip/getp/application/people/service/DeletePeopleService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/people/service/DeletePeopleService.java @@ -2,6 +2,7 @@ import es.princip.getp.application.people.port.out.DeletePeoplePort; import es.princip.getp.application.people.port.out.LoadPeoplePort; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.people.model.People; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -16,7 +17,7 @@ public class DeletePeopleService { private final DeletePeoplePort deletePeoplePort; @Transactional - public void delete(final Long memberId) { + public void delete(final MemberId memberId) { final People people = loadPeoplePort.loadBy(memberId); deletePeoplePort.delete(people.getId()); } diff --git a/src/main/java/es/princip/getp/application/people/service/EditPeopleProfileService.java b/get-p-application/src/main/java/es/princip/getp/application/people/service/EditPeopleProfileService.java similarity index 81% rename from src/main/java/es/princip/getp/application/people/service/EditPeopleProfileService.java rename to get-p-application/src/main/java/es/princip/getp/application/people/service/EditPeopleProfileService.java index 10bb8eaf..8ae4860e 100644 --- a/src/main/java/es/princip/getp/application/people/service/EditPeopleProfileService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/people/service/EditPeopleProfileService.java @@ -1,10 +1,11 @@ package es.princip.getp.application.people.service; -import es.princip.getp.application.people.command.EditPeopleProfileCommand; +import es.princip.getp.application.people.dto.command.EditPeopleProfileCommand; import es.princip.getp.application.people.mapper.PeopleDataMapper; import es.princip.getp.application.people.port.in.EditPeopleProfileUseCase; import es.princip.getp.application.people.port.out.LoadPeoplePort; import es.princip.getp.application.people.port.out.UpdatePeoplePort; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.people.model.People; import es.princip.getp.domain.people.model.PeopleProfileData; import lombok.RequiredArgsConstructor; @@ -14,7 +15,7 @@ @Service @Transactional(readOnly = true) @RequiredArgsConstructor -public class EditPeopleProfileService implements EditPeopleProfileUseCase { +class EditPeopleProfileService implements EditPeopleProfileUseCase { private final LoadPeoplePort loadPeoplePort; private final UpdatePeoplePort updatePeoplePort; @@ -23,7 +24,7 @@ public class EditPeopleProfileService implements EditPeopleProfileUseCase { @Transactional public void edit(final EditPeopleProfileCommand command) { - final Long memberId = command.memberId(); + final MemberId memberId = command.memberId(); final People people = loadPeoplePort.loadBy(memberId); final PeopleProfileData data = peopleDataMapper.mapToData(command); people.editProfile(data); diff --git a/src/main/java/es/princip/getp/application/people/service/EditPeopleService.java b/get-p-application/src/main/java/es/princip/getp/application/people/service/EditPeopleService.java similarity index 79% rename from src/main/java/es/princip/getp/application/people/service/EditPeopleService.java rename to get-p-application/src/main/java/es/princip/getp/application/people/service/EditPeopleService.java index 4fd51510..e2a2b6f0 100644 --- a/src/main/java/es/princip/getp/application/people/service/EditPeopleService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/people/service/EditPeopleService.java @@ -1,8 +1,8 @@ package es.princip.getp.application.people.service; -import es.princip.getp.application.member.command.EditMemberCommand; +import es.princip.getp.application.member.dto.command.EditMemberCommand; import es.princip.getp.application.member.port.in.EditMemberUseCase; -import es.princip.getp.application.people.command.EditPeopleCommand; +import es.princip.getp.application.people.dto.command.EditPeopleCommand; import es.princip.getp.application.people.port.in.EditPeopleUseCase; import es.princip.getp.application.people.port.out.LoadPeoplePort; import es.princip.getp.application.people.port.out.UpdatePeoplePort; @@ -14,7 +14,7 @@ @Service @RequiredArgsConstructor @Transactional(readOnly = true) -public class EditPeopleService implements EditPeopleUseCase { +class EditPeopleService implements EditPeopleUseCase { private final EditMemberUseCase editMemberUseCase; @@ -25,7 +25,7 @@ public class EditPeopleService implements EditPeopleUseCase { public void edit(final EditPeopleCommand command) { editMemberUseCase.editMember(EditMemberCommand.from(command)); final People people = loadPeoplePort.loadBy(command.memberId()); - people.editInfo(command.email(), command.peopleType()); + people.editInfo(command.email()); updatePeoplePort.update(people); } } \ No newline at end of file diff --git a/src/main/java/es/princip/getp/application/people/service/GetMyPeopleService.java b/get-p-application/src/main/java/es/princip/getp/application/people/service/GetMyPeopleService.java similarity index 60% rename from src/main/java/es/princip/getp/application/people/service/GetMyPeopleService.java rename to get-p-application/src/main/java/es/princip/getp/application/people/service/GetMyPeopleService.java index 7f90c844..91f54a70 100644 --- a/src/main/java/es/princip/getp/application/people/service/GetMyPeopleService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/people/service/GetMyPeopleService.java @@ -1,9 +1,10 @@ package es.princip.getp.application.people.service; -import es.princip.getp.api.controller.people.query.dto.people.MyPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.peopleProfile.DetailPeopleProfileResponse; +import es.princip.getp.application.people.dto.response.people.MyPeopleResponse; +import es.princip.getp.application.people.dto.response.peopleProfile.PeopleProfileDetailResponse; import es.princip.getp.application.people.port.in.GetMyPeopleQuery; import es.princip.getp.application.people.port.out.FindMyPeoplePort; +import es.princip.getp.domain.member.model.MemberId; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -11,17 +12,17 @@ @Service @RequiredArgsConstructor @Transactional(readOnly = true) -public class GetMyPeopleService implements GetMyPeopleQuery { +class GetMyPeopleService implements GetMyPeopleQuery { private final FindMyPeoplePort findMyPeoplePort; @Override - public MyPeopleResponse getByMemberId(final Long memberId) { + public MyPeopleResponse getBy(final MemberId memberId) { return findMyPeoplePort.findBy(memberId); } @Override - public DetailPeopleProfileResponse getDetailProfileByMemberId(final Long memberId) { + public PeopleProfileDetailResponse getDetailProfileBy(final MemberId memberId) { return findMyPeoplePort.findDetailProfileBy(memberId); } } \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/people/service/GetPeopleService.java b/get-p-application/src/main/java/es/princip/getp/application/people/service/GetPeopleService.java new file mode 100644 index 00000000..fdaead33 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/people/service/GetPeopleService.java @@ -0,0 +1,76 @@ +package es.princip.getp.application.people.service; + +import es.princip.getp.application.people.dto.command.GetPeopleCommand; +import es.princip.getp.application.people.dto.command.PeopleSearchFilter; +import es.princip.getp.application.people.dto.response.people.CardPeopleResponse; +import es.princip.getp.application.people.dto.response.people.PeopleDetailResponse; +import es.princip.getp.application.people.port.in.GetPeopleQuery; +import es.princip.getp.application.people.port.out.FindPeoplePort; +import es.princip.getp.application.support.MosaicFactory; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.model.PeopleId; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; + +import static es.princip.getp.application.support.ApplicationQueryUtil.isNotLogined; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +class GetPeopleService implements GetPeopleQuery { + + private final FindPeoplePort findPeoplePort; + private final MosaicFactory mosaicFactory; + + private boolean doesFilterRequireLogin(final PeopleSearchFilter filter) { + return filter.isLiked(); + } + + private boolean isFilterAuthorized(final PeopleSearchFilter filter, final Member member) { + return (filter.isLiked() && member.isClient()); + } + + private boolean isMemberAuthenticated(final Member member) { + return member != null; + } + + private void validateFilter(final PeopleSearchFilter filter, final Member member) { + if (!doesFilterRequireLogin(filter)) { + return ; + } + if (!isMemberAuthenticated(member)) { + throw new AuthenticationCredentialsNotFoundException("해당 필터를 사용하려면 로그인이 필요합니다."); + } + if (!isFilterAuthorized(filter, member)) { + throw new AccessDeniedException("사용자가 사용할 수 없는 필터입니다."); + } + } + + @Override + public Page getPagedCards(final GetPeopleCommand command) { + final PeopleSearchFilter filter = command.filter(); + validateFilter(filter, command.member()); + final Pageable pageable = command.pageable(); + final MemberId memberId = Optional.ofNullable(command.member()) + .map(Member::getId) + .orElse(null); + return findPeoplePort.findCardBy(pageable, filter, memberId); + } + + @Override + public PeopleDetailResponse getDetailBy(final Member member, final PeopleId peopleId) { + final PeopleDetailResponse response = findPeoplePort.findDetailBy(member, peopleId); + if (isNotLogined(member)) { + return mosaicFactory.mosaic(response); + } + return response; + } +} \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/people/service/PeopleDetailResponseMosaicResolver.java b/get-p-application/src/main/java/es/princip/getp/application/people/service/PeopleDetailResponseMosaicResolver.java new file mode 100644 index 00000000..ea86b549 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/people/service/PeopleDetailResponseMosaicResolver.java @@ -0,0 +1,55 @@ +package es.princip.getp.application.people.service; + +import es.princip.getp.application.people.dto.response.people.PeopleDetailResponse; +import es.princip.getp.application.people.dto.response.peopleProfile.PeopleProfileDetailResponse; +import es.princip.getp.application.people.dto.response.peopleProfile.PortfolioResponse; +import es.princip.getp.application.support.MosaicResolver; +import es.princip.getp.application.support.MosaicResolverSupport; +import es.princip.getp.domain.people.model.Education; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.MessageSource; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +class PeopleDetailResponseMosaicResolver extends MosaicResolverSupport + implements MosaicResolver { + + @Autowired + protected PeopleDetailResponseMosaicResolver(MessageSource messageSource) { + super(messageSource); + } + + private Education mosaicEducation(final Education education) { + final String school = mosaicMessage(education.getSchool()); + final String major = mosaicMessage(education.getMajor()); + return new Education(school, major); + } + + private List mosaicPortfolioResponse(final List portfolios) { + return portfolios.stream() + .map(portfolio -> new PortfolioResponse( + mosaicMessage(portfolio.description()), + mosaicMessage(portfolio.url()) + )) + .toList(); + } + + @Override + public boolean supports(Class clazz) { + return PeopleDetailResponse.class.equals(clazz); + } + + @Override + public PeopleDetailResponse resolve(PeopleDetailResponse response) { + PeopleProfileDetailResponse mosaicResponse = response.getProfile(); + final String introduction = mosaicMessage(mosaicResponse.getIntroduction()); + final String activityArea = mosaicMessage(mosaicResponse.getActivityArea()); + final Education education = mosaicEducation(mosaicResponse.getEducation()); + final List techStacks = mosaicMessage(mosaicResponse.getHashtags()); + final List portfolios = mosaicPortfolioResponse(mosaicResponse.getPortfolios()); + + return response.mosaic(introduction, activityArea, education, techStacks, portfolios); + } +} diff --git a/src/main/java/es/princip/getp/application/people/service/RegisterPeopleProfileService.java b/get-p-application/src/main/java/es/princip/getp/application/people/service/RegisterPeopleProfileService.java similarity index 80% rename from src/main/java/es/princip/getp/application/people/service/RegisterPeopleProfileService.java rename to get-p-application/src/main/java/es/princip/getp/application/people/service/RegisterPeopleProfileService.java index 9ac4f5d9..8a1cca5b 100644 --- a/src/main/java/es/princip/getp/application/people/service/RegisterPeopleProfileService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/people/service/RegisterPeopleProfileService.java @@ -1,10 +1,11 @@ package es.princip.getp.application.people.service; -import es.princip.getp.application.people.command.RegisterPeopleProfileCommand; +import es.princip.getp.application.people.dto.command.RegisterPeopleProfileCommand; import es.princip.getp.application.people.mapper.PeopleDataMapper; import es.princip.getp.application.people.port.in.RegisterPeopleProfileUseCase; import es.princip.getp.application.people.port.out.LoadPeoplePort; import es.princip.getp.application.people.port.out.UpdatePeoplePort; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.people.model.People; import es.princip.getp.domain.people.model.PeopleProfileData; import lombok.RequiredArgsConstructor; @@ -14,7 +15,7 @@ @Service @RequiredArgsConstructor @Transactional(readOnly = true) -public class RegisterPeopleProfileService implements RegisterPeopleProfileUseCase { +class RegisterPeopleProfileService implements RegisterPeopleProfileUseCase { private final LoadPeoplePort loadPeoplePort; private final UpdatePeoplePort updatePeoplePort; @@ -23,7 +24,7 @@ public class RegisterPeopleProfileService implements RegisterPeopleProfileUseCas @Transactional public void register(final RegisterPeopleProfileCommand command) { - final Long memberId = command.memberId(); + final MemberId memberId = command.memberId(); final People people = loadPeoplePort.loadBy(memberId); final PeopleProfileData data = peopleDataMapper.mapToData(command); people.registerProfile(data); diff --git a/src/main/java/es/princip/getp/application/people/service/RegisterPeopleService.java b/get-p-application/src/main/java/es/princip/getp/application/people/service/RegisterPeopleService.java similarity index 81% rename from src/main/java/es/princip/getp/application/people/service/RegisterPeopleService.java rename to get-p-application/src/main/java/es/princip/getp/application/people/service/RegisterPeopleService.java index 6c6698f5..646a8c67 100644 --- a/src/main/java/es/princip/getp/application/people/service/RegisterPeopleService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/people/service/RegisterPeopleService.java @@ -1,14 +1,15 @@ package es.princip.getp.application.people.service; -import es.princip.getp.application.member.command.EditMemberCommand; +import es.princip.getp.application.member.dto.command.EditMemberCommand; import es.princip.getp.application.member.port.in.EditMemberUseCase; -import es.princip.getp.application.people.command.RegisterPeopleCommand; +import es.princip.getp.application.people.dto.command.RegisterPeopleCommand; import es.princip.getp.application.people.exception.AlreadyExistsPeopleException; import es.princip.getp.application.people.mapper.PeopleMapper; import es.princip.getp.application.people.port.in.RegisterPeopleUseCase; import es.princip.getp.application.people.port.out.CheckPeoplePort; import es.princip.getp.application.people.port.out.SavePeoplePort; import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.people.model.PeopleId; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -16,7 +17,7 @@ @Service @RequiredArgsConstructor @Transactional(readOnly = true) -public class RegisterPeopleService implements RegisterPeopleUseCase { +class RegisterPeopleService implements RegisterPeopleUseCase { private final PeopleMapper peopleMapper; @@ -33,12 +34,12 @@ public class RegisterPeopleService implements RegisterPeopleUseCase { * @throws AlreadyExistsPeopleException 이미 등록된 피플 정보가 존재하는 경우 */ @Transactional - public Long register(final RegisterPeopleCommand command) { + public PeopleId register(final RegisterPeopleCommand command) { if (checkPeoplePort.existsBy(command.memberId())) { throw new AlreadyExistsPeopleException(); } editMemberUseCase.editMember(EditMemberCommand.from(command)); - final People people = peopleMapper.mapToPeople(command.memberId(), command.email(), command.peopleType()); + final People people = peopleMapper.mapToPeople(command.memberId(), command.email()); return savePeoplePort.save(people); } } \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/ApplyProjectService.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/ApplyProjectService.java new file mode 100644 index 00000000..505ac70d --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/ApplyProjectService.java @@ -0,0 +1,85 @@ +package es.princip.getp.application.project.apply; + +import es.princip.getp.application.people.port.out.LoadPeoplePort; +import es.princip.getp.application.project.apply.dto.command.ApplyProjectAsTeamCommand; +import es.princip.getp.application.project.apply.dto.command.ApplyProjectCommand; +import es.princip.getp.application.project.apply.exception.AlreadyAppliedProjectException; +import es.princip.getp.application.project.apply.port.in.ApplyProjectUseCase; +import es.princip.getp.application.project.apply.port.out.CheckProjectApplicationPort; +import es.princip.getp.application.project.apply.port.out.SaveProjectApplicationPort; +import es.princip.getp.application.project.commission.port.out.LoadProjectPort; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.apply.model.ProjectApplication; +import es.princip.getp.domain.project.apply.model.ProjectApplicationData; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; +import es.princip.getp.domain.project.apply.service.IndividualProjectApplier; +import es.princip.getp.domain.project.apply.service.TeamProjectApplier; +import es.princip.getp.domain.project.commission.model.Project; +import es.princip.getp.domain.project.commission.model.ProjectId; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Set; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +class ApplyProjectService implements ApplyProjectUseCase { + + private final LoadPeoplePort loadPeoplePort; + private final LoadProjectPort loadProjectPort; + private final CheckProjectApplicationPort checkApplicationPort; + private final SaveProjectApplicationPort saveApplicationPort; + + private final ProjectApplicationDataMapper mapper; + + private final IndividualProjectApplier individualApplier; + private final TeamProjectApplier teamApplier; + + private final TeamApprovalRequestSender teamApprovalRequestSender; + private final TeamApprovalLinkGenerator teamApprovalLinkGenerator; + + @Override + @Transactional + public ProjectApplicationId apply(final ApplyProjectCommand command) { + final People applicant = loadPeoplePort.loadBy(command.getMember().getId()); + final Project project = loadProjectPort.loadBy(command.getProjectId()); + validateApplicantAlreadyApplied(applicant, project); + return applyInternal(applicant, project, command); + } + + private ProjectApplicationId applyInternal( + final People applicant, + final Project project, + final ApplyProjectCommand command + ) { + final ProjectApplicationData data = mapper.mapToData(command); + if (command instanceof ApplyProjectAsTeamCommand teamCommand) { + final Member applicantMember = teamCommand.getMember(); + final Set teammates = loadPeoplePort.loadBy(teamCommand.getTeammates()); + teammates.forEach(teammate -> validateApplicantAlreadyApplied(teammate, project)); + final ProjectApplication application = teamApplier.apply(applicant, project, data, teammates); + final ProjectApplicationId applicationId = saveApplicationPort.save(application); + teammates.forEach(teammate -> teamApprovalRequestSender.send( + applicantMember, + teammate, + project, + teamApprovalLinkGenerator.generate(applicationId, teammate.getId()) + )); + return applicationId; + } + final ProjectApplication application = individualApplier.apply(applicant, project, data); + return saveApplicationPort.save(application); + } + + private void validateApplicantAlreadyApplied(final People applicant, final Project project) { + final PeopleId applicantId = applicant.getId(); + final ProjectId projectId = project.getId(); + if (checkApplicationPort.existsBy(applicantId, projectId)) { + throw new AlreadyAppliedProjectException(); + } + } +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/ApproveTeammateService.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/ApproveTeammateService.java new file mode 100644 index 00000000..d9a367e3 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/ApproveTeammateService.java @@ -0,0 +1,42 @@ +package es.princip.getp.application.project.apply; + +import es.princip.getp.application.people.port.out.LoadPeoplePort; +import es.princip.getp.application.project.apply.dto.command.ApproveTeammateCommand; +import es.princip.getp.application.project.apply.port.in.ApproveTeammateUseCase; +import es.princip.getp.application.project.apply.port.out.LoadProjectApplicantPort; +import es.princip.getp.application.project.apply.port.out.UpdateProjectApplicantPort; +import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.apply.model.ProjectApplication; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; +import es.princip.getp.domain.project.apply.model.TeamProjectApplication; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +class ApproveTeammateService implements ApproveTeammateUseCase { + + private final LoadPeoplePort loadPeoplePort; + private final LoadProjectApplicantPort loadApplicationPort; + private final UpdateProjectApplicantPort updateApplicationPort; + + private final TeamApprovalTokenService tokenService; + + @Transactional + public void approve(final String token) { + final ApproveTeammateCommand command = tokenService.parse(token); + final ProjectApplicationId applicationId = command.applicationId(); + final PeopleId teammateId = command.teammateId(); + final ProjectApplication application = loadApplicationPort.loadBy(applicationId); + if (application instanceof TeamProjectApplication teamApplication) { + final People teammate = loadPeoplePort.loadBy(teammateId); + teamApplication.approve(teammate); + updateApplicationPort.update(teamApplication); + return ; + } + throw new IllegalArgumentException("팀 프로젝트 지원이 아닙니다."); + } +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/DevTeamApprovalLinkGenerator.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/DevTeamApprovalLinkGenerator.java new file mode 100644 index 00000000..7f923a4e --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/DevTeamApprovalLinkGenerator.java @@ -0,0 +1,30 @@ +package es.princip.getp.application.project.apply; + +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; + +@Service +@Profile("dev") +class DevTeamApprovalLinkGenerator implements TeamApprovalLinkGenerator { + + private final String contextPath; + private final TeamApprovalTokenService tokenService; + + @Autowired + public DevTeamApprovalLinkGenerator( + final TeamApprovalTokenService tokenService, + @Value("${server.servlet.context-path}") final String contextPath + ) { + this.tokenService = tokenService; + this.contextPath = contextPath; + } + + public String generate(final ProjectApplicationId applicationId, final PeopleId teammateId) { + final String token = tokenService.generate(applicationId, teammateId); + return String.format("https://api.principes.xyz%s/teammates/approve?token=%s", contextPath, token); + } +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/EmailTeamApprovalRequestSender.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/EmailTeamApprovalRequestSender.java new file mode 100644 index 00000000..f97ca5d6 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/EmailTeamApprovalRequestSender.java @@ -0,0 +1,63 @@ +package es.princip.getp.application.project.apply; + +import es.princip.getp.application.mail.command.SendMailCommand; +import es.princip.getp.application.mail.port.in.SendMailUseCase; +import es.princip.getp.application.project.apply.exception.FailedTeamApprovalRequestSendingException; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.project.commission.model.Project; +import jakarta.mail.MessagingException; +import lombok.RequiredArgsConstructor; +import org.springframework.mail.MailException; +import org.springframework.stereotype.Service; +import org.thymeleaf.TemplateEngine; +import org.thymeleaf.context.Context; + +@Service +@RequiredArgsConstructor +class EmailTeamApprovalRequestSender implements TeamApprovalRequestSender { + + private final TemplateEngine templateEngine; + private final SendMailUseCase sendMailUseCase; + + private String title(final Member requester, final Project project) { + return String.format( + """ + [GET-P] %s님이 %s 프로젝트를 함께 지원하고 싶어해요. + """, + requester.getNickname(), + project.getTitle() + ); + } + + private String text( + final Member requester, + final Project project, + final String approvalLink + ) { + final Context context = new Context(); + context.setVariable("requester", requester); + context.setVariable("project", project); + context.setVariable("approvalLink", approvalLink); + return templateEngine.process("teammate-approval", context); + } + + @Override + public void send( + final Member requester, + final People receiver, + final Project project, + final String approvalLink + ) { + final SendMailCommand message = SendMailCommand.of( + receiver.getInfo().getEmail(), + title(requester, project), + text(requester, project, approvalLink) + ); + try { + sendMailUseCase.send(message); + } catch (MailException | MessagingException exception) { + throw new FailedTeamApprovalRequestSendingException(); + } + } +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/GetApplicantService.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/GetApplicantService.java new file mode 100644 index 00000000..a1164464 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/GetApplicantService.java @@ -0,0 +1,48 @@ +package es.princip.getp.application.project.apply; + +import es.princip.getp.application.client.port.out.LoadClientPort; +import es.princip.getp.application.project.apply.dto.response.ProjectApplicantResponse; +import es.princip.getp.application.project.apply.exception.NotMyProjectException; +import es.princip.getp.application.project.apply.port.in.GetApplicantQuery; +import es.princip.getp.application.project.apply.port.out.FindProjectApplicantPort; +import es.princip.getp.application.project.apply.port.out.SerializeApplicantCursorPort; +import es.princip.getp.application.project.commission.port.out.LoadProjectPort; +import es.princip.getp.application.support.Cursor; +import es.princip.getp.application.support.CursorPageable; +import es.princip.getp.application.support.dto.SliceResponse; +import es.princip.getp.domain.client.model.Client; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.project.commission.model.Project; +import es.princip.getp.domain.project.commission.model.ProjectId; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Slice; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +class GetApplicantService implements GetApplicantQuery { + + private final LoadClientPort loadClientPort; + private final LoadProjectPort loadProjectPort; + + private final FindProjectApplicantPort findApplicantPort; + private final SerializeApplicantCursorPort serializeApplicantCursorPort; + + @Override + public SliceResponse getApplicantsBy( + final CursorPageable pageable, + final Member member, + final ProjectId projectId + ) { + final Client client = loadClientPort.loadBy(member.getId()); + final Project project = loadProjectPort.loadBy(projectId); + if (!project.isClient(client)) { + throw new NotMyProjectException(); + } + final Slice response = findApplicantPort.findApplicantsBy(pageable, projectId); + final String cursor = serializeApplicantCursorPort.serializeCursor(response); + return SliceResponse.of(response, cursor); + } +} \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/GetApplicationDetailService.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/GetApplicationDetailService.java new file mode 100644 index 00000000..47a716c6 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/GetApplicationDetailService.java @@ -0,0 +1,42 @@ +package es.princip.getp.application.project.apply; + +import es.princip.getp.application.people.port.out.LoadPeoplePort; +import es.princip.getp.application.project.apply.dto.response.ProjectApplicationDetailResponse; +import es.princip.getp.application.project.apply.exception.NotMyProjectApplicationException; +import es.princip.getp.application.project.apply.port.in.GetApplicationDetailQuery; +import es.princip.getp.application.project.apply.port.out.LoadProjectApplicantPort; +import es.princip.getp.application.project.commission.dto.response.ProjectDetailResponse; +import es.princip.getp.application.project.commission.port.out.FindProjectPort; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.project.apply.model.ProjectApplication; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +class GetApplicationDetailService implements GetApplicationDetailQuery { + + private final FindProjectPort findProjectPort; + private final LoadPeoplePort loadPeoplePort; + private final LoadProjectApplicantPort loadApplicationPort; + + private final ProjectApplicationDetailResponseFactory responseFactory; + + @Override + public ProjectApplicationDetailResponse getApplicationDetailBy( + final Member member, + final ProjectApplicationId applicationId + ) { + final People applicant = loadPeoplePort.loadBy(member.getId()); + final ProjectApplication application = loadApplicationPort.loadBy(applicationId); + if (!application.isApplicant(applicant.getId())) { + throw new NotMyProjectApplicationException(); + } + final ProjectDetailResponse project = findProjectPort.findBy(member, application.getProjectId()); + return responseFactory.mapToResponse(application, project); + } +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/GetApplicationFormService.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/GetApplicationFormService.java new file mode 100644 index 00000000..f899005d --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/GetApplicationFormService.java @@ -0,0 +1,46 @@ +package es.princip.getp.application.project.apply; + +import es.princip.getp.application.client.port.out.LoadClientPort; +import es.princip.getp.application.project.commission.dto.response.ProjectDetailResponse; +import es.princip.getp.application.project.apply.exception.NotMyProjectException; +import es.princip.getp.application.project.apply.port.in.GetApplicationFormQuery; +import es.princip.getp.application.project.apply.port.out.LoadProjectApplicantPort; +import es.princip.getp.application.project.commission.port.out.FindProjectPort; +import es.princip.getp.application.project.commission.port.out.LoadProjectPort; +import es.princip.getp.domain.client.model.Client; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.project.apply.model.ProjectApplication; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; +import es.princip.getp.domain.project.commission.model.Project; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +class GetApplicationFormService implements GetApplicationFormQuery { + + private final FindProjectPort findProjectPort; + + private final LoadProjectPort loadProjectPort; + private final LoadClientPort loadClientPort; + private final LoadProjectApplicantPort loadApplicationPort; + + private final ProjectApplicationFormResponseFactory responseFactory; + + @Override + public es.princip.getp.application.project.apply.dto.response.ProjectApplicationFormResponse getApplicationFormBy( + final Member member, + final ProjectApplicationId applicationId + ) { + final Client client = loadClientPort.loadBy(member.getId()); + final ProjectApplication application = loadApplicationPort.loadBy(applicationId); + final Project project = loadProjectPort.loadBy(application.getProjectId()); + if (!project.isClient(client)) { + throw new NotMyProjectException(); + } + final ProjectDetailResponse projectDetailResponse = findProjectPort.findBy(member, application.getProjectId()); + return responseFactory.mapToResponse(application, projectDetailResponse); + } +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/LocalTeamApprovalLinkGenerator.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/LocalTeamApprovalLinkGenerator.java new file mode 100644 index 00000000..9f4b743b --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/LocalTeamApprovalLinkGenerator.java @@ -0,0 +1,33 @@ +package es.princip.getp.application.project.apply; + +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; + +@Service +@Profile("local") +class LocalTeamApprovalLinkGenerator implements TeamApprovalLinkGenerator { + + private final String port; + private final String contextPath; + private final TeamApprovalTokenService tokenService; + + @Autowired + public LocalTeamApprovalLinkGenerator( + final TeamApprovalTokenService tokenService, + @Value("${server.port}") final String port, + @Value("${server.servlet.context-path}") final String contextPath + ) { + this.tokenService = tokenService; + this.port = port; + this.contextPath = contextPath; + } + + public String generate(final ProjectApplicationId applicationId, final PeopleId teammateId) { + final String token = tokenService.generate(applicationId, teammateId); + return String.format("http://localhost:%s%s/teammates/approve?token=%s", port, contextPath, token); + } +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/ProjectApplicationDataMapper.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/ProjectApplicationDataMapper.java new file mode 100644 index 00000000..9a5f5a9a --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/ProjectApplicationDataMapper.java @@ -0,0 +1,11 @@ +package es.princip.getp.application.project.apply; + +import es.princip.getp.application.project.apply.dto.command.ApplyProjectCommand; +import es.princip.getp.domain.project.apply.model.ProjectApplicationData; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +interface ProjectApplicationDataMapper { + + ProjectApplicationData mapToData(ApplyProjectCommand command); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/ProjectApplicationDetailResponseFactory.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/ProjectApplicationDetailResponseFactory.java new file mode 100644 index 00000000..b475eef0 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/ProjectApplicationDetailResponseFactory.java @@ -0,0 +1,75 @@ +package es.princip.getp.application.project.apply; + +import es.princip.getp.application.project.commission.dto.response.ProjectDetailResponse; +import es.princip.getp.application.project.apply.dto.response.ProjectApplicationDetailResponse; +import es.princip.getp.domain.common.model.AttachmentFile; +import es.princip.getp.domain.common.model.URL; +import es.princip.getp.domain.project.apply.model.IndividualProjectApplication; +import es.princip.getp.domain.project.apply.model.ProjectApplication; +import es.princip.getp.domain.project.apply.model.TeamProjectApplication; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; + +import static es.princip.getp.domain.project.apply.model.ProjectApplicationType.INDIVIDUAL; +import static es.princip.getp.domain.project.apply.model.ProjectApplicationType.TEAM; + +@Component +@RequiredArgsConstructor +class ProjectApplicationDetailResponseFactory { + + private final ProjectApplicationDetailTeammateResponseFactory teammateResponseFactory; + + ProjectApplicationDetailResponse mapToResponse( + final ProjectApplication application, + final ProjectDetailResponse project + ) { + if (application instanceof TeamProjectApplication teamApplication) { + return mapToResponse(teamApplication, project); + } + if (application instanceof IndividualProjectApplication individualApplication) { + return mapToResponse(individualApplication, project); + } + throw new IllegalArgumentException("지원서 타입이 올바르지 않습니다."); + } + + private List mapToStringList(final List attachmentFiles) { + return attachmentFiles.stream() + .map(AttachmentFile::getUrl) + .map(URL::getValue) + .toList(); + } + + private ProjectApplicationDetailResponse mapToResponse( + final TeamProjectApplication application, + final ProjectDetailResponse project + ) { + return new ProjectApplicationDetailResponse( + application.getId().getValue(), + TEAM, + project, + application.getExpectedDuration(), + application.getStatus(), + application.getDescription(), + mapToStringList(application.getAttachmentFiles()), + teammateResponseFactory.mapToResponse(application) + ); + } + + private ProjectApplicationDetailResponse mapToResponse( + final IndividualProjectApplication application, + final ProjectDetailResponse project + ) { + return new ProjectApplicationDetailResponse( + application.getId().getValue(), + INDIVIDUAL, + project, + application.getExpectedDuration(), + application.getStatus(), + application.getDescription(), + mapToStringList(application.getAttachmentFiles()), + null + ); + } +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/ProjectApplicationDetailTeammateResponseFactory.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/ProjectApplicationDetailTeammateResponseFactory.java new file mode 100644 index 00000000..b8dc5bdf --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/ProjectApplicationDetailTeammateResponseFactory.java @@ -0,0 +1,39 @@ +package es.princip.getp.application.project.apply; + +import es.princip.getp.application.member.port.out.LoadMemberPort; +import es.princip.getp.application.people.port.out.LoadPeoplePort; +import es.princip.getp.application.project.apply.dto.response.ProjectApplicationDetailTeammateResponse; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.apply.model.TeamProjectApplication; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +@RequiredArgsConstructor +class ProjectApplicationDetailTeammateResponseFactory { + + private final LoadPeoplePort loadPeoplePort; + private final LoadMemberPort loadMemberPort; + + List mapToResponse( + final TeamProjectApplication application + ) { + return application.getTeammates().stream() + .map(teammate -> { + final PeopleId peopleId = teammate.getPeopleId(); + final People people = loadPeoplePort.loadBy(peopleId); + final Member member = loadMemberPort.loadBy(people.getMemberId()); + return new ProjectApplicationDetailTeammateResponse( + teammate.getId().getValue(), + member.getNickname().getValue(), + teammate.getStatus(), + member.getProfileImage().getUrl() + ); + }) + .toList(); + } +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/ProjectApplicationFormResponseFactory.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/ProjectApplicationFormResponseFactory.java new file mode 100644 index 00000000..1f0a162e --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/ProjectApplicationFormResponseFactory.java @@ -0,0 +1,110 @@ +package es.princip.getp.application.project.apply; + +import es.princip.getp.application.member.port.out.LoadMemberPort; +import es.princip.getp.application.people.dto.response.peopleProfile.PortfolioResponse; +import es.princip.getp.application.people.port.out.LoadPeoplePort; +import es.princip.getp.application.project.apply.dto.response.ProjectApplicationFormResponse; +import es.princip.getp.application.project.commission.dto.response.ProjectDetailResponse; +import es.princip.getp.domain.common.model.AttachmentFile; +import es.princip.getp.domain.common.model.TechStack; +import es.princip.getp.domain.common.model.URL; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.project.apply.model.IndividualProjectApplication; +import es.princip.getp.domain.project.apply.model.ProjectApplication; +import es.princip.getp.domain.project.apply.model.TeamProjectApplication; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import static es.princip.getp.domain.project.apply.model.ProjectApplicationType.INDIVIDUAL; +import static es.princip.getp.domain.project.apply.model.ProjectApplicationType.TEAM; + +@Component +@RequiredArgsConstructor +class ProjectApplicationFormResponseFactory { + + private final LoadPeoplePort loadPeoplePort; + private final LoadMemberPort loadMemberPort; + private final ProjectApplicationDetailTeammateResponseFactory teammateResponseFactory; + + ProjectApplicationFormResponse mapToResponse( + final ProjectApplication application, + final ProjectDetailResponse project + ) { + if (application instanceof TeamProjectApplication teamApplication) { + return mapToResponse(teamApplication, project); + } + if (application instanceof IndividualProjectApplication individualApplication) { + return mapToResponse(individualApplication, project); + } + throw new IllegalArgumentException("지원서 타입이 올바르지 않습니다."); + } + + private ProjectApplicationFormResponse mapToResponse( + final TeamProjectApplication application, + final ProjectDetailResponse project + ) { + final People applicant = loadPeoplePort.loadBy(application.getApplicantId()); + final Member member = loadMemberPort.loadBy(applicant.getMemberId()); + return new ProjectApplicationFormResponse( + application.getId().getValue(), + TEAM, + project, + member.getNickname().getValue(), + applicant.getProfile().getTechStacks().stream() + .map(TechStack::getValue) + .toList(), + applicant.getProfile().getActivityArea(), + applicant.getProfile().getEducation(), + applicant.getProfile().getIntroduction(), + applicant.getProfile().getPortfolios().stream() + .map(portfolio -> new PortfolioResponse( + portfolio.getDescription(), + portfolio.getUrl().getValue() + )) + .toList(), + application.getExpectedDuration(), + application.getStatus(), + application.getDescription(), + application.getAttachmentFiles().stream() + .map(AttachmentFile::getUrl) + .map(URL::getValue) + .toList(), + teammateResponseFactory.mapToResponse(application) + ); + } + + private ProjectApplicationFormResponse mapToResponse( + final IndividualProjectApplication application, + final ProjectDetailResponse project + ) { + final People applicant = loadPeoplePort.loadBy(application.getApplicantId()); + final Member member = loadMemberPort.loadBy(applicant.getMemberId()); + return new ProjectApplicationFormResponse( + application.getId().getValue(), + INDIVIDUAL, + project, + member.getNickname().getValue(), + applicant.getProfile().getTechStacks().stream() + .map(TechStack::getValue) + .toList(), + applicant.getProfile().getActivityArea(), + applicant.getProfile().getEducation(), + applicant.getProfile().getIntroduction(), + applicant.getProfile().getPortfolios().stream() + .map(portfolio -> new PortfolioResponse( + portfolio.getDescription(), + portfolio.getUrl().getValue() + )) + .toList(), + application.getExpectedDuration(), + application.getStatus(), + application.getDescription(), + application.getAttachmentFiles().stream() + .map(AttachmentFile::getUrl) + .map(URL::getValue) + .toList(), + null + ); + } +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/SearchTeammateService.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/SearchTeammateService.java new file mode 100644 index 00000000..280accb5 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/SearchTeammateService.java @@ -0,0 +1,34 @@ +package es.princip.getp.application.project.apply; + +import es.princip.getp.application.people.dto.command.SearchTeammateCommand; +import es.princip.getp.application.project.apply.dto.response.SearchTeammateResponse; +import es.princip.getp.application.project.apply.port.in.SearchTeammateQuery; +import es.princip.getp.application.project.apply.port.out.FindTeammatePort; +import es.princip.getp.application.project.apply.port.out.SerializeTeammateCursorPort; +import es.princip.getp.application.support.Cursor; +import es.princip.getp.application.support.CursorPageable; +import es.princip.getp.application.support.dto.SliceResponse; +import es.princip.getp.domain.project.commission.model.ProjectId; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Slice; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +class SearchTeammateService implements SearchTeammateQuery { + + private final FindTeammatePort findTeammatePort; + private final SerializeTeammateCursorPort serializeTeammateCursorPort; + + @Override + public SliceResponse search(final SearchTeammateCommand command) { + final ProjectId projectId = command.projectId(); + final CursorPageable pageable = command.pageable(); + final String nickname = command.nickname(); + final Slice response = findTeammatePort.findBy(projectId, pageable, nickname); + final String cursor = serializeTeammateCursorPort.serializeCursor(response); + return SliceResponse.of(response, cursor); + } +} \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/TeamApprovalLinkGenerator.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/TeamApprovalLinkGenerator.java new file mode 100644 index 00000000..4044defb --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/TeamApprovalLinkGenerator.java @@ -0,0 +1,9 @@ +package es.princip.getp.application.project.apply; + +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; + +interface TeamApprovalLinkGenerator { + + String generate(ProjectApplicationId applicationId, PeopleId teammateId); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/TeamApprovalRequestSender.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/TeamApprovalRequestSender.java new file mode 100644 index 00000000..c26bd94d --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/TeamApprovalRequestSender.java @@ -0,0 +1,10 @@ +package es.princip.getp.application.project.apply; + +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.project.commission.model.Project; + +interface TeamApprovalRequestSender { + + void send(Member requester, People receiver, Project project, String approvalLink); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/TeamApprovalTokenService.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/TeamApprovalTokenService.java new file mode 100644 index 00000000..9b7a4a4b --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/TeamApprovalTokenService.java @@ -0,0 +1,73 @@ +package es.princip.getp.application.project.apply; + +import es.princip.getp.application.auth.exception.ExpiredTokenException; +import es.princip.getp.application.auth.exception.InvalidTokenException; +import es.princip.getp.application.project.apply.dto.command.ApproveTeammateCommand; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; +import io.jsonwebtoken.*; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.security.Key; +import java.util.Date; + +@Service +@RequiredArgsConstructor +class TeamApprovalTokenService { + + private static final String TOKEN_TYPE = "팀원 승인"; + private static final String APPLICATION_ID_KEY = "applicationId"; + private static final String TEAMMATE_ID_KEY = "teammateId"; + + private final Long expireTime; + private final Key key; + + @Autowired + public TeamApprovalTokenService( + @Value("${spring.jwt.teammate-approval-token.expire-time}") final Long expireTime, + @Value("${spring.jwt.secret}") final String secretKey + ) { + this.expireTime = expireTime; + this.key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretKey)); + } + + public String generate(final ProjectApplicationId applicationId, final PeopleId teammateId) { + final long now = System.currentTimeMillis(); + final Date expireTime = new Date(now + this.expireTime); + + return Jwts.builder() + .claim(APPLICATION_ID_KEY, applicationId.getValue()) + .claim(TEAMMATE_ID_KEY, teammateId.getValue()) + .setExpiration(expireTime) + .signWith(key, SignatureAlgorithm.HS256) + .compact(); + } + + private Claims parseClaims(final String token) { + try { + return Jwts.parserBuilder() + .setSigningKey(key) + .build() + .parseClaimsJws(token) + .getBody(); + } catch (final ExpiredJwtException exception) { + throw new ExpiredTokenException(TOKEN_TYPE); + } + } + + public ApproveTeammateCommand parse(final String token) { + try { + final Claims claims = parseClaims(token); + final Long applicationId = claims.get(APPLICATION_ID_KEY, Long.class); + final Long teammateId = claims.get(TEAMMATE_ID_KEY, Long.class); + return new ApproveTeammateCommand(new ProjectApplicationId(applicationId), new PeopleId(teammateId)); + } catch (final JwtException | IllegalArgumentException exception) { + throw new InvalidTokenException(TOKEN_TYPE); + } + } +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/command/ApplyProjectAsIndividualCommand.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/command/ApplyProjectAsIndividualCommand.java new file mode 100644 index 00000000..87b4a669 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/command/ApplyProjectAsIndividualCommand.java @@ -0,0 +1,21 @@ +package es.princip.getp.application.project.apply.dto.command; + +import es.princip.getp.domain.common.model.AttachmentFile; +import es.princip.getp.domain.common.model.Duration; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.project.commission.model.ProjectId; + +import java.util.List; + +public class ApplyProjectAsIndividualCommand extends ApplyProjectCommand { + + public ApplyProjectAsIndividualCommand( + final Member member, + final ProjectId projectId, + final Duration expectedDuration, + final String description, + final List attachmentFiles + ) { + super(member, projectId, expectedDuration, description, attachmentFiles); + } +} \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/command/ApplyProjectAsTeamCommand.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/command/ApplyProjectAsTeamCommand.java new file mode 100644 index 00000000..ef298e05 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/command/ApplyProjectAsTeamCommand.java @@ -0,0 +1,29 @@ +package es.princip.getp.application.project.apply.dto.command; + +import es.princip.getp.domain.common.model.AttachmentFile; +import es.princip.getp.domain.common.model.Duration; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.commission.model.ProjectId; +import lombok.Getter; + +import java.util.List; +import java.util.Set; + +@Getter +public class ApplyProjectAsTeamCommand extends ApplyProjectCommand { + + private final Set teammates; + + public ApplyProjectAsTeamCommand( + final Member member, + final ProjectId projectId, + final Duration expectedDuration, + final String description, + final List attachmentFiles, + final Set teammates + ) { + super(member, projectId, expectedDuration, description, attachmentFiles); + this.teammates = teammates; + } +} \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/command/ApplyProjectCommand.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/command/ApplyProjectCommand.java new file mode 100644 index 00000000..c7c92ecc --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/command/ApplyProjectCommand.java @@ -0,0 +1,21 @@ +package es.princip.getp.application.project.apply.dto.command; + +import es.princip.getp.domain.common.model.AttachmentFile; +import es.princip.getp.domain.common.model.Duration; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.project.commission.model.ProjectId; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +@Getter +@RequiredArgsConstructor +public abstract class ApplyProjectCommand { + + private final Member member; + private final ProjectId projectId; + private final Duration expectedDuration; + private final String description; + private final List attachmentFiles; +} \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/command/ApproveTeammateCommand.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/command/ApproveTeammateCommand.java new file mode 100644 index 00000000..169d1898 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/command/ApproveTeammateCommand.java @@ -0,0 +1,10 @@ +package es.princip.getp.application.project.apply.dto.command; + +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; + +public record ApproveTeammateCommand( + ProjectApplicationId applicationId, + PeopleId teammateId +) { +} \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/response/ApplyProjectResponse.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/response/ApplyProjectResponse.java new file mode 100644 index 00000000..b7422d2e --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/response/ApplyProjectResponse.java @@ -0,0 +1,6 @@ +package es.princip.getp.application.project.apply.dto.response; + +public record ApplyProjectResponse( + Long applicationId +) { +} \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/response/ProjectApplicantResponse.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/response/ProjectApplicantResponse.java new file mode 100644 index 00000000..a3b7123d --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/response/ProjectApplicantResponse.java @@ -0,0 +1,19 @@ +package es.princip.getp.application.project.apply.dto.response; + +import com.fasterxml.jackson.annotation.JsonInclude; +import es.princip.getp.domain.people.model.Education; +import es.princip.getp.domain.project.apply.model.ProjectApplicationStatus; + +import java.util.List; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; + +public record ProjectApplicantResponse( + Long peopleId, + String nickname, + String profileImageUrl, + Education education, + ProjectApplicationStatus status, + @JsonInclude(NON_NULL) List teammates +) { +} \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/response/ProjectApplicantTeammateResponse.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/response/ProjectApplicantTeammateResponse.java new file mode 100644 index 00000000..1645c29c --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/response/ProjectApplicantTeammateResponse.java @@ -0,0 +1,8 @@ +package es.princip.getp.application.project.apply.dto.response; + +public record ProjectApplicantTeammateResponse( + Long peopleId, + String nickname, + String profileImageUrl +) { +} \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/response/ProjectApplicationDetailResponse.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/response/ProjectApplicationDetailResponse.java new file mode 100644 index 00000000..1250dfa9 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/response/ProjectApplicationDetailResponse.java @@ -0,0 +1,23 @@ +package es.princip.getp.application.project.apply.dto.response; + +import com.fasterxml.jackson.annotation.JsonInclude; +import es.princip.getp.application.project.commission.dto.response.ProjectDetailResponse; +import es.princip.getp.domain.common.model.Duration; +import es.princip.getp.domain.project.apply.model.ProjectApplicationStatus; +import es.princip.getp.domain.project.apply.model.ProjectApplicationType; + +import java.util.List; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; + +public record ProjectApplicationDetailResponse( + Long applicationId, + ProjectApplicationType type, + ProjectDetailResponse project, + Duration expectedDuration, + ProjectApplicationStatus status, + String description, + List attachmentFiles, + @JsonInclude(NON_NULL) List teammates +) { +} \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/response/ProjectApplicationDetailTeammateResponse.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/response/ProjectApplicationDetailTeammateResponse.java new file mode 100644 index 00000000..553aa7c5 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/response/ProjectApplicationDetailTeammateResponse.java @@ -0,0 +1,11 @@ +package es.princip.getp.application.project.apply.dto.response; + +import es.princip.getp.domain.project.apply.model.TeammateStatus; + +public record ProjectApplicationDetailTeammateResponse( + Long peopleId, + String nickname, + TeammateStatus status, + String profileImageUrl +) { +} \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/response/ProjectApplicationFormResponse.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/response/ProjectApplicationFormResponse.java new file mode 100644 index 00000000..fc39352e --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/response/ProjectApplicationFormResponse.java @@ -0,0 +1,31 @@ +package es.princip.getp.application.project.apply.dto.response; + +import com.fasterxml.jackson.annotation.JsonInclude; +import es.princip.getp.application.people.dto.response.peopleProfile.PortfolioResponse; +import es.princip.getp.application.project.commission.dto.response.ProjectDetailResponse; +import es.princip.getp.domain.common.model.Duration; +import es.princip.getp.domain.people.model.Education; +import es.princip.getp.domain.project.apply.model.ProjectApplicationStatus; +import es.princip.getp.domain.project.apply.model.ProjectApplicationType; + +import java.util.List; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; + +public record ProjectApplicationFormResponse( + Long applicationId, + ProjectApplicationType type, + ProjectDetailResponse project, + String nickname, + List techStacks, + String activityArea, + Education education, + String introduction, + List portfolios, + Duration expectedDuration, + ProjectApplicationStatus status, + String description, + List attachmentFiles, + @JsonInclude(NON_NULL) List teammates +) { +} \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/response/SearchTeammateResponse.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/response/SearchTeammateResponse.java new file mode 100644 index 00000000..6de9ce65 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/dto/response/SearchTeammateResponse.java @@ -0,0 +1,8 @@ +package es.princip.getp.application.project.apply.dto.response; + +public record SearchTeammateResponse( + Long peopleId, + String nickname, + String profileImageUri +) { +} diff --git a/src/main/java/es/princip/getp/application/project/apply/exception/AlreadyAppliedProjectException.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/exception/AlreadyAppliedProjectException.java similarity index 100% rename from src/main/java/es/princip/getp/application/project/apply/exception/AlreadyAppliedProjectException.java rename to get-p-application/src/main/java/es/princip/getp/application/project/apply/exception/AlreadyAppliedProjectException.java diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/exception/FailedTeamApprovalRequestSendingException.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/exception/FailedTeamApprovalRequestSendingException.java new file mode 100644 index 00000000..9e551db7 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/exception/FailedTeamApprovalRequestSendingException.java @@ -0,0 +1,14 @@ +package es.princip.getp.application.project.apply.exception; + +import es.princip.getp.application.support.ExternalServerException; +import es.princip.getp.domain.support.ErrorDescription; + +public class FailedTeamApprovalRequestSendingException extends ExternalServerException { + + private static final String code = "FAILED_TEAM_APPROVAL_REQUEST_SENDING"; + private static final String message = "팀원 승인 신청 전송에 실패했습니다. 잠시 후 다시 시도해주세요."; + + public FailedTeamApprovalRequestSendingException() { + super(ErrorDescription.of(code, message)); + } +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/exception/NotFoundProjectApplicationException.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/exception/NotFoundProjectApplicationException.java new file mode 100644 index 00000000..0f713aa2 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/exception/NotFoundProjectApplicationException.java @@ -0,0 +1,14 @@ +package es.princip.getp.application.project.apply.exception; + +import es.princip.getp.domain.support.ErrorDescription; +import es.princip.getp.domain.support.ErrorDescriptionException; + +public class NotFoundProjectApplicationException extends ErrorDescriptionException { + + private static final String code = "NOT_FOUND_PROJECT_APPLICATION"; + private static final String message = "존재하지 않는 프로젝트 지원 내역입니다."; + + public NotFoundProjectApplicationException() { + super(ErrorDescription.of(code, message)); + } +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/exception/NotMyProjectApplicationException.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/exception/NotMyProjectApplicationException.java new file mode 100644 index 00000000..2a359c3e --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/exception/NotMyProjectApplicationException.java @@ -0,0 +1,14 @@ +package es.princip.getp.application.project.apply.exception; + +import es.princip.getp.application.support.ForbiddenException; +import es.princip.getp.domain.support.ErrorDescription; + +public class NotMyProjectApplicationException extends ForbiddenException { + + private static final String code = "NOT_MY_PROJECT_APPLICATION"; + private static final String message = "프로젝트 지원자 본인이 아닙니다."; + + public NotMyProjectApplicationException() { + super(ErrorDescription.of(code, message)); + } +} diff --git a/src/main/java/es/princip/getp/application/project/apply/exception/NotMyProjectException.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/exception/NotMyProjectException.java similarity index 100% rename from src/main/java/es/princip/getp/application/project/apply/exception/NotMyProjectException.java rename to get-p-application/src/main/java/es/princip/getp/application/project/apply/exception/NotMyProjectException.java diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/in/ApplyProjectUseCase.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/in/ApplyProjectUseCase.java new file mode 100644 index 00000000..d8736833 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/in/ApplyProjectUseCase.java @@ -0,0 +1,9 @@ +package es.princip.getp.application.project.apply.port.in; + +import es.princip.getp.application.project.apply.dto.command.ApplyProjectCommand; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; + +public interface ApplyProjectUseCase { + + ProjectApplicationId apply(final ApplyProjectCommand command); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/in/ApproveTeammateUseCase.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/in/ApproveTeammateUseCase.java new file mode 100644 index 00000000..086106b8 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/in/ApproveTeammateUseCase.java @@ -0,0 +1,6 @@ +package es.princip.getp.application.project.apply.port.in; + +public interface ApproveTeammateUseCase { + + void approve(String token); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/in/GetApplicantQuery.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/in/GetApplicantQuery.java new file mode 100644 index 00000000..4a6f7595 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/in/GetApplicantQuery.java @@ -0,0 +1,13 @@ +package es.princip.getp.application.project.apply.port.in; + +import es.princip.getp.application.project.apply.dto.response.ProjectApplicantResponse; +import es.princip.getp.application.support.Cursor; +import es.princip.getp.application.support.CursorPageable; +import es.princip.getp.application.support.dto.SliceResponse; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.project.commission.model.ProjectId; + +public interface GetApplicantQuery { + + SliceResponse getApplicantsBy(CursorPageable pageable, Member member, ProjectId projectId); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/in/GetApplicationDetailQuery.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/in/GetApplicationDetailQuery.java new file mode 100644 index 00000000..1d421685 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/in/GetApplicationDetailQuery.java @@ -0,0 +1,13 @@ +package es.princip.getp.application.project.apply.port.in; + +import es.princip.getp.application.project.apply.dto.response.ProjectApplicationDetailResponse; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; + +public interface GetApplicationDetailQuery { + + ProjectApplicationDetailResponse getApplicationDetailBy( + Member member, + ProjectApplicationId applicationId + ); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/in/GetApplicationFormQuery.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/in/GetApplicationFormQuery.java new file mode 100644 index 00000000..b2b2d85a --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/in/GetApplicationFormQuery.java @@ -0,0 +1,13 @@ +package es.princip.getp.application.project.apply.port.in; + +import es.princip.getp.application.project.apply.dto.response.ProjectApplicationFormResponse; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; + +public interface GetApplicationFormQuery { + + ProjectApplicationFormResponse getApplicationFormBy( + Member member, + ProjectApplicationId applicationId + ); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/in/SearchTeammateQuery.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/in/SearchTeammateQuery.java new file mode 100644 index 00000000..cee9310c --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/in/SearchTeammateQuery.java @@ -0,0 +1,10 @@ +package es.princip.getp.application.project.apply.port.in; + +import es.princip.getp.application.people.dto.command.SearchTeammateCommand; +import es.princip.getp.application.project.apply.dto.response.SearchTeammateResponse; +import es.princip.getp.application.support.dto.SliceResponse; + +public interface SearchTeammateQuery { + + SliceResponse search(SearchTeammateCommand command); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/CheckProjectApplicationPort.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/CheckProjectApplicationPort.java new file mode 100644 index 00000000..b3118c5e --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/CheckProjectApplicationPort.java @@ -0,0 +1,9 @@ +package es.princip.getp.application.project.apply.port.out; + +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.commission.model.ProjectId; + +public interface CheckProjectApplicationPort { + + boolean existsBy(PeopleId applicantId, ProjectId projectId); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/CountProjectApplicationPort.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/CountProjectApplicationPort.java new file mode 100644 index 00000000..8faf430d --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/CountProjectApplicationPort.java @@ -0,0 +1,11 @@ +package es.princip.getp.application.project.apply.port.out; + +import es.princip.getp.domain.project.commission.model.ProjectId; + +import java.util.Map; + +public interface CountProjectApplicationPort { + + Map countBy(final ProjectId... projectId); + Long countBy(final ProjectId projectId); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/FindProjectApplicantPort.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/FindProjectApplicantPort.java new file mode 100644 index 00000000..73478ed0 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/FindProjectApplicantPort.java @@ -0,0 +1,12 @@ +package es.princip.getp.application.project.apply.port.out; + +import es.princip.getp.application.project.apply.dto.response.ProjectApplicantResponse; +import es.princip.getp.application.support.Cursor; +import es.princip.getp.application.support.CursorPageable; +import es.princip.getp.domain.project.commission.model.ProjectId; +import org.springframework.data.domain.Slice; + +public interface FindProjectApplicantPort { + + Slice findApplicantsBy(CursorPageable pageable, ProjectId projectId); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/FindTeammatePort.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/FindTeammatePort.java new file mode 100644 index 00000000..85c25fcd --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/FindTeammatePort.java @@ -0,0 +1,12 @@ +package es.princip.getp.application.project.apply.port.out; + +import es.princip.getp.application.project.apply.dto.response.SearchTeammateResponse; +import es.princip.getp.application.support.Cursor; +import es.princip.getp.application.support.CursorPageable; +import es.princip.getp.domain.project.commission.model.ProjectId; +import org.springframework.data.domain.Slice; + +public interface FindTeammatePort { + + Slice findBy(ProjectId projectId, CursorPageable pageable, String nickname); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/LoadProjectApplicantPort.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/LoadProjectApplicantPort.java new file mode 100644 index 00000000..fe0afe7d --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/LoadProjectApplicantPort.java @@ -0,0 +1,9 @@ +package es.princip.getp.application.project.apply.port.out; + +import es.princip.getp.domain.project.apply.model.ProjectApplication; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; + +public interface LoadProjectApplicantPort { + + ProjectApplication loadBy(ProjectApplicationId applicationId); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/SaveProjectApplicationPort.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/SaveProjectApplicationPort.java new file mode 100644 index 00000000..32f57eae --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/SaveProjectApplicationPort.java @@ -0,0 +1,9 @@ +package es.princip.getp.application.project.apply.port.out; + +import es.princip.getp.domain.project.apply.model.ProjectApplication; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; + +public interface SaveProjectApplicationPort { + + ProjectApplicationId save(ProjectApplication projectApplication); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/SerializeApplicantCursorPort.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/SerializeApplicantCursorPort.java new file mode 100644 index 00000000..3c376916 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/SerializeApplicantCursorPort.java @@ -0,0 +1,9 @@ +package es.princip.getp.application.project.apply.port.out; + +import es.princip.getp.application.project.apply.dto.response.ProjectApplicantResponse; +import org.springframework.data.domain.Slice; + +public interface SerializeApplicantCursorPort { + + String serializeCursor(Slice slice); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/SerializeTeammateCursorPort.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/SerializeTeammateCursorPort.java new file mode 100644 index 00000000..8f96f35a --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/SerializeTeammateCursorPort.java @@ -0,0 +1,9 @@ +package es.princip.getp.application.project.apply.port.out; + +import es.princip.getp.application.project.apply.dto.response.SearchTeammateResponse; +import org.springframework.data.domain.Slice; + +public interface SerializeTeammateCursorPort { + + String serializeCursor(Slice slice); +} diff --git a/src/main/java/es/princip/getp/application/project/apply/port/out/SaveProjectApplicationPort.java b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/UpdateProjectApplicantPort.java similarity index 57% rename from src/main/java/es/princip/getp/application/project/apply/port/out/SaveProjectApplicationPort.java rename to get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/UpdateProjectApplicantPort.java index 4adb03ec..b0ecfd7a 100644 --- a/src/main/java/es/princip/getp/application/project/apply/port/out/SaveProjectApplicationPort.java +++ b/get-p-application/src/main/java/es/princip/getp/application/project/apply/port/out/UpdateProjectApplicantPort.java @@ -2,7 +2,7 @@ import es.princip.getp.domain.project.apply.model.ProjectApplication; -public interface SaveProjectApplicationPort { +public interface UpdateProjectApplicantPort { - Long save(ProjectApplication projectApplication); + void update(ProjectApplication application); } diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/commission/GetProjectService.java b/get-p-application/src/main/java/es/princip/getp/application/project/commission/GetProjectService.java new file mode 100644 index 00000000..1da40950 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/commission/GetProjectService.java @@ -0,0 +1,78 @@ +package es.princip.getp.application.project.commission; + +import es.princip.getp.application.project.commission.dto.command.GetProjectCommand; +import es.princip.getp.application.project.commission.dto.command.ProjectSearchFilter; +import es.princip.getp.application.project.commission.dto.response.ProjectCardResponse; +import es.princip.getp.application.project.commission.dto.response.ProjectDetailResponse; +import es.princip.getp.application.project.commission.port.in.GetProjectQuery; +import es.princip.getp.application.project.commission.port.out.FindProjectPort; +import es.princip.getp.application.support.MosaicFactory; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.project.commission.model.ProjectId; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; + +import static es.princip.getp.application.support.ApplicationQueryUtil.isNotLogined; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class GetProjectService implements GetProjectQuery { + + private final FindProjectPort findProjectPort; + private final MosaicFactory mosaicFactory; + + private boolean doesFilterRequireLogin(final ProjectSearchFilter filter) { + return (filter.isApplied() || filter.isLiked() || filter.isCommissioned()); + } + + private boolean isFilterAuthorized(final ProjectSearchFilter filter, final Member member) { + return (filter.isApplied() && member.isPeople()) || + (filter.isLiked() && member.isPeople()) || + (filter.isCommissioned() && member.isClient()); + } + + private boolean isMemberAuthenticated(final Member member) { + return member != null; + } + + private void validateFilter(final ProjectSearchFilter filter, final Member member) { + if (!doesFilterRequireLogin(filter)) { + return ; + } + if (!isMemberAuthenticated(member)) { + throw new AuthenticationCredentialsNotFoundException("해당 필터를 사용하려면 로그인이 필요합니다."); + } + if (!isFilterAuthorized(filter, member)) { + throw new AccessDeniedException("사용자가 사용할 수 없는 필터입니다."); + } + } + + @Override + public Page getPagedCards(final GetProjectCommand command) { + final ProjectSearchFilter filter = command.filter(); + validateFilter(filter, command.member()); + final Pageable pageable = command.pageable(); + final MemberId memberId = Optional.ofNullable(command.member()) + .map(Member::getId) + .orElse(null); + return findProjectPort.findBy(pageable, filter, memberId); + } + + @Override + public ProjectDetailResponse getDetailBy(final Member member, final ProjectId projectId) { + final ProjectDetailResponse response = findProjectPort.findBy(member, projectId); + if (isNotLogined(member)) { + return mosaicFactory.mosaic(response); + } + return response; + } +} diff --git a/src/main/java/es/princip/getp/application/project/commission/ProjectCommissionService.java b/get-p-application/src/main/java/es/princip/getp/application/project/commission/ProjectCommissionService.java similarity index 77% rename from src/main/java/es/princip/getp/application/project/commission/ProjectCommissionService.java rename to get-p-application/src/main/java/es/princip/getp/application/project/commission/ProjectCommissionService.java index 2f878085..17a3aab5 100644 --- a/src/main/java/es/princip/getp/application/project/commission/ProjectCommissionService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/project/commission/ProjectCommissionService.java @@ -1,11 +1,13 @@ package es.princip.getp.application.project.commission; import es.princip.getp.application.client.port.out.LoadClientPort; -import es.princip.getp.application.project.commission.command.CommissionProjectCommand; +import es.princip.getp.application.project.commission.dto.command.CommissionProjectCommand; import es.princip.getp.application.project.commission.port.in.CommissionProjectUseCase; import es.princip.getp.application.project.commission.port.out.SaveProjectPort; +import es.princip.getp.domain.client.model.ClientId; import es.princip.getp.domain.project.commission.model.Project; import es.princip.getp.domain.project.commission.model.ProjectData; +import es.princip.getp.domain.project.commission.model.ProjectId; import es.princip.getp.domain.project.commission.service.ProjectCommissioner; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -22,8 +24,8 @@ public class ProjectCommissionService implements CommissionProjectUseCase { private final ProjectCommissioner projectCommissioner; @Transactional - public Long commission(final CommissionProjectCommand command) { - final Long clientId = loadClientPort.loadBy(command.memberId()).getClientId(); + public ProjectId commission(final CommissionProjectCommand command) { + final ClientId clientId = loadClientPort.loadBy(command.memberId()).getId(); final ProjectData data = projectDataMapper.mapToData(clientId, command); final Project project = projectCommissioner.commissionProject(data); return saveProjectPort.save(project); diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/commission/ProjectDataMapper.java b/get-p-application/src/main/java/es/princip/getp/application/project/commission/ProjectDataMapper.java new file mode 100644 index 00000000..aee8c796 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/commission/ProjectDataMapper.java @@ -0,0 +1,12 @@ +package es.princip.getp.application.project.commission; + +import es.princip.getp.application.project.commission.dto.command.CommissionProjectCommand; +import es.princip.getp.domain.client.model.ClientId; +import es.princip.getp.domain.project.commission.model.ProjectData; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +interface ProjectDataMapper { + + ProjectData mapToData(ClientId clientId, CommissionProjectCommand command); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/commission/ProjectDetailResponseMosaicResolver.java b/get-p-application/src/main/java/es/princip/getp/application/project/commission/ProjectDetailResponseMosaicResolver.java new file mode 100644 index 00000000..99e328fb --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/commission/ProjectDetailResponseMosaicResolver.java @@ -0,0 +1,53 @@ +package es.princip.getp.application.project.commission; + +import es.princip.getp.application.common.dto.response.AddressResponse; +import es.princip.getp.application.project.commission.dto.response.ProjectClientResponse; +import es.princip.getp.application.project.commission.dto.response.ProjectDetailResponse; +import es.princip.getp.application.support.MosaicResolver; +import es.princip.getp.application.support.MosaicResolverSupport; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.MessageSource; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +class ProjectDetailResponseMosaicResolver extends MosaicResolverSupport + implements MosaicResolver { + + @Autowired + public ProjectDetailResponseMosaicResolver(final MessageSource messageSource) { + super(messageSource); + } + + private ProjectClientResponse mosaicClient(final ProjectClientResponse client) { + final String mosaicNickname = mosaicMessage(client.nickname()); + final AddressResponse mosaicAddress = mosaicAddress(client.address()); + return new ProjectClientResponse(null, mosaicNickname, mosaicAddress); + } + + private AddressResponse mosaicAddress(final AddressResponse address) { + if (address == null) { + return null; + } + return new AddressResponse( + mosaicMessage(address.zipcode()), + mosaicMessage(address.street()), + mosaicMessage(address.detail()) + ); + } + + @Override + public boolean supports(final Class clazz) { + return ProjectDetailResponse.class.equals(clazz); + } + + @Override + public ProjectDetailResponse resolve(final ProjectDetailResponse response) { + final String description = mosaicMessage(response.getDescription()); + final List attachmentFiles= mosaicMessage(response.getAttachmentFiles()); + final ProjectClientResponse client = mosaicClient(response.getClient()); + + return response.mosaic(description, attachmentFiles, client); + } +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/application/project/commission/command/CommissionProjectCommand.java b/get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/command/CommissionProjectCommand.java similarity index 78% rename from src/main/java/es/princip/getp/application/project/commission/command/CommissionProjectCommand.java rename to get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/command/CommissionProjectCommand.java index 9532fae3..7b2a5e1a 100644 --- a/src/main/java/es/princip/getp/application/project/commission/command/CommissionProjectCommand.java +++ b/get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/command/CommissionProjectCommand.java @@ -1,17 +1,19 @@ -package es.princip.getp.application.project.commission.command; +package es.princip.getp.application.project.commission.dto.command; import es.princip.getp.domain.common.model.AttachmentFile; import es.princip.getp.domain.common.model.Duration; import es.princip.getp.domain.common.model.Hashtag; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.project.commission.model.MeetingType; import es.princip.getp.domain.project.commission.model.ProjectCategory; import java.util.List; public record CommissionProjectCommand( - Long memberId, + MemberId memberId, String title, Long payment, + Long recruitmentCount, Duration applicationDuration, Duration estimatedDuration, String description, diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/command/GetProjectCommand.java b/get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/command/GetProjectCommand.java new file mode 100644 index 00000000..e75ffad4 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/command/GetProjectCommand.java @@ -0,0 +1,11 @@ +package es.princip.getp.application.project.commission.dto.command; + +import es.princip.getp.domain.member.model.Member; +import org.springframework.data.domain.Pageable; + +public record GetProjectCommand( + Pageable pageable, + ProjectSearchFilter filter, + Member member +) { +} \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/command/ProjectSearchFilter.java b/get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/command/ProjectSearchFilter.java new file mode 100644 index 00000000..f82e940e --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/command/ProjectSearchFilter.java @@ -0,0 +1,24 @@ +package es.princip.getp.application.project.commission.dto.command; + +import lombok.Getter; + +@Getter +public class ProjectSearchFilter { + + private final boolean liked; + private final boolean commissioned; + private final boolean applied; + private final boolean closed; + + public ProjectSearchFilter( + final String liked, + final String commissioned, + final String applied, + final String closed + ) { + this.liked = Boolean.parseBoolean(liked); + this.commissioned = Boolean.parseBoolean(commissioned); + this.applied = Boolean.parseBoolean(applied); + this.closed = Boolean.parseBoolean(closed); + } +} \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/command/ProjectSearchOrder.java b/get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/command/ProjectSearchOrder.java new file mode 100644 index 00000000..ab85f93d --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/command/ProjectSearchOrder.java @@ -0,0 +1,11 @@ +package es.princip.getp.application.project.commission.dto.command; + +import es.princip.getp.util.StringUtil; + +public enum ProjectSearchOrder { + PROJECT_ID, CREATED_AT, PAYMENT, APPLICATION_DURATION; + + public static ProjectSearchOrder get(final String value) { + return ProjectSearchOrder.valueOf(StringUtil.camelToSnake(value).toUpperCase()); + } +} diff --git a/src/main/java/es/princip/getp/api/controller/project/command/dto/response/CommissionProjectResponse.java b/get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/response/CommissionProjectResponse.java similarity index 52% rename from src/main/java/es/princip/getp/api/controller/project/command/dto/response/CommissionProjectResponse.java rename to get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/response/CommissionProjectResponse.java index 78f3070e..95d4c5e9 100644 --- a/src/main/java/es/princip/getp/api/controller/project/command/dto/response/CommissionProjectResponse.java +++ b/get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/response/CommissionProjectResponse.java @@ -1,4 +1,4 @@ -package es.princip.getp.api.controller.project.command.dto.response; +package es.princip.getp.application.project.commission.dto.response; public record CommissionProjectResponse( Long projectId diff --git a/src/main/java/es/princip/getp/api/controller/project/query/dto/ProjectCardResponse.java b/get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/response/ProjectCardResponse.java similarity index 68% rename from src/main/java/es/princip/getp/api/controller/project/query/dto/ProjectCardResponse.java rename to get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/response/ProjectCardResponse.java index b902ccab..c67e2d89 100644 --- a/src/main/java/es/princip/getp/api/controller/project/query/dto/ProjectCardResponse.java +++ b/get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/response/ProjectCardResponse.java @@ -1,31 +1,36 @@ -package es.princip.getp.api.controller.project.query.dto; +package es.princip.getp.application.project.commission.dto.response; -import es.princip.getp.api.controller.common.dto.HashtagsResponse; import es.princip.getp.domain.common.model.Duration; +import es.princip.getp.domain.common.model.Hashtag; import es.princip.getp.domain.project.commission.model.Project; import es.princip.getp.domain.project.commission.model.ProjectStatus; +import java.util.List; + public record ProjectCardResponse( Long projectId, String title, Long payment, + Long recruitmentCount, Long applicantsCount, Long estimatedDays, Duration applicationDuration, - HashtagsResponse hashtags, + List hashtags, String description, ProjectStatus status ) { - public static ProjectCardResponse of(final Project project, final Long applicantsCount) { return new ProjectCardResponse( - project.getProjectId(), + project.getId().getValue(), project.getTitle(), project.getPayment(), + project.getRecruitmentCount(), applicantsCount, project.getEstimatedDuration().days(), project.getApplicationDuration(), - HashtagsResponse.from(project.getHashtags()), + project.getHashtags().stream() + .map(Hashtag::getValue) + .toList(), project.getDescription(), project.getStatus() ); diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/response/ProjectClientResponse.java b/get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/response/ProjectClientResponse.java new file mode 100644 index 00000000..772b20f7 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/response/ProjectClientResponse.java @@ -0,0 +1,14 @@ +package es.princip.getp.application.project.commission.dto.response; + +import com.fasterxml.jackson.annotation.JsonInclude; +import es.princip.getp.application.common.dto.response.AddressResponse; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; + +@JsonInclude(NON_NULL) +public record ProjectClientResponse( + Long clientId, + String nickname, + AddressResponse address +) { +} \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/response/ProjectDetailResponse.java b/get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/response/ProjectDetailResponse.java new file mode 100644 index 00000000..819b82e8 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/commission/dto/response/ProjectDetailResponse.java @@ -0,0 +1,79 @@ +package es.princip.getp.application.project.commission.dto.response; + +import com.fasterxml.jackson.annotation.JsonInclude; +import es.princip.getp.domain.common.model.Duration; +import es.princip.getp.domain.project.commission.model.MeetingType; +import es.princip.getp.domain.project.commission.model.ProjectCategory; +import es.princip.getp.domain.project.commission.model.ProjectStatus; +import lombok.Getter; + +import java.util.List; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; + +@Getter +public class ProjectDetailResponse { + private final Long projectId; + private final String title; + private final Long payment; + private final Long recruitmentCount; + private final Long applicantsCount; + private final Duration applicationDuration; + private final Duration estimatedDuration; + private String description; + private final MeetingType meetingType; + private final ProjectCategory category; + private final ProjectStatus status; + private List attachmentFiles; + private final List hashtags; + private final Long likesCount; + @JsonInclude(NON_NULL) private final Boolean liked; + private ProjectClientResponse client; + + public ProjectDetailResponse( + final Long projectId, + final String title, + final Long payment, + final Long recruitmentCount, + final Long applicantsCount, + final Duration applicationDuration, + final Duration estimatedDuration, + final String description, + final MeetingType meetingType, + final ProjectCategory category, + final ProjectStatus status, + final List attachmentFiles, + final List hashtags, + final Long likesCount, + final Boolean liked, + final ProjectClientResponse client + ) { + this.projectId = projectId; + this.title = title; + this.payment = payment; + this.recruitmentCount = recruitmentCount; + this.applicantsCount = applicantsCount; + this.applicationDuration = applicationDuration; + this.estimatedDuration = estimatedDuration; + this.description = description; + this.meetingType = meetingType; + this.category = category; + this.status = status; + this.attachmentFiles = attachmentFiles; + this.hashtags = hashtags; + this.likesCount = likesCount; + this.liked = liked; + this.client = client; + } + + public ProjectDetailResponse mosaic( + final String description, + final List attachmentFiles, + final ProjectClientResponse client + ) { + this.description = description; + this.attachmentFiles = attachmentFiles; + this.client = client; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/commission/NotFoundProjectException.java b/get-p-application/src/main/java/es/princip/getp/application/project/commission/exception/NotFoundProjectException.java similarity index 59% rename from src/main/java/es/princip/getp/persistence/adapter/project/commission/NotFoundProjectException.java rename to get-p-application/src/main/java/es/princip/getp/application/project/commission/exception/NotFoundProjectException.java index 86c331c3..bef395cf 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/project/commission/NotFoundProjectException.java +++ b/get-p-application/src/main/java/es/princip/getp/application/project/commission/exception/NotFoundProjectException.java @@ -1,9 +1,9 @@ -package es.princip.getp.persistence.adapter.project.commission; +package es.princip.getp.application.project.commission.exception; import es.princip.getp.domain.support.ErrorDescription; -import es.princip.getp.persistence.support.NotFoundException; +import es.princip.getp.domain.support.ErrorDescriptionException; -public class NotFoundProjectException extends NotFoundException { +public class NotFoundProjectException extends ErrorDescriptionException { private static final String code = "NOT_FOUND_PROJECT"; private static final String message = "존재하지 않는 프로젝트입니다."; diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/commission/port/in/CommissionProjectUseCase.java b/get-p-application/src/main/java/es/princip/getp/application/project/commission/port/in/CommissionProjectUseCase.java new file mode 100644 index 00000000..522aabf2 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/commission/port/in/CommissionProjectUseCase.java @@ -0,0 +1,9 @@ +package es.princip.getp.application.project.commission.port.in; + +import es.princip.getp.application.project.commission.dto.command.CommissionProjectCommand; +import es.princip.getp.domain.project.commission.model.ProjectId; + +public interface CommissionProjectUseCase { + + ProjectId commission(CommissionProjectCommand command); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/commission/port/in/GetProjectQuery.java b/get-p-application/src/main/java/es/princip/getp/application/project/commission/port/in/GetProjectQuery.java new file mode 100644 index 00000000..18d56ba2 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/commission/port/in/GetProjectQuery.java @@ -0,0 +1,15 @@ +package es.princip.getp.application.project.commission.port.in; + +import es.princip.getp.application.project.commission.dto.command.GetProjectCommand; +import es.princip.getp.application.project.commission.dto.response.ProjectCardResponse; +import es.princip.getp.application.project.commission.dto.response.ProjectDetailResponse; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.project.commission.model.ProjectId; +import org.springframework.data.domain.Page; + +public interface GetProjectQuery { + + Page getPagedCards(GetProjectCommand command); + + ProjectDetailResponse getDetailBy(Member member, ProjectId projectId); +} \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/commission/port/out/FindProjectPort.java b/get-p-application/src/main/java/es/princip/getp/application/project/commission/port/out/FindProjectPort.java new file mode 100644 index 00000000..29d8de9e --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/commission/port/out/FindProjectPort.java @@ -0,0 +1,17 @@ +package es.princip.getp.application.project.commission.port.out; + +import es.princip.getp.application.project.commission.dto.response.ProjectCardResponse; +import es.princip.getp.application.project.commission.dto.response.ProjectDetailResponse; +import es.princip.getp.application.project.commission.dto.command.ProjectSearchFilter; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.project.commission.model.ProjectId; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +public interface FindProjectPort { + + Page findBy(Pageable pageable, ProjectSearchFilter filter, MemberId memberId); + + ProjectDetailResponse findBy(Member member, ProjectId projectId); +} diff --git a/src/main/java/es/princip/getp/application/project/commission/port/out/LoadProjectPort.java b/get-p-application/src/main/java/es/princip/getp/application/project/commission/port/out/LoadProjectPort.java similarity index 61% rename from src/main/java/es/princip/getp/application/project/commission/port/out/LoadProjectPort.java rename to get-p-application/src/main/java/es/princip/getp/application/project/commission/port/out/LoadProjectPort.java index aabce0b0..e924450a 100644 --- a/src/main/java/es/princip/getp/application/project/commission/port/out/LoadProjectPort.java +++ b/get-p-application/src/main/java/es/princip/getp/application/project/commission/port/out/LoadProjectPort.java @@ -1,8 +1,9 @@ package es.princip.getp.application.project.commission.port.out; import es.princip.getp.domain.project.commission.model.Project; +import es.princip.getp.domain.project.commission.model.ProjectId; public interface LoadProjectPort { - Project loadBy(Long projectId); + Project loadBy(ProjectId projectId); } diff --git a/src/main/java/es/princip/getp/application/project/commission/port/out/SaveProjectPort.java b/get-p-application/src/main/java/es/princip/getp/application/project/commission/port/out/SaveProjectPort.java similarity index 62% rename from src/main/java/es/princip/getp/application/project/commission/port/out/SaveProjectPort.java rename to get-p-application/src/main/java/es/princip/getp/application/project/commission/port/out/SaveProjectPort.java index 010f0072..e30d4d90 100644 --- a/src/main/java/es/princip/getp/application/project/commission/port/out/SaveProjectPort.java +++ b/get-p-application/src/main/java/es/princip/getp/application/project/commission/port/out/SaveProjectPort.java @@ -1,8 +1,9 @@ package es.princip.getp.application.project.commission.port.out; import es.princip.getp.domain.project.commission.model.Project; +import es.princip.getp.domain.project.commission.model.ProjectId; public interface SaveProjectPort { - Long save(Project project); + ProjectId save(Project project); } diff --git a/src/main/java/es/princip/getp/application/project/commission/port/out/UpdateProjectPort.java b/get-p-application/src/main/java/es/princip/getp/application/project/commission/port/out/UpdateProjectPort.java similarity index 100% rename from src/main/java/es/princip/getp/application/project/commission/port/out/UpdateProjectPort.java rename to get-p-application/src/main/java/es/princip/getp/application/project/commission/port/out/UpdateProjectPort.java diff --git a/src/main/java/es/princip/getp/application/project/meeting/EmailMeetingSender.java b/get-p-application/src/main/java/es/princip/getp/application/project/meeting/EmailMeetingSender.java similarity index 95% rename from src/main/java/es/princip/getp/application/project/meeting/EmailMeetingSender.java rename to get-p-application/src/main/java/es/princip/getp/application/project/meeting/EmailMeetingSender.java index 127ef6d1..1a413b91 100644 --- a/src/main/java/es/princip/getp/application/project/meeting/EmailMeetingSender.java +++ b/get-p-application/src/main/java/es/princip/getp/application/project/meeting/EmailMeetingSender.java @@ -6,6 +6,7 @@ import es.princip.getp.domain.people.model.People; import es.princip.getp.domain.project.commission.model.Project; import es.princip.getp.domain.project.meeting.model.ProjectMeeting; +import jakarta.mail.MessagingException; import lombok.RequiredArgsConstructor; import org.springframework.mail.MailException; import org.springframework.stereotype.Service; @@ -52,7 +53,7 @@ public void send( ); try { sendMailUseCase.send(message); - } catch (MailException exception) { + } catch (MailException | MessagingException exception) { throw new FailedMeetingSendingException(); } } diff --git a/src/main/java/es/princip/getp/application/project/meeting/MeetingSender.java b/get-p-application/src/main/java/es/princip/getp/application/project/meeting/MeetingSender.java similarity index 100% rename from src/main/java/es/princip/getp/application/project/meeting/MeetingSender.java rename to get-p-application/src/main/java/es/princip/getp/application/project/meeting/MeetingSender.java diff --git a/src/main/java/es/princip/getp/application/project/meeting/ProjectMeetingService.java b/get-p-application/src/main/java/es/princip/getp/application/project/meeting/ProjectMeetingService.java similarity index 77% rename from src/main/java/es/princip/getp/application/project/meeting/ProjectMeetingService.java rename to get-p-application/src/main/java/es/princip/getp/application/project/meeting/ProjectMeetingService.java index 5bd1f7ac..fce04014 100644 --- a/src/main/java/es/princip/getp/application/project/meeting/ProjectMeetingService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/project/meeting/ProjectMeetingService.java @@ -1,15 +1,18 @@ package es.princip.getp.application.project.meeting; +import es.princip.getp.application.client.port.out.LoadClientPort; import es.princip.getp.application.people.port.out.LoadPeoplePort; import es.princip.getp.application.project.apply.port.out.CheckProjectApplicationPort; import es.princip.getp.application.project.commission.port.out.LoadProjectPort; -import es.princip.getp.application.project.meeting.command.ScheduleMeetingCommand; +import es.princip.getp.application.project.meeting.dto.command.ScheduleMeetingCommand; import es.princip.getp.application.project.meeting.exception.NotApplicantException; import es.princip.getp.application.project.meeting.exception.NotClientOfProjectException; -import es.princip.getp.application.project.meeting.port.out.CheckProjectMeetingPort; import es.princip.getp.application.project.meeting.port.out.SaveProjectMeetingPort; +import es.princip.getp.domain.client.model.Client; import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.people.model.PeopleId; import es.princip.getp.domain.project.commission.model.Project; +import es.princip.getp.domain.project.commission.model.ProjectId; import es.princip.getp.domain.project.meeting.model.ProjectMeeting; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -20,12 +23,12 @@ @Transactional(readOnly = true) public class ProjectMeetingService { + private final LoadClientPort loadClientPort; private final LoadPeoplePort loadPeoplePort; private final LoadProjectPort loadProjectPort; private final CheckProjectApplicationPort checkProjectApplicationPort; private final SaveProjectMeetingPort saveProjectMeetingPort; - private final CheckProjectMeetingPort checkProjectMeetingPort; private final MeetingSender meetingSender; @@ -39,8 +42,9 @@ public class ProjectMeetingService { public Long scheduleMeeting(final ScheduleMeetingCommand command) { final People people = loadPeoplePort.loadBy(command.applicantId()); final Project project = loadProjectPort.loadBy(command.projectId()); + final Client client = loadClientPort.loadBy(command.memberId()); - checkMemberIsClientOfProject(command.memberId(), command.projectId()); + checkClientOfProject(client, project); checkPeopleIsApplicant(command.applicantId(), command.projectId()); final ProjectMeeting projectMeeting = ProjectMeeting.builder() @@ -58,13 +62,13 @@ public Long scheduleMeeting(final ScheduleMeetingCommand command) { return meetingId; } - private void checkMemberIsClientOfProject(final Long memberId, final Long projectId) { - if (!checkProjectMeetingPort.existsApplicantByProjectIdAndMemberId(projectId, memberId)) { + private void checkClientOfProject(final Client client, final Project project) { + if (!project.isClient(client)) { throw new NotClientOfProjectException(); } } - private void checkPeopleIsApplicant(final Long applicantId, final Long projectId) { + private void checkPeopleIsApplicant(final PeopleId applicantId, final ProjectId projectId) { if (!checkProjectApplicationPort.existsBy(applicantId, projectId)) { throw new NotApplicantException(); } diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/meeting/dto/command/ScheduleMeetingCommand.java b/get-p-application/src/main/java/es/princip/getp/application/project/meeting/dto/command/ScheduleMeetingCommand.java new file mode 100644 index 00000000..edda3d9b --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/meeting/dto/command/ScheduleMeetingCommand.java @@ -0,0 +1,18 @@ +package es.princip.getp.application.project.meeting.dto.command; + +import es.princip.getp.domain.common.model.MeetingSchedule; +import es.princip.getp.domain.common.model.PhoneNumber; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.commission.model.ProjectId; + +public record ScheduleMeetingCommand( + MemberId memberId, + ProjectId projectId, + PeopleId applicantId, + String location, + MeetingSchedule schedule, + PhoneNumber phoneNumber, + String description +) { +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/project/meeting/dto/response/ScheduleMeetingResponse.java b/get-p-application/src/main/java/es/princip/getp/application/project/meeting/dto/response/ScheduleMeetingResponse.java new file mode 100644 index 00000000..1c088d3c --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/project/meeting/dto/response/ScheduleMeetingResponse.java @@ -0,0 +1,6 @@ +package es.princip.getp.application.project.meeting.dto.response; + +public record ScheduleMeetingResponse( + Long meetingId +) { +} diff --git a/src/main/java/es/princip/getp/application/project/meeting/exception/FailedMeetingSendingException.java b/get-p-application/src/main/java/es/princip/getp/application/project/meeting/exception/FailedMeetingSendingException.java similarity index 100% rename from src/main/java/es/princip/getp/application/project/meeting/exception/FailedMeetingSendingException.java rename to get-p-application/src/main/java/es/princip/getp/application/project/meeting/exception/FailedMeetingSendingException.java diff --git a/src/main/java/es/princip/getp/application/project/meeting/exception/NotApplicantException.java b/get-p-application/src/main/java/es/princip/getp/application/project/meeting/exception/NotApplicantException.java similarity index 100% rename from src/main/java/es/princip/getp/application/project/meeting/exception/NotApplicantException.java rename to get-p-application/src/main/java/es/princip/getp/application/project/meeting/exception/NotApplicantException.java diff --git a/src/main/java/es/princip/getp/application/project/meeting/exception/NotClientOfProjectException.java b/get-p-application/src/main/java/es/princip/getp/application/project/meeting/exception/NotClientOfProjectException.java similarity index 100% rename from src/main/java/es/princip/getp/application/project/meeting/exception/NotClientOfProjectException.java rename to get-p-application/src/main/java/es/princip/getp/application/project/meeting/exception/NotClientOfProjectException.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/meeting/NotFoundProjectMeetingException.java b/get-p-application/src/main/java/es/princip/getp/application/project/meeting/exception/NotFoundProjectMeetingException.java similarity index 60% rename from src/main/java/es/princip/getp/persistence/adapter/project/meeting/NotFoundProjectMeetingException.java rename to get-p-application/src/main/java/es/princip/getp/application/project/meeting/exception/NotFoundProjectMeetingException.java index 1861a440..f6951787 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/project/meeting/NotFoundProjectMeetingException.java +++ b/get-p-application/src/main/java/es/princip/getp/application/project/meeting/exception/NotFoundProjectMeetingException.java @@ -1,9 +1,9 @@ -package es.princip.getp.persistence.adapter.project.meeting; +package es.princip.getp.application.project.meeting.exception; import es.princip.getp.domain.support.ErrorDescription; -import es.princip.getp.persistence.support.NotFoundException; +import es.princip.getp.domain.support.ErrorDescriptionException; -class NotFoundProjectMeetingException extends NotFoundException { +public class NotFoundProjectMeetingException extends ErrorDescriptionException { private static final String code = "NOT_FOUND_PROJECT_MEETING"; private static final String message = "존재하지 않는 미팅입니다."; diff --git a/src/main/java/es/princip/getp/application/project/meeting/port/out/LoadProjectMeetingPort.java b/get-p-application/src/main/java/es/princip/getp/application/project/meeting/port/out/LoadProjectMeetingPort.java similarity index 100% rename from src/main/java/es/princip/getp/application/project/meeting/port/out/LoadProjectMeetingPort.java rename to get-p-application/src/main/java/es/princip/getp/application/project/meeting/port/out/LoadProjectMeetingPort.java diff --git a/src/main/java/es/princip/getp/application/project/meeting/port/out/SaveProjectMeetingPort.java b/get-p-application/src/main/java/es/princip/getp/application/project/meeting/port/out/SaveProjectMeetingPort.java similarity index 100% rename from src/main/java/es/princip/getp/application/project/meeting/port/out/SaveProjectMeetingPort.java rename to get-p-application/src/main/java/es/princip/getp/application/project/meeting/port/out/SaveProjectMeetingPort.java diff --git a/src/main/java/es/princip/getp/application/project/meeting/port/out/UpdateProjectMeetingPort.java b/get-p-application/src/main/java/es/princip/getp/application/project/meeting/port/out/UpdateProjectMeetingPort.java similarity index 100% rename from src/main/java/es/princip/getp/application/project/meeting/port/out/UpdateProjectMeetingPort.java rename to get-p-application/src/main/java/es/princip/getp/application/project/meeting/port/out/UpdateProjectMeetingPort.java diff --git a/get-p-application/src/main/java/es/princip/getp/application/serviceTerm/dto/command/ServiceTermCommand.java b/get-p-application/src/main/java/es/princip/getp/application/serviceTerm/dto/command/ServiceTermCommand.java new file mode 100644 index 00000000..ce528117 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/serviceTerm/dto/command/ServiceTermCommand.java @@ -0,0 +1,8 @@ +package es.princip.getp.application.serviceTerm.dto.command; + +public record ServiceTermCommand( + String tag, + boolean required, + boolean revocable +) { +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/serviceTerm/dto/response/ServiceTermResponse.java b/get-p-application/src/main/java/es/princip/getp/application/serviceTerm/dto/response/ServiceTermResponse.java similarity index 74% rename from src/main/java/es/princip/getp/api/controller/serviceTerm/dto/response/ServiceTermResponse.java rename to get-p-application/src/main/java/es/princip/getp/application/serviceTerm/dto/response/ServiceTermResponse.java index 2b6e963d..d4c9e7da 100644 --- a/src/main/java/es/princip/getp/api/controller/serviceTerm/dto/response/ServiceTermResponse.java +++ b/get-p-application/src/main/java/es/princip/getp/application/serviceTerm/dto/response/ServiceTermResponse.java @@ -1,10 +1,9 @@ -package es.princip.getp.api.controller.serviceTerm.dto.response; +package es.princip.getp.application.serviceTerm.dto.response; import es.princip.getp.domain.serviceTerm.model.ServiceTerm; -import jakarta.validation.constraints.NotNull; public record ServiceTermResponse( - @NotNull String tag, + String tag, boolean required, boolean revocable ) { diff --git a/src/main/java/es/princip/getp/application/serviceTerm/exception/DuplicatedTagException.java b/get-p-application/src/main/java/es/princip/getp/application/serviceTerm/exception/DuplicatedTagException.java similarity index 100% rename from src/main/java/es/princip/getp/application/serviceTerm/exception/DuplicatedTagException.java rename to get-p-application/src/main/java/es/princip/getp/application/serviceTerm/exception/DuplicatedTagException.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/serviceTerm/NotFoundServiceTermException.java b/get-p-application/src/main/java/es/princip/getp/application/serviceTerm/exception/NotFoundServiceTermException.java similarity index 55% rename from src/main/java/es/princip/getp/persistence/adapter/serviceTerm/NotFoundServiceTermException.java rename to get-p-application/src/main/java/es/princip/getp/application/serviceTerm/exception/NotFoundServiceTermException.java index 95188975..ad5c0904 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/serviceTerm/NotFoundServiceTermException.java +++ b/get-p-application/src/main/java/es/princip/getp/application/serviceTerm/exception/NotFoundServiceTermException.java @@ -1,20 +1,20 @@ -package es.princip.getp.persistence.adapter.serviceTerm; +package es.princip.getp.application.serviceTerm.exception; import es.princip.getp.domain.support.ErrorDescription; -import es.princip.getp.persistence.support.NotFoundException; +import es.princip.getp.application.support.NotFoundException; import java.util.Set; -class NotFoundServiceTermException extends NotFoundException { +public class NotFoundServiceTermException extends NotFoundException { private static final String code = "NOT_FOUND_SERVICE_TERM"; private static final String message = "존재하지 않는 서비스 약관입니다."; - NotFoundServiceTermException(final Set tags) { + public NotFoundServiceTermException(final Set tags) { super(ErrorDescription.of(code, formatMessage(tags))); } - static String formatMessage(final Set tags) { + public static String formatMessage(final Set tags) { return String.format("%s은(는) %s", String.join(", ", tags), message); } } diff --git a/src/main/java/es/princip/getp/application/serviceTerm/port/in/RegisterServiceTermUseCase.java b/get-p-application/src/main/java/es/princip/getp/application/serviceTerm/port/in/RegisterServiceTermUseCase.java similarity index 55% rename from src/main/java/es/princip/getp/application/serviceTerm/port/in/RegisterServiceTermUseCase.java rename to get-p-application/src/main/java/es/princip/getp/application/serviceTerm/port/in/RegisterServiceTermUseCase.java index a3547b7f..30d43181 100644 --- a/src/main/java/es/princip/getp/application/serviceTerm/port/in/RegisterServiceTermUseCase.java +++ b/get-p-application/src/main/java/es/princip/getp/application/serviceTerm/port/in/RegisterServiceTermUseCase.java @@ -1,9 +1,9 @@ package es.princip.getp.application.serviceTerm.port.in; -import es.princip.getp.api.controller.serviceTerm.dto.reqeust.ServiceTermRequest; +import es.princip.getp.application.serviceTerm.dto.command.ServiceTermCommand; import es.princip.getp.domain.serviceTerm.model.ServiceTerm; public interface RegisterServiceTermUseCase { - ServiceTerm register(ServiceTermRequest request); + ServiceTerm register(ServiceTermCommand command); } diff --git a/src/main/java/es/princip/getp/application/serviceTerm/port/out/CheckServiceTermPort.java b/get-p-application/src/main/java/es/princip/getp/application/serviceTerm/port/out/CheckServiceTermPort.java similarity index 100% rename from src/main/java/es/princip/getp/application/serviceTerm/port/out/CheckServiceTermPort.java rename to get-p-application/src/main/java/es/princip/getp/application/serviceTerm/port/out/CheckServiceTermPort.java diff --git a/src/main/java/es/princip/getp/application/serviceTerm/port/out/LoadServiceTermPort.java b/get-p-application/src/main/java/es/princip/getp/application/serviceTerm/port/out/LoadServiceTermPort.java similarity index 100% rename from src/main/java/es/princip/getp/application/serviceTerm/port/out/LoadServiceTermPort.java rename to get-p-application/src/main/java/es/princip/getp/application/serviceTerm/port/out/LoadServiceTermPort.java diff --git a/src/main/java/es/princip/getp/application/serviceTerm/port/out/SaveServiceTermPort.java b/get-p-application/src/main/java/es/princip/getp/application/serviceTerm/port/out/SaveServiceTermPort.java similarity index 100% rename from src/main/java/es/princip/getp/application/serviceTerm/port/out/SaveServiceTermPort.java rename to get-p-application/src/main/java/es/princip/getp/application/serviceTerm/port/out/SaveServiceTermPort.java diff --git a/src/main/java/es/princip/getp/application/serviceTerm/service/ServiceTermService.java b/get-p-application/src/main/java/es/princip/getp/application/serviceTerm/service/ServiceTermService.java similarity index 78% rename from src/main/java/es/princip/getp/application/serviceTerm/service/ServiceTermService.java rename to get-p-application/src/main/java/es/princip/getp/application/serviceTerm/service/ServiceTermService.java index f565de90..eb82e27f 100644 --- a/src/main/java/es/princip/getp/application/serviceTerm/service/ServiceTermService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/serviceTerm/service/ServiceTermService.java @@ -1,6 +1,6 @@ package es.princip.getp.application.serviceTerm.service; -import es.princip.getp.api.controller.serviceTerm.dto.reqeust.ServiceTermRequest; +import es.princip.getp.application.serviceTerm.dto.command.ServiceTermCommand; import es.princip.getp.application.serviceTerm.exception.DuplicatedTagException; import es.princip.getp.application.serviceTerm.port.in.RegisterServiceTermUseCase; import es.princip.getp.application.serviceTerm.port.out.CheckServiceTermPort; @@ -21,12 +21,12 @@ public class ServiceTermService implements RegisterServiceTermUseCase { @Override @Transactional - public ServiceTerm register(final ServiceTermRequest request) { - final ServiceTermTag tag = ServiceTermTag.of(request.tag()); + public ServiceTerm register(final ServiceTermCommand command) { + final ServiceTermTag tag = ServiceTermTag.of(command.tag()); if (checkServiceTermPort.existsBy(tag)) { throw new DuplicatedTagException(); } - final ServiceTerm serviceTerm = new ServiceTerm(tag, request.required(), request.revocable()); + final ServiceTerm serviceTerm = new ServiceTerm(tag, command.required(), command.revocable()); saveServiceTermPort.save(serviceTerm); return serviceTerm; } diff --git a/src/main/java/es/princip/getp/application/storage/FileLog.java b/get-p-application/src/main/java/es/princip/getp/application/storage/FileLog.java similarity index 82% rename from src/main/java/es/princip/getp/application/storage/FileLog.java rename to get-p-application/src/main/java/es/princip/getp/application/storage/FileLog.java index 30277124..adee4ae8 100644 --- a/src/main/java/es/princip/getp/application/storage/FileLog.java +++ b/get-p-application/src/main/java/es/princip/getp/application/storage/FileLog.java @@ -1,5 +1,6 @@ package es.princip.getp.application.storage; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.support.BaseModel; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -16,13 +17,13 @@ public class FileLog extends BaseModel { private static final String FILE_PREFIX = "files"; private final Long id; - @NotNull private final Long memberId; + @NotNull private final MemberId memberId; @NotBlank private final String filename; private final LocalDateTime uploadedAt; public FileLog( final Long id, - final Long memberId, + final MemberId memberId, final String filename, final LocalDateTime uploadedAt ) { @@ -38,14 +39,14 @@ private static String convertFilename(final String original) { return original.replace(" ", "_"); } - public static FileLog of(final Long memberId, final MultipartFile file) { + public static FileLog of(final MemberId memberId, final MultipartFile file) { final String filename = file.getOriginalFilename(); assert filename != null; return new FileLog(null, memberId, convertFilename(filename), null); } public Path getPath() { - return Paths.get(String.valueOf(memberId)) + return Paths.get(String.valueOf(memberId.getValue())) .resolve(FILE_PREFIX) .resolve(String.valueOf(id)) .resolve(filename); diff --git a/src/main/java/es/princip/getp/application/storage/UploadFileService.java b/get-p-application/src/main/java/es/princip/getp/application/storage/UploadFileService.java similarity index 88% rename from src/main/java/es/princip/getp/application/storage/UploadFileService.java rename to get-p-application/src/main/java/es/princip/getp/application/storage/UploadFileService.java index b53c8556..239b3b87 100644 --- a/src/main/java/es/princip/getp/application/storage/UploadFileService.java +++ b/get-p-application/src/main/java/es/princip/getp/application/storage/UploadFileService.java @@ -1,11 +1,12 @@ package es.princip.getp.application.storage; -import es.princip.getp.application.storage.command.UploadFileCommand; +import es.princip.getp.application.storage.dto.command.UploadFileCommand; import es.princip.getp.application.storage.exception.NotSupportedExtensionException; import es.princip.getp.application.storage.port.in.UploadFileUseCase; import es.princip.getp.application.storage.port.out.LogFilePort; import es.princip.getp.application.storage.port.out.StoreFilePort; -import es.princip.getp.infrastructure.adapter.storage.exception.FailedFileSaveException; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.application.storage.exception.FailedFileSaveException; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -53,7 +54,7 @@ private void validateFile(final MultipartFile file) { @Override @Transactional public URI upload(final UploadFileCommand command) { - final Long memberId = command.memberId(); + final MemberId memberId = command.memberId(); final MultipartFile file = command.file(); validateFile(file); final FileLog fileLog = logFilePort.log(FileLog.of(memberId, file)); diff --git a/get-p-application/src/main/java/es/princip/getp/application/storage/dto/command/UploadFileCommand.java b/get-p-application/src/main/java/es/princip/getp/application/storage/dto/command/UploadFileCommand.java new file mode 100644 index 00000000..c717fbd0 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/storage/dto/command/UploadFileCommand.java @@ -0,0 +1,10 @@ +package es.princip.getp.application.storage.dto.command; + +import es.princip.getp.domain.member.model.MemberId; +import org.springframework.web.multipart.MultipartFile; + +public record UploadFileCommand( + MemberId memberId, + MultipartFile file +) { +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/storage/dto/response/UploadFileResponse.java b/get-p-application/src/main/java/es/princip/getp/application/storage/dto/response/UploadFileResponse.java new file mode 100644 index 00000000..e99e903c --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/storage/dto/response/UploadFileResponse.java @@ -0,0 +1,6 @@ +package es.princip.getp.application.storage.dto.response; + +import java.net.URI; + +public record UploadFileResponse(URI fileUri) { +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/infrastructure/adapter/storage/exception/FailedFileDeleteException.java b/get-p-application/src/main/java/es/princip/getp/application/storage/exception/FailedFileDeleteException.java similarity index 75% rename from src/main/java/es/princip/getp/infrastructure/adapter/storage/exception/FailedFileDeleteException.java rename to get-p-application/src/main/java/es/princip/getp/application/storage/exception/FailedFileDeleteException.java index fdbb8dbb..e00a8bd8 100644 --- a/src/main/java/es/princip/getp/infrastructure/adapter/storage/exception/FailedFileDeleteException.java +++ b/get-p-application/src/main/java/es/princip/getp/application/storage/exception/FailedFileDeleteException.java @@ -1,7 +1,6 @@ -package es.princip.getp.infrastructure.adapter.storage.exception; +package es.princip.getp.application.storage.exception; import es.princip.getp.domain.support.ErrorDescription; -import es.princip.getp.infrastructure.support.FileStorageException; public class FailedFileDeleteException extends FileStorageException { diff --git a/src/main/java/es/princip/getp/infrastructure/adapter/storage/exception/FailedFileSaveException.java b/get-p-application/src/main/java/es/princip/getp/application/storage/exception/FailedFileSaveException.java similarity index 75% rename from src/main/java/es/princip/getp/infrastructure/adapter/storage/exception/FailedFileSaveException.java rename to get-p-application/src/main/java/es/princip/getp/application/storage/exception/FailedFileSaveException.java index 0a3c3a6c..3521a9f4 100644 --- a/src/main/java/es/princip/getp/infrastructure/adapter/storage/exception/FailedFileSaveException.java +++ b/get-p-application/src/main/java/es/princip/getp/application/storage/exception/FailedFileSaveException.java @@ -1,7 +1,6 @@ -package es.princip.getp.infrastructure.adapter.storage.exception; +package es.princip.getp.application.storage.exception; import es.princip.getp.domain.support.ErrorDescription; -import es.princip.getp.infrastructure.support.FileStorageException; public class FailedFileSaveException extends FileStorageException { diff --git a/src/main/java/es/princip/getp/infrastructure/support/FileStorageException.java b/get-p-application/src/main/java/es/princip/getp/application/storage/exception/FileStorageException.java similarity index 86% rename from src/main/java/es/princip/getp/infrastructure/support/FileStorageException.java rename to get-p-application/src/main/java/es/princip/getp/application/storage/exception/FileStorageException.java index 51d305d9..a4c2b9f8 100644 --- a/src/main/java/es/princip/getp/infrastructure/support/FileStorageException.java +++ b/get-p-application/src/main/java/es/princip/getp/application/storage/exception/FileStorageException.java @@ -1,4 +1,4 @@ -package es.princip.getp.infrastructure.support; +package es.princip.getp.application.storage.exception; import es.princip.getp.domain.support.ErrorDescription; import es.princip.getp.domain.support.ErrorDescriptionException; diff --git a/src/main/java/es/princip/getp/application/storage/exception/NotSupportedExtensionException.java b/get-p-application/src/main/java/es/princip/getp/application/storage/exception/NotSupportedExtensionException.java similarity index 100% rename from src/main/java/es/princip/getp/application/storage/exception/NotSupportedExtensionException.java rename to get-p-application/src/main/java/es/princip/getp/application/storage/exception/NotSupportedExtensionException.java diff --git a/src/main/java/es/princip/getp/application/storage/port/in/UploadFileUseCase.java b/get-p-application/src/main/java/es/princip/getp/application/storage/port/in/UploadFileUseCase.java similarity index 68% rename from src/main/java/es/princip/getp/application/storage/port/in/UploadFileUseCase.java rename to get-p-application/src/main/java/es/princip/getp/application/storage/port/in/UploadFileUseCase.java index 750a085a..47e31ed6 100644 --- a/src/main/java/es/princip/getp/application/storage/port/in/UploadFileUseCase.java +++ b/get-p-application/src/main/java/es/princip/getp/application/storage/port/in/UploadFileUseCase.java @@ -1,6 +1,6 @@ package es.princip.getp.application.storage.port.in; -import es.princip.getp.application.storage.command.UploadFileCommand; +import es.princip.getp.application.storage.dto.command.UploadFileCommand; import java.net.URI; diff --git a/src/main/java/es/princip/getp/application/storage/port/out/DeleteFilePort.java b/get-p-application/src/main/java/es/princip/getp/application/storage/port/out/DeleteFilePort.java similarity index 100% rename from src/main/java/es/princip/getp/application/storage/port/out/DeleteFilePort.java rename to get-p-application/src/main/java/es/princip/getp/application/storage/port/out/DeleteFilePort.java diff --git a/src/main/java/es/princip/getp/application/storage/port/out/LogFilePort.java b/get-p-application/src/main/java/es/princip/getp/application/storage/port/out/LogFilePort.java similarity index 100% rename from src/main/java/es/princip/getp/application/storage/port/out/LogFilePort.java rename to get-p-application/src/main/java/es/princip/getp/application/storage/port/out/LogFilePort.java diff --git a/src/main/java/es/princip/getp/application/storage/port/out/StoreFilePort.java b/get-p-application/src/main/java/es/princip/getp/application/storage/port/out/StoreFilePort.java similarity index 100% rename from src/main/java/es/princip/getp/application/storage/port/out/StoreFilePort.java rename to get-p-application/src/main/java/es/princip/getp/application/storage/port/out/StoreFilePort.java diff --git a/src/main/java/es/princip/getp/application/support/ApplicationLogicException.java b/get-p-application/src/main/java/es/princip/getp/application/support/ApplicationLogicException.java similarity index 100% rename from src/main/java/es/princip/getp/application/support/ApplicationLogicException.java rename to get-p-application/src/main/java/es/princip/getp/application/support/ApplicationLogicException.java diff --git a/get-p-application/src/main/java/es/princip/getp/application/support/ApplicationQueryUtil.java b/get-p-application/src/main/java/es/princip/getp/application/support/ApplicationQueryUtil.java new file mode 100644 index 00000000..0a6722b0 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/support/ApplicationQueryUtil.java @@ -0,0 +1,12 @@ +package es.princip.getp.application.support; + +import es.princip.getp.domain.member.model.Member; + +public class ApplicationQueryUtil { + public static boolean isNotLogined(Member member) { + if (member == null) { + return true; + } + return false; + } +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/support/Cursor.java b/get-p-application/src/main/java/es/princip/getp/application/support/Cursor.java new file mode 100644 index 00000000..08832ef0 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/support/Cursor.java @@ -0,0 +1,18 @@ +package es.princip.getp.application.support; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@Getter +@EqualsAndHashCode +public class Cursor { + + private final Long id; + + @JsonCreator + public Cursor(@JsonProperty("id") final Long id) { + this.id = id; + } +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/support/CursorPageable.java b/get-p-application/src/main/java/es/princip/getp/application/support/CursorPageable.java new file mode 100644 index 00000000..6a1e9cea --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/support/CursorPageable.java @@ -0,0 +1,11 @@ +package es.princip.getp.application.support; + +import org.springframework.data.domain.Sort; + +public interface CursorPageable { + + int getPageSize(); + boolean hasCursor(); + T getCursor(); + Sort getSort(); +} diff --git a/src/main/java/es/princip/getp/application/support/ExternalServerException.java b/get-p-application/src/main/java/es/princip/getp/application/support/ExternalServerException.java similarity index 100% rename from src/main/java/es/princip/getp/application/support/ExternalServerException.java rename to get-p-application/src/main/java/es/princip/getp/application/support/ExternalServerException.java diff --git a/src/main/java/es/princip/getp/application/support/ForbiddenException.java b/get-p-application/src/main/java/es/princip/getp/application/support/ForbiddenException.java similarity index 100% rename from src/main/java/es/princip/getp/application/support/ForbiddenException.java rename to get-p-application/src/main/java/es/princip/getp/application/support/ForbiddenException.java diff --git a/get-p-application/src/main/java/es/princip/getp/application/support/MosaicFactory.java b/get-p-application/src/main/java/es/princip/getp/application/support/MosaicFactory.java new file mode 100644 index 00000000..87481e2a --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/support/MosaicFactory.java @@ -0,0 +1,23 @@ +package es.princip.getp.application.support; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class MosaicFactory { + + private final List> mosaicResolvers; + + @SuppressWarnings("unchecked") + public T mosaic(final T response) { + final Class clazz = response.getClass(); + final MosaicResolver resolver = (MosaicResolver) mosaicResolvers.stream() + .filter(res -> res.supports(clazz)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("해당 클래스에 대한 MosaicResolver가 없습니다.")); + return resolver.resolve(response); + } +} \ No newline at end of file diff --git a/get-p-application/src/main/java/es/princip/getp/application/support/MosaicResolver.java b/get-p-application/src/main/java/es/princip/getp/application/support/MosaicResolver.java new file mode 100644 index 00000000..309fec58 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/support/MosaicResolver.java @@ -0,0 +1,7 @@ +package es.princip.getp.application.support; + +public interface MosaicResolver { + + boolean supports(Class clazz); + T resolve(T response); +} diff --git a/get-p-application/src/main/java/es/princip/getp/application/support/MosaicResolverSupport.java b/get-p-application/src/main/java/es/princip/getp/application/support/MosaicResolverSupport.java new file mode 100644 index 00000000..6d067422 --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/support/MosaicResolverSupport.java @@ -0,0 +1,47 @@ +package es.princip.getp.application.support; + +import org.springframework.context.MessageSource; + +import java.util.List; +import java.util.Locale; + +public abstract class MosaicResolverSupport { + + private static final String RESTRICTED_ACCESS = "restricted.access"; + private final MessageSource messageSource; + + protected MosaicResolverSupport(final MessageSource messageSource) { + this.messageSource = messageSource; + } + + private String getRestrictedAccessMessage() { + return messageSource.getMessage(RESTRICTED_ACCESS, null, Locale.getDefault()); + } + + protected String mosaicMessage(final String original) { + if (original == null) { + return null; + } + + final int originalLength = original.length(); + final String message = getRestrictedAccessMessage(); + final int messageLength = message.length(); + + final StringBuilder sb = new StringBuilder(originalLength); + for (int i = 0; i < originalLength; i++) { + sb.append(message.charAt(i % messageLength)); + } + + return sb.toString(); + } + + protected List mosaicMessage(final List original) { + if (original == null) { + return null; + } + + return original.stream() + .map(this::mosaicMessage) + .toList(); + } +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/persistence/support/NotFoundException.java b/get-p-application/src/main/java/es/princip/getp/application/support/NotFoundException.java similarity index 88% rename from src/main/java/es/princip/getp/persistence/support/NotFoundException.java rename to get-p-application/src/main/java/es/princip/getp/application/support/NotFoundException.java index d39ca765..e1be8d46 100644 --- a/src/main/java/es/princip/getp/persistence/support/NotFoundException.java +++ b/get-p-application/src/main/java/es/princip/getp/application/support/NotFoundException.java @@ -1,4 +1,4 @@ -package es.princip.getp.persistence.support; +package es.princip.getp.application.support; import es.princip.getp.domain.support.ErrorDescription; import es.princip.getp.domain.support.ErrorDescriptionException; diff --git a/src/main/java/es/princip/getp/api/support/dto/PageResponse.java b/get-p-application/src/main/java/es/princip/getp/application/support/dto/PageResponse.java similarity index 97% rename from src/main/java/es/princip/getp/api/support/dto/PageResponse.java rename to get-p-application/src/main/java/es/princip/getp/application/support/dto/PageResponse.java index 8475ad70..6fa21520 100644 --- a/src/main/java/es/princip/getp/api/support/dto/PageResponse.java +++ b/get-p-application/src/main/java/es/princip/getp/application/support/dto/PageResponse.java @@ -1,4 +1,4 @@ -package es.princip.getp.api.support.dto; +package es.princip.getp.application.support.dto; import lombok.Builder; import lombok.Getter; diff --git a/get-p-application/src/main/java/es/princip/getp/application/support/dto/SliceResponse.java b/get-p-application/src/main/java/es/princip/getp/application/support/dto/SliceResponse.java new file mode 100644 index 00000000..6188b4db --- /dev/null +++ b/get-p-application/src/main/java/es/princip/getp/application/support/dto/SliceResponse.java @@ -0,0 +1,63 @@ +package es.princip.getp.application.support.dto; + +import lombok.Builder; +import lombok.Getter; +import org.springframework.data.domain.Slice; +import org.springframework.data.domain.Sort; + +import java.util.List; + +@Getter +public class SliceResponse { + + private final List content; + private final SliceInfo sliceInfo; + + private SliceResponse(final List content, final SliceInfo sliceInfo) { + this.content = content; + this.sliceInfo = sliceInfo; + } + + public static SliceResponse of(final Slice slice, final String cursor) { + SliceInfo pageInfo = SliceInfo.builder() + .size(slice.getSize()) + .numberOfElements(slice.getNumberOfElements()) + .first(slice.isFirst()) + .last(slice.isLast()) + .empty(slice.isEmpty()) + .sort(slice.getSort()) + .cursor(cursor) + .build(); + return new SliceResponse<>(slice.getContent(), pageInfo); + } + + @Getter + static class SliceInfo { + private final int size; + private final int numberOfElements; + private final boolean first; + private final boolean last; + private final boolean empty; + private final String cursor; + private final PageResponse.SortInfo sort; + + @Builder + public SliceInfo( + final int size, + final int numberOfElements, + final boolean first, + final boolean last, + final boolean empty, + final String cursor, + final Sort sort + ) { + this.size = size; + this.numberOfElements = numberOfElements; + this.first = first; + this.last = last; + this.empty = empty; + this.cursor = cursor; + this.sort = sort.stream().map(PageResponse.SortInfo::new).findFirst().orElse(null); + } + } +} diff --git a/src/main/java/es/princip/getp/util/StringUtil.java b/get-p-application/src/main/java/es/princip/getp/util/StringUtil.java similarity index 100% rename from src/main/java/es/princip/getp/util/StringUtil.java rename to get-p-application/src/main/java/es/princip/getp/util/StringUtil.java diff --git a/get-p-application/src/main/resources/app-config.yml b/get-p-application/src/main/resources/app-config.yml new file mode 100644 index 00000000..c145d139 --- /dev/null +++ b/get-p-application/src/main/resources/app-config.yml @@ -0,0 +1,34 @@ +spring: + mail: + host: smtp.gmail.com + port: ${GMAIL_PORT} + username: ${GMAIL_USERNAME} + password: ${GMAIL_PASSWORD} + properties: + mail: + smtp: + auth: true + starttls: + enable: true + required: true + connectiontimeout: 5000 + timeout: 5000 + writetimeout: 5000 + templates: + logo: classpath:/static/logo.png + + verification-code: + length: ${VERIFICATION_CODE_LENGTH} + expire-time: ${VERIFICATION_CODE_EXPIRE_TIME} + + jwt: + secret: ${JWT_SECRET} + access-token: + expire-time: ${JWT_ACCESS_TOKEN_EXPIRE_TIME} + refresh-token: + expire-time: ${JWT_REFRESH_TOKEN_EXPIRE_TIME} + teammate-approval-token: + expire-time: ${JWT_TEAMMATE_APPROVAL_TOKEN_EXPIRE_TIME} + + messages: + basename: messages/validation, messages/messages \ No newline at end of file diff --git a/src/main/resources/junit-platform.properties b/get-p-application/src/main/resources/junit-platform.properties similarity index 100% rename from src/main/resources/junit-platform.properties rename to get-p-application/src/main/resources/junit-platform.properties diff --git a/get-p-application/src/main/resources/messages/messages.properties b/get-p-application/src/main/resources/messages/messages.properties new file mode 100644 index 00000000..734c6dee --- /dev/null +++ b/get-p-application/src/main/resources/messages/messages.properties @@ -0,0 +1 @@ +restricted.access=\uB85C\uADF8\uC778 \uD6C4 \uC774\uC6A9\uD574\uC8FC\uC138\uC694. \ No newline at end of file diff --git a/src/main/resources/messages/validation.properties b/get-p-application/src/main/resources/messages/validation.properties similarity index 100% rename from src/main/resources/messages/validation.properties rename to get-p-application/src/main/resources/messages/validation.properties diff --git a/get-p-application/src/main/resources/templates/teammate-approval.html b/get-p-application/src/main/resources/templates/teammate-approval.html new file mode 100644 index 00000000..6ff6709b --- /dev/null +++ b/get-p-application/src/main/resources/templates/teammate-approval.html @@ -0,0 +1,65 @@ + + + + + + + +
+
+ +

+

프로젝트 지원에 동의하시면, 아래의 팀원 승인 버튼을 클릭해주세요.

+ 팀원 승인 +

본 신청은 5일 뒤에 만료됩니다. 본 메일을 타인에게 전달하거나 공유하지 마세요.

+
+
+ + diff --git a/src/test/java/es/princip/getp/application/auth/service/VerificationServiceTest.java b/get-p-application/src/test/java/es/princip/getp/application/auth/service/VerificationServiceTest.java similarity index 79% rename from src/test/java/es/princip/getp/application/auth/service/VerificationServiceTest.java rename to get-p-application/src/test/java/es/princip/getp/application/auth/service/VerificationServiceTest.java index 411d0603..346de97e 100644 --- a/src/test/java/es/princip/getp/application/auth/service/VerificationServiceTest.java +++ b/get-p-application/src/test/java/es/princip/getp/application/auth/service/VerificationServiceTest.java @@ -2,6 +2,7 @@ import es.princip.getp.application.auth.exception.IncorrectVerificationCodeException; import es.princip.getp.application.auth.exception.NotFoundVerificationException; +import es.princip.getp.domain.auth.EmailVerification; import es.princip.getp.domain.common.model.Email; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -12,13 +13,12 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import java.util.Optional; - import static es.princip.getp.fixture.auth.EmailVerificationFixture.emailVerification; import static es.princip.getp.fixture.common.EmailFixture.email; import static org.assertj.core.api.Assertions.assertThatCode; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) @@ -66,20 +66,22 @@ class VerifyEmail { @Test void verifyEmail() { final Email email = email(); - String verificationCode = "1234"; - EmailVerification emailVerification = emailVerification(email, verificationCode); - when(emailVerificationRepository.findById(email.getValue())).thenReturn(Optional.of(emailVerification)); + final String verificationCode = "1234"; + final EmailVerification emailVerification = emailVerification(email, verificationCode); + given(emailVerificationRepository.findById(email.getValue())) + .willReturn(emailVerification); emailVerificationService.verifyEmail(email, verificationCode); - verify(emailVerificationRepository, times(1)).delete(emailVerification); + verify(emailVerificationRepository, times(1)).deleteById(email.getValue()); } @DisplayName("유효하지 않은 이메일 인증인 경우 실패한다.") @Test void verifyEmail_WhenEmailVerificationIsInvalid_ShouldThrowException() { final Email email = email(); - when(emailVerificationRepository.findById(email.getValue())).thenReturn(Optional.empty()); + given(emailVerificationRepository.findById(email.getValue())) + .willThrow(NotFoundVerificationException.class); assertThatCode(() -> emailVerificationService.verifyEmail(email, "1234")) .isInstanceOf(NotFoundVerificationException.class); @@ -89,9 +91,10 @@ void verifyEmail_WhenEmailVerificationIsInvalid_ShouldThrowException() { @Test void verifyEmail_WhenVerificationCodeIsIncorrect_ShouldThrowException() { final Email email = email(); - String verificationCode = "5678"; - EmailVerification emailVerification = emailVerification(email, verificationCode); - when(emailVerificationRepository.findById(email.getValue())).thenReturn(Optional.of(emailVerification)); + final String verificationCode = "5678"; + final EmailVerification emailVerification = emailVerification(email, verificationCode); + given(emailVerificationRepository.findById(email.getValue())) + .willReturn(emailVerification); assertThatCode(() -> emailVerificationService.verifyEmail(email, "1234")) .isInstanceOf(IncorrectVerificationCodeException.class); diff --git a/get-p-application/src/test/java/es/princip/getp/application/common/fixture/AddressResponseFixture.java b/get-p-application/src/test/java/es/princip/getp/application/common/fixture/AddressResponseFixture.java new file mode 100644 index 00000000..75e61be7 --- /dev/null +++ b/get-p-application/src/test/java/es/princip/getp/application/common/fixture/AddressResponseFixture.java @@ -0,0 +1,18 @@ +package es.princip.getp.application.common.fixture; + +import es.princip.getp.application.common.dto.response.AddressResponse; +import es.princip.getp.domain.client.model.Address; + +import static es.princip.getp.fixture.client.AddressFixture.address; + +public class AddressResponseFixture { + + public static AddressResponse addressResponse() { + final Address address = address(); + return new AddressResponse( + address.getZipcode(), + address.getStreet(), + address.getDetail() + ); + } +} diff --git a/get-p-application/src/test/java/es/princip/getp/application/common/fixture/HashtagDtoFixture.java b/get-p-application/src/test/java/es/princip/getp/application/common/fixture/HashtagDtoFixture.java new file mode 100644 index 00000000..2fde09ed --- /dev/null +++ b/get-p-application/src/test/java/es/princip/getp/application/common/fixture/HashtagDtoFixture.java @@ -0,0 +1,22 @@ +package es.princip.getp.application.common.fixture; + +import es.princip.getp.domain.common.model.Hashtag; + +import java.util.List; + +import static es.princip.getp.fixture.common.HashtagFixture.hashtags; + +public class HashtagDtoFixture { + + public static List hashtagsRequest() { + return hashtags().stream() + .map(Hashtag::getValue) + .toList(); + } + + public static List hashtagsResponse() { + return hashtags().stream() + .map(Hashtag::getValue) + .toList(); + } +} diff --git a/get-p-application/src/test/java/es/princip/getp/application/common/fixture/TechStackDtoFixture.java b/get-p-application/src/test/java/es/princip/getp/application/common/fixture/TechStackDtoFixture.java new file mode 100644 index 00000000..ce9a83e4 --- /dev/null +++ b/get-p-application/src/test/java/es/princip/getp/application/common/fixture/TechStackDtoFixture.java @@ -0,0 +1,22 @@ +package es.princip.getp.application.common.fixture; + +import es.princip.getp.domain.common.model.TechStack; + +import java.util.List; + +import static es.princip.getp.fixture.common.TechStackFixture.techStacks; + +public class TechStackDtoFixture { + + public static List techStacksRequest() { + return techStacks().stream() + .map(TechStack::getValue) + .toList(); + } + + public static List techStacksResponse() { + return techStacks().stream() + .map(TechStack::getValue) + .toList(); + } +} diff --git a/get-p-application/src/test/java/es/princip/getp/application/like/people/service/LikePeopleMockingUtil.java b/get-p-application/src/test/java/es/princip/getp/application/like/people/service/LikePeopleMockingUtil.java new file mode 100644 index 00000000..ddd5d4b4 --- /dev/null +++ b/get-p-application/src/test/java/es/princip/getp/application/like/people/service/LikePeopleMockingUtil.java @@ -0,0 +1,28 @@ +package es.princip.getp.application.like.people.service; + +import es.princip.getp.application.like.people.port.out.CheckPeopleLikePort; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.model.PeopleId; + +import static org.mockito.BDDMockito.given; + +public class LikePeopleMockingUtil { + + static void mockMemberNeverLikedPeople( + final CheckPeopleLikePort checkPeopleLikePort, + final MemberId memberId, + final PeopleId peopleId + ) { + given(checkPeopleLikePort.existsBy(memberId, peopleId)) + .willReturn(false); + } + + static void mockMemberAlreadyLikedPeople( + final CheckPeopleLikePort checkPeopleLikePort, + final MemberId memberId, + final PeopleId peopleId + ) { + given(checkPeopleLikePort.existsBy(memberId, peopleId)) + .willReturn(true); + } +} diff --git a/src/test/java/es/princip/getp/application/like/people/service/LikePeopleServiceTest.java b/get-p-application/src/test/java/es/princip/getp/application/like/people/service/LikePeopleServiceTest.java similarity index 62% rename from src/test/java/es/princip/getp/application/like/people/service/LikePeopleServiceTest.java rename to get-p-application/src/test/java/es/princip/getp/application/like/people/service/LikePeopleServiceTest.java index 868822e8..6dfa0224 100644 --- a/src/test/java/es/princip/getp/application/like/people/service/LikePeopleServiceTest.java +++ b/get-p-application/src/test/java/es/princip/getp/application/like/people/service/LikePeopleServiceTest.java @@ -1,14 +1,12 @@ package es.princip.getp.application.like.people.service; -import es.princip.getp.application.client.port.out.LoadClientPort; import es.princip.getp.application.like.exception.AlreadyLikedException; import es.princip.getp.application.like.people.port.out.CheckPeopleLikePort; import es.princip.getp.application.like.people.port.out.SavePeopleLikePort; import es.princip.getp.application.people.port.out.LoadPeoplePort; -import es.princip.getp.domain.client.model.Client; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.people.model.People; -import es.princip.getp.domain.people.model.PeopleType; -import es.princip.getp.fixture.client.ClientFixture; +import es.princip.getp.domain.people.model.PeopleId; import es.princip.getp.fixture.people.PeopleFixture; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -17,52 +15,48 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import static es.princip.getp.application.like.people.service.LikePeopleMockingUtil.mockMemberAlreadyLikedPeople; +import static es.princip.getp.application.like.people.service.LikePeopleMockingUtil.mockMemberNeverLikedPeople; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) class LikePeopleServiceTest { @Mock private LoadPeoplePort loadPeoplePort; - @Mock private LoadClientPort loadClientPort; @Mock private SavePeopleLikePort savePeopleLikePort; @Mock private CheckPeopleLikePort checkPeopleLikePort; @InjectMocks private LikePeopleService likePeopleService; - private final Long clientId = 1L; - private final Long memberId = 1L; - private final Long peopleId = 1L; + private final MemberId memberId = new MemberId(1L); + private final PeopleId peopleId = new PeopleId(1L); - private final People people = PeopleFixture.people(memberId, PeopleType.INDIVIDUAL); + private final People people = PeopleFixture.people(memberId); @BeforeEach void setUp() { - given(loadPeoplePort.loadByPeopleId(peopleId)).willReturn(people); - Client client = spy(ClientFixture.client(clientId)); - doReturn(clientId).when(client).getClientId(); - given(loadClientPort.loadBy(memberId)).willReturn(client); + given(loadPeoplePort.loadBy(peopleId)).willReturn(people); } @Test void 의뢰자는_피플에_좋아요를_누를_수_있다() { - given(checkPeopleLikePort.existsBy(clientId, peopleId)) - .willReturn(false); + mockMemberNeverLikedPeople(checkPeopleLikePort, memberId, peopleId); likePeopleService.like(memberId, peopleId); verify(savePeopleLikePort, times(1)).save( argThat( - arg-> arg.getClientId().equals(clientId) && arg.getPeopleId().equals(peopleId) + arg-> arg.getMemberId().equals(memberId) && arg.getPeopleId().equals(peopleId) ) ); } @Test void 의뢰자는_피플에_좋아요를_중복으로_누를_수_없다() { - given(checkPeopleLikePort.existsBy(clientId, peopleId)) - .willReturn(true); + mockMemberAlreadyLikedPeople(checkPeopleLikePort, memberId, peopleId); assertThatThrownBy(() -> likePeopleService.like(memberId, peopleId)) .isInstanceOf(AlreadyLikedException.class); diff --git a/src/test/java/es/princip/getp/application/like/people/service/UnlikePeopleServiceTest.java b/get-p-application/src/test/java/es/princip/getp/application/like/people/service/UnlikePeopleServiceTest.java similarity index 65% rename from src/test/java/es/princip/getp/application/like/people/service/UnlikePeopleServiceTest.java rename to get-p-application/src/test/java/es/princip/getp/application/like/people/service/UnlikePeopleServiceTest.java index dd51dfa6..2e9c3bf8 100644 --- a/src/test/java/es/princip/getp/application/like/people/service/UnlikePeopleServiceTest.java +++ b/get-p-application/src/test/java/es/princip/getp/application/like/people/service/UnlikePeopleServiceTest.java @@ -1,16 +1,14 @@ package es.princip.getp.application.like.people.service; -import es.princip.getp.application.client.port.out.LoadClientPort; import es.princip.getp.application.like.exception.NeverLikedException; import es.princip.getp.application.like.people.port.out.CheckPeopleLikePort; import es.princip.getp.application.like.people.port.out.DeletePeopleLikePort; import es.princip.getp.application.like.people.port.out.LoadPeopleLikePort; import es.princip.getp.application.people.port.out.LoadPeoplePort; -import es.princip.getp.domain.client.model.Client; import es.princip.getp.domain.like.people.model.PeopleLike; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.people.model.People; -import es.princip.getp.domain.people.model.PeopleType; -import es.princip.getp.fixture.client.ClientFixture; +import es.princip.getp.domain.people.model.PeopleId; import es.princip.getp.fixture.like.PeopleLikeFixture; import es.princip.getp.fixture.people.PeopleFixture; import org.junit.jupiter.api.BeforeEach; @@ -20,43 +18,39 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import static es.princip.getp.application.like.people.service.LikePeopleMockingUtil.mockMemberAlreadyLikedPeople; +import static es.princip.getp.application.like.people.service.LikePeopleMockingUtil.mockMemberNeverLikedPeople; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) class UnlikePeopleServiceTest { @Mock private LoadPeoplePort loadPeoplePort; - @Mock private LoadClientPort loadClientPort; @Mock private LoadPeopleLikePort loadPeopleLikePort; @Mock private CheckPeopleLikePort checkPeopleLikePort; @Mock private DeletePeopleLikePort deletePeopleLikePort; @InjectMocks private UnlikePeopleService likePeopleService; - private final Long clientId = 1L; - private final Long memberId = 1L; - private final Long peopleId = 1L; + private final MemberId memberId = new MemberId(1L); + private final PeopleId peopleId = new PeopleId(1L); - private final People people = PeopleFixture.people(memberId, PeopleType.INDIVIDUAL); - private final PeopleLike like = PeopleLikeFixture.peopleLike(clientId, peopleId); + private final People people = PeopleFixture.people(memberId); + private final PeopleLike like = PeopleLikeFixture.peopleLike(memberId, peopleId); @BeforeEach void setUp() { - given(loadPeoplePort.loadByPeopleId(peopleId)).willReturn(people); - Client client = spy(ClientFixture.client(clientId)); - doReturn(clientId).when(client).getClientId(); - given(loadClientPort.loadBy(memberId)).willReturn(client); + given(loadPeoplePort.loadBy(peopleId)).willReturn(people); } @Test void 의뢰자는_피플에_눌렀던_좋아요를_취소할_수_있다() { - given(checkPeopleLikePort.existsBy(clientId, peopleId)) - .willReturn(true); - given(loadPeopleLikePort.loadBy(clientId, peopleId)) + mockMemberAlreadyLikedPeople(checkPeopleLikePort, memberId, peopleId); + given(loadPeopleLikePort.loadBy(memberId, peopleId)) .willReturn(like); - likePeopleService.unlike(memberId, peopleId); verify(deletePeopleLikePort, times(1)).delete(like); @@ -64,8 +58,7 @@ void setUp() { @Test void 의뢰자는_좋아요를_누른적이_없는_피플에_좋아요를_취소할_수_없다() { - given(checkPeopleLikePort.existsBy(clientId, peopleId)) - .willReturn(false); + mockMemberNeverLikedPeople(checkPeopleLikePort, memberId, peopleId); assertThatThrownBy(() -> likePeopleService.unlike(memberId, peopleId)) .isInstanceOf(NeverLikedException.class); diff --git a/get-p-application/src/test/java/es/princip/getp/application/like/project/service/LikeProjectMockingUtil.java b/get-p-application/src/test/java/es/princip/getp/application/like/project/service/LikeProjectMockingUtil.java new file mode 100644 index 00000000..ec1158ad --- /dev/null +++ b/get-p-application/src/test/java/es/princip/getp/application/like/project/service/LikeProjectMockingUtil.java @@ -0,0 +1,28 @@ +package es.princip.getp.application.like.project.service; + +import es.princip.getp.application.like.project.port.out.CheckProjectLikePort; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.project.commission.model.ProjectId; + +import static org.mockito.BDDMockito.given; + +public class LikeProjectMockingUtil { + + static void mockMemberNeverLikedProject( + final CheckProjectLikePort checkProjectLikePort, + final MemberId memberId, + final ProjectId projectId + ) { + given(checkProjectLikePort.existsBy(memberId, projectId)) + .willReturn(false); + } + + static void mockMemberAlreadyLikedProject( + final CheckProjectLikePort checkProjectLikePort, + final MemberId memberId, + final ProjectId projectId + ) { + given(checkProjectLikePort.existsBy(memberId, projectId)) + .willReturn(true); + } +} diff --git a/src/test/java/es/princip/getp/application/like/project/service/LikeProjectServiceTest.java b/get-p-application/src/test/java/es/princip/getp/application/like/project/service/LikeProjectServiceTest.java similarity index 67% rename from src/test/java/es/princip/getp/application/like/project/service/LikeProjectServiceTest.java rename to get-p-application/src/test/java/es/princip/getp/application/like/project/service/LikeProjectServiceTest.java index 1db8189e..8cc4dc28 100644 --- a/src/test/java/es/princip/getp/application/like/project/service/LikeProjectServiceTest.java +++ b/get-p-application/src/test/java/es/princip/getp/application/like/project/service/LikeProjectServiceTest.java @@ -3,13 +3,12 @@ import es.princip.getp.application.like.exception.AlreadyLikedException; import es.princip.getp.application.like.project.port.out.CheckProjectLikePort; import es.princip.getp.application.like.project.port.out.SaveProjectLikePort; -import es.princip.getp.application.people.port.out.LoadPeoplePort; import es.princip.getp.application.project.commission.port.out.LoadProjectPort; -import es.princip.getp.domain.people.model.People; -import es.princip.getp.domain.people.model.PeopleType; +import es.princip.getp.domain.client.model.ClientId; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.project.commission.model.Project; +import es.princip.getp.domain.project.commission.model.ProjectId; import es.princip.getp.domain.project.commission.model.ProjectStatus; -import es.princip.getp.fixture.people.PeopleFixture; import es.princip.getp.fixture.project.ProjectFixture; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -18,52 +17,48 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import static es.princip.getp.application.like.project.service.LikeProjectMockingUtil.mockMemberAlreadyLikedProject; +import static es.princip.getp.application.like.project.service.LikeProjectMockingUtil.mockMemberNeverLikedProject; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) public class LikeProjectServiceTest { - @Mock private LoadPeoplePort loadPeoplePort; @Mock private LoadProjectPort loadProjectPort; @Mock private CheckProjectLikePort checkProjectLikePort; @Mock private SaveProjectLikePort saveProjectLikePort; @InjectMocks private LikeProjectService likeProjectService; - private final Long memberId = 1L; - private final Long peopleId = 1L; - private final Long clientId = 1L; - private final Long projectId = 1L; - private final Project project = ProjectFixture.project(clientId, ProjectStatus.APPLYING); + private final MemberId memberId = new MemberId(1L); + private final ClientId clientId = new ClientId(1L); + private final ProjectId projectId = new ProjectId(1L); + private final Project project = ProjectFixture.project(clientId, ProjectStatus.APPLICATION_OPENED); @BeforeEach void setUp() { given(loadProjectPort.loadBy(projectId)).willReturn(project); - final People people = spy(PeopleFixture.people(memberId, PeopleType.INDIVIDUAL)); - doReturn(peopleId).when(people).getId(); - given(loadPeoplePort.loadBy(memberId)).willReturn(people); } @Test void 피플은_프로젝트에_좋아요를_누를_수_있다() { - given(checkProjectLikePort.existsBy(peopleId, projectId)) - .willReturn(false); + mockMemberNeverLikedProject(checkProjectLikePort, memberId, projectId); likeProjectService.like(memberId, projectId); verify(saveProjectLikePort, times(1)).save( argThat( - arg -> arg.getPeopleId().equals(peopleId) && arg.getProjectId().equals(projectId) + arg -> arg.getMemberId().equals(memberId) && arg.getProjectId().equals(projectId) ) ); } @Test void 피플은_프로젝트에_좋아요를_중복으로_누를_수_없다() { - given(checkProjectLikePort.existsBy(peopleId, projectId)) - .willReturn(true); + mockMemberAlreadyLikedProject(checkProjectLikePort, memberId, projectId); assertThatThrownBy(() -> likeProjectService.like(memberId, projectId)) .isInstanceOf(AlreadyLikedException.class); diff --git a/src/test/java/es/princip/getp/application/like/project/service/UnlikeProjectServiceTest.java b/get-p-application/src/test/java/es/princip/getp/application/like/project/service/UnlikeProjectServiceTest.java similarity index 68% rename from src/test/java/es/princip/getp/application/like/project/service/UnlikeProjectServiceTest.java rename to get-p-application/src/test/java/es/princip/getp/application/like/project/service/UnlikeProjectServiceTest.java index be57062e..8f58d805 100644 --- a/src/test/java/es/princip/getp/application/like/project/service/UnlikeProjectServiceTest.java +++ b/get-p-application/src/test/java/es/princip/getp/application/like/project/service/UnlikeProjectServiceTest.java @@ -4,15 +4,14 @@ import es.princip.getp.application.like.project.port.out.CheckProjectLikePort; import es.princip.getp.application.like.project.port.out.DeleteProjectLikePort; import es.princip.getp.application.like.project.port.out.LoadProjectLikePort; -import es.princip.getp.application.people.port.out.LoadPeoplePort; import es.princip.getp.application.project.commission.port.out.LoadProjectPort; +import es.princip.getp.domain.client.model.ClientId; import es.princip.getp.domain.like.project.model.ProjectLike; -import es.princip.getp.domain.people.model.People; -import es.princip.getp.domain.people.model.PeopleType; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.project.commission.model.Project; +import es.princip.getp.domain.project.commission.model.ProjectId; import es.princip.getp.domain.project.commission.model.ProjectStatus; import es.princip.getp.fixture.like.ProjectLikeFixture; -import es.princip.getp.fixture.people.PeopleFixture; import es.princip.getp.fixture.project.ProjectFixture; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -21,40 +20,38 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import static es.princip.getp.application.like.project.service.LikeProjectMockingUtil.mockMemberAlreadyLikedProject; +import static es.princip.getp.application.like.project.service.LikeProjectMockingUtil.mockMemberNeverLikedProject; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) -public class UnlikeProjectServiceTest { +class UnlikeProjectServiceTest { - @Mock private LoadPeoplePort loadPeoplePort; @Mock private LoadProjectPort loadProjectPort; @Mock private LoadProjectLikePort loadProjectLikePort; @Mock private CheckProjectLikePort checkProjectLikePort; @Mock private DeleteProjectLikePort deleteProjectLikePort; @InjectMocks private UnlikeProjectService unlikeProjectService; - private final Long memberId = 1L; - private final Long peopleId = 1L; - private final Long clientId = 1L; - private final Long projectId = 1L; - private final Project project = ProjectFixture.project(clientId, ProjectStatus.APPLYING); - private final ProjectLike like = ProjectLikeFixture.projectLike(peopleId, projectId); + private final MemberId memberId = new MemberId(1L); + private final ClientId clientId = new ClientId(1L); + private final ProjectId projectId = new ProjectId(1L); + + private final Project project = ProjectFixture.project(clientId, ProjectStatus.APPLICATION_OPENED); + private final ProjectLike like = ProjectLikeFixture.projectLike(memberId, projectId); @BeforeEach void setUp() { given(loadProjectPort.loadBy(projectId)).willReturn(project); - final People people = spy(PeopleFixture.people(memberId, PeopleType.INDIVIDUAL)); - doReturn(peopleId).when(people).getId(); - given(loadPeoplePort.loadBy(memberId)).willReturn(people); } @Test void 피플은_프로젝트에_눌렀던_좋아요를_취소할_수_있다() { - given(checkProjectLikePort.existsBy(peopleId, projectId)) - .willReturn(true); - given(loadProjectLikePort.loadBy(peopleId, projectId)) + mockMemberAlreadyLikedProject(checkProjectLikePort, memberId, projectId); + given(loadProjectLikePort.loadBy(memberId, projectId)) .willReturn(like); unlikeProjectService.unlike(memberId, projectId); @@ -64,8 +61,7 @@ void setUp() { @Test void 피플은_좋아요를_누른적이_없는_프로젝트에_좋아요를_취소할_수_없다() { - given(checkProjectLikePort.existsBy(peopleId, projectId)) - .willReturn(false); + mockMemberNeverLikedProject(checkProjectLikePort, memberId, projectId); assertThatThrownBy(() -> unlikeProjectService.unlike(memberId, projectId)) .isInstanceOf(NeverLikedException.class); diff --git a/get-p-application/src/test/java/es/princip/getp/application/people/fixture/PortfolioResponseFixture.java b/get-p-application/src/test/java/es/princip/getp/application/people/fixture/PortfolioResponseFixture.java new file mode 100644 index 00000000..e4cd61de --- /dev/null +++ b/get-p-application/src/test/java/es/princip/getp/application/people/fixture/PortfolioResponseFixture.java @@ -0,0 +1,19 @@ +package es.princip.getp.application.people.fixture; + +import es.princip.getp.application.people.dto.response.peopleProfile.PortfolioResponse; + +import java.util.List; + +import static es.princip.getp.fixture.people.PortfolioFixture.portfolios; + +public class PortfolioResponseFixture { + + public static List portfolioResponses() { + return portfolios().stream() + .map(portfolio -> new PortfolioResponse( + portfolio.getDescription(), + portfolio.getUrl().getValue() + )) + .toList(); + } +} diff --git a/get-p-application/src/test/java/es/princip/getp/application/people/service/PeopleDetailResponseMosaicResolverTest.java b/get-p-application/src/test/java/es/princip/getp/application/people/service/PeopleDetailResponseMosaicResolverTest.java new file mode 100644 index 00000000..38616965 --- /dev/null +++ b/get-p-application/src/test/java/es/princip/getp/application/people/service/PeopleDetailResponseMosaicResolverTest.java @@ -0,0 +1,108 @@ +package es.princip.getp.application.people.service; + +import es.princip.getp.application.people.dto.response.people.PeopleDetailResponse; +import es.princip.getp.application.people.dto.response.peopleProfile.PeopleProfileDetailResponse; +import es.princip.getp.application.people.dto.response.peopleProfile.PortfolioResponse; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.model.Education; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.context.MessageSource; + +import java.util.List; +import java.util.Locale; + +import static es.princip.getp.application.common.fixture.HashtagDtoFixture.hashtagsResponse; +import static es.princip.getp.application.people.fixture.PortfolioResponseFixture.portfolioResponses; +import static es.princip.getp.application.common.fixture.TechStackDtoFixture.techStacksResponse; +import static es.princip.getp.fixture.member.NicknameFixture.NICKNAME; +import static es.princip.getp.fixture.member.ProfileImageFixture.profileImage; +import static es.princip.getp.fixture.people.ActivityAreaFixture.activityArea; +import static es.princip.getp.fixture.people.EducationFixture.education; +import static es.princip.getp.fixture.people.IntroductionFixture.introduction; +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.mockito.BDDMockito.given; + +@ExtendWith(MockitoExtension.class) +class PeopleDetailResponseMosaicResolverTest { + + @Mock private MessageSource messageSource; + + @InjectMocks private PeopleDetailResponseMosaicResolver resolver; + + private static final String MESSAGE = "로그인 후 이용해주세요."; + private final MemberId memberId = new MemberId(1L); + + private final PeopleDetailResponse response = new PeopleDetailResponse( + 1L, + NICKNAME, + profileImage(memberId).getUrl(), + 0, + 0, + true, + new PeopleProfileDetailResponse( + introduction(), + activityArea(), + education(), + techStacksResponse(), + hashtagsResponse(), + portfolioResponses() + ) + ); + + @BeforeEach + void setup() { + given(messageSource.getMessage("restricted.access", null, Locale.getDefault())) + .willReturn(MESSAGE); + } + + private void assertEducation(Education actual, Education expected) { + assertSoftly(education -> { + education.assertThat(actual.getMajor()) + .hasSameSizeAs(expected.getMajor()); + education.assertThat(actual.getSchool()) + .hasSameSizeAs(expected.getSchool()); + }); + } + + private void assertPortfolios(List actual, List expected) { + assertSoftly(portfolios -> { + portfolios.assertThat(actual) + .extracting(portfolio -> portfolio.description().length()) + .containsExactlyElementsOf(expected + .stream() + .map(portfolio -> portfolio.description().length()) + .toList()); + portfolios.assertThat(actual) + .extracting(portfolio -> portfolio.url().length()) + .containsExactlyElementsOf(expected + .stream() + .map(portfolio -> portfolio.url().length()) + .toList()); + }); + } + + @Test + void 피플_상세_정보를_모자이크_한다() { + final PeopleDetailResponse mosaicResponse = resolver.resolve(response); + + assertSoftly(peopleProfile -> { + peopleProfile.assertThat(mosaicResponse.getProfile().getIntroduction()) + .hasSameSizeAs(response.getProfile().getIntroduction()); + peopleProfile.assertThat(mosaicResponse.getProfile().getActivityArea()) + .hasSameSizeAs(response.getProfile().getActivityArea()); + peopleProfile.assertThat(mosaicResponse.getProfile().getTechStacks()) + .extracting(String::length) + .containsExactlyElementsOf(response.getProfile().getTechStacks() + .stream() + .map(String::length) + .toList()); + assertEducation(mosaicResponse.getProfile().getEducation(), response.getProfile().getEducation()); + assertPortfolios(mosaicResponse.getProfile().getPortfolios(), response.getProfile().getPortfolios()); + }); + } +} \ No newline at end of file diff --git a/get-p-application/src/test/java/es/princip/getp/application/project/commission/ProjectDetailResponseFixture.java b/get-p-application/src/test/java/es/princip/getp/application/project/commission/ProjectDetailResponseFixture.java new file mode 100644 index 00000000..32ba550f --- /dev/null +++ b/get-p-application/src/test/java/es/princip/getp/application/project/commission/ProjectDetailResponseFixture.java @@ -0,0 +1,54 @@ +package es.princip.getp.application.project.commission; + +import es.princip.getp.application.project.commission.dto.response.ProjectClientResponse; +import es.princip.getp.application.project.commission.dto.response.ProjectDetailResponse; +import es.princip.getp.domain.common.model.Duration; +import es.princip.getp.domain.project.commission.model.MeetingType; +import es.princip.getp.domain.project.commission.model.ProjectCategory; +import es.princip.getp.domain.project.commission.model.ProjectId; +import es.princip.getp.domain.project.commission.model.ProjectStatus; + +import java.time.LocalDate; +import java.util.List; + +import static es.princip.getp.application.common.fixture.HashtagDtoFixture.hashtagsResponse; +import static es.princip.getp.application.common.fixture.AddressResponseFixture.addressResponse; +import static es.princip.getp.fixture.member.NicknameFixture.NICKNAME; +import static es.princip.getp.fixture.project.ProjectFixture.*; + +public class ProjectDetailResponseFixture { + + public static ProjectDetailResponse projectDetailResponse(final ProjectId projectId) { + return new ProjectDetailResponse( + projectId.getValue(), + TITLE, + PAYMENT, + RECRUITMENT_COUNT, + 5L, + Duration.of( + LocalDate.of(2024, 7, 1), + LocalDate.of(2024, 7, 7) + ), + Duration.of( + LocalDate.of(2024, 7, 14), + LocalDate.of(2024, 7, 21) + ), + DESCRIPTION, + MeetingType.IN_PERSON, + ProjectCategory.BACKEND, + ProjectStatus.APPLICATION_OPENED, + List.of( + "https://example.com/attachment1", + "https://example.com/attachment2" + ), + hashtagsResponse(), + 5L, + true, + new ProjectClientResponse( + 1L, + NICKNAME, + addressResponse() + ) + ); + } +} diff --git a/get-p-application/src/test/java/es/princip/getp/application/project/commission/ProjectDetailResponseMosaicResolverTest.java b/get-p-application/src/test/java/es/princip/getp/application/project/commission/ProjectDetailResponseMosaicResolverTest.java new file mode 100644 index 00000000..32ab2ebc --- /dev/null +++ b/get-p-application/src/test/java/es/princip/getp/application/project/commission/ProjectDetailResponseMosaicResolverTest.java @@ -0,0 +1,63 @@ +package es.princip.getp.application.project.commission; + +import es.princip.getp.application.project.commission.dto.response.ProjectClientResponse; +import es.princip.getp.application.project.commission.dto.response.ProjectDetailResponse; +import es.princip.getp.domain.project.commission.model.ProjectId; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.context.MessageSource; + +import java.util.Locale; + +import static es.princip.getp.application.project.commission.ProjectDetailResponseFixture.projectDetailResponse; +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.mockito.BDDMockito.given; + +@ExtendWith(MockitoExtension.class) +class ProjectDetailResponseMosaicResolverTest { + + @Mock private MessageSource messageSource; + @InjectMocks private ProjectDetailResponseMosaicResolver resolver; + + private static final String MESSAGE = "로그인 후 이용해주세요."; + private final ProjectId projectId = new ProjectId(1L); + private final ProjectDetailResponse response = projectDetailResponse(projectId); + + @BeforeEach + void setup() { + given(messageSource.getMessage("restricted.access", null, Locale.getDefault())) + .willReturn(MESSAGE); + } + + private void assertClientResponse(ProjectClientResponse actual, ProjectClientResponse expected) { + assertSoftly(client -> { + client.assertThat(actual.clientId()).isEqualTo(null); + client.assertThat(actual.nickname()).hasSameSizeAs(expected.nickname()); + assertSoftly(address -> { + address.assertThat(actual.address().zipcode()).hasSameSizeAs(expected.address().zipcode()); + address.assertThat(actual.address().detail()).hasSameSizeAs(expected.address().detail()); + address.assertThat(actual.address().street()).hasSameSizeAs(expected.address().street()); + }); + }); + } + + @Test + void 프로젝트_상세_정보를_모자이크_한다() { + final ProjectDetailResponse mosaicResponse = resolver.resolve(response); + + assertSoftly(projectDetailResponse -> { + projectDetailResponse.assertThat(mosaicResponse.getDescription()).hasSameSizeAs(response.getDescription()); + projectDetailResponse.assertThat(mosaicResponse.getAttachmentFiles()) + .extracting(String::length) + .containsExactlyElementsOf(response.getAttachmentFiles() + .stream() + .map(String::length) + .toList()); + assertClientResponse(mosaicResponse.getClient(), response.getClient()); + }); + } +} diff --git a/src/test/java/es/princip/getp/application/project/meeting/ProjectMeetingServiceTest.java b/get-p-application/src/test/java/es/princip/getp/application/project/meeting/ProjectMeetingServiceTest.java similarity index 58% rename from src/test/java/es/princip/getp/application/project/meeting/ProjectMeetingServiceTest.java rename to get-p-application/src/test/java/es/princip/getp/application/project/meeting/ProjectMeetingServiceTest.java index ca7cbb16..fcf89bf9 100644 --- a/src/test/java/es/princip/getp/application/project/meeting/ProjectMeetingServiceTest.java +++ b/get-p-application/src/test/java/es/princip/getp/application/project/meeting/ProjectMeetingServiceTest.java @@ -1,18 +1,23 @@ package es.princip.getp.application.project.meeting; +import es.princip.getp.application.client.port.out.LoadClientPort; import es.princip.getp.application.people.port.out.LoadPeoplePort; import es.princip.getp.application.project.apply.port.out.CheckProjectApplicationPort; import es.princip.getp.application.project.commission.port.out.LoadProjectPort; -import es.princip.getp.application.project.meeting.command.ScheduleMeetingCommand; +import es.princip.getp.application.project.meeting.dto.command.ScheduleMeetingCommand; import es.princip.getp.application.project.meeting.exception.NotApplicantException; import es.princip.getp.application.project.meeting.exception.NotClientOfProjectException; -import es.princip.getp.application.project.meeting.port.out.CheckProjectMeetingPort; import es.princip.getp.application.project.meeting.port.out.SaveProjectMeetingPort; +import es.princip.getp.domain.client.model.Client; +import es.princip.getp.domain.client.model.ClientId; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.people.model.People; -import es.princip.getp.domain.people.model.PeopleType; +import es.princip.getp.domain.people.model.PeopleId; import es.princip.getp.domain.project.commission.model.Project; +import es.princip.getp.domain.project.commission.model.ProjectId; import es.princip.getp.domain.project.commission.model.ProjectStatus; import es.princip.getp.domain.project.meeting.model.ProjectMeeting; +import es.princip.getp.fixture.client.ClientFixture; import es.princip.getp.fixture.people.PeopleFixture; import es.princip.getp.fixture.project.ProjectFixture; import org.junit.jupiter.api.BeforeEach; @@ -28,46 +33,46 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class ProjectMeetingServiceTest { - @Mock private CheckProjectApplicationPort checkProjectApplicationPort; @Mock private LoadPeoplePort loadPeoplePort; - + @Mock private LoadClientPort loadClientPort; @Mock private LoadProjectPort loadProjectPort; + @Mock private CheckProjectApplicationPort checkProjectApplicationPort; @Mock private SaveProjectMeetingPort saveProjectMeetingPort; - @Mock private CheckProjectMeetingPort checkProjectMeetingPort; - @Mock private MeetingSender meetingSender; @InjectMocks private ProjectMeetingService projectMeetingService; - private final Long memberId = 1L; - private final Long projectId = 1L; - private final Long applicantId = 1L; - private final Long meetingId = 1L; - private final Project project = ProjectFixture.project(applicantId, ProjectStatus.APPLYING); - private final People people = PeopleFixture.people(memberId, PeopleType.INDIVIDUAL); + private final MemberId pmemberId = new MemberId(1L); // 지원자의 회원 ID + private final PeopleId applicantId = new PeopleId(1L); // 지원자의 피플 ID + + private final MemberId cmemberId = new MemberId(2L); // 의뢰자의 회원 ID + private final ClientId clientId = new ClientId(1L); // 지원자가 지원한 프로젝트의 의뢰자의 의뢰자 ID + private final ProjectId projectId = new ProjectId(1L);// 지원자가 지원한 프로젝트의 프로젝트 ID + + private final Long meetingId = 1L; // 신청된 미팅의 미팅 ID + + private final Project project = ProjectFixture.project(clientId, ProjectStatus.APPLICATION_OPENED); + private final People people = PeopleFixture.people(pmemberId); + + private final ScheduleMeetingCommand command = scheduleMeetingCommand(cmemberId, projectId, applicantId); @BeforeEach void setUp() { - given(loadPeoplePort.loadBy(memberId)).willReturn(people); + given(loadPeoplePort.loadBy(applicantId)).willReturn(people); given(loadProjectPort.loadBy(projectId)).willReturn(project); } @Test void 의뢰자는_프로젝트_지원자에게_미팅을_신청할_수_있다() { - given(checkProjectMeetingPort.existsApplicantByProjectIdAndMemberId(projectId, memberId)) - .willReturn(true); - given(checkProjectApplicationPort.existsBy(applicantId, projectId)) - .willReturn(true); + mockClientIsClientOfProject(); + mockPeopleIsApplicant(); given(saveProjectMeetingPort.save(any(ProjectMeeting.class))) .willReturn(meetingId); - - final ScheduleMeetingCommand command = scheduleMeetingCommand(memberId, projectId, applicantId); final Long meetingId = projectMeetingService.scheduleMeeting(command); @@ -79,10 +84,7 @@ void setUp() { @Test void 의뢰자는_자신이_의뢰한_프로젝트가_아니면_미팅을_신청할_수_없다() { - given(checkProjectMeetingPort.existsApplicantByProjectIdAndMemberId(projectId, memberId)) - .willReturn(false); - - final ScheduleMeetingCommand command = scheduleMeetingCommand(memberId, projectId, applicantId); + mockClientIsNotClientOfProject(); assertThatThrownBy(() -> projectMeetingService.scheduleMeeting(command)) .isInstanceOf(NotClientOfProjectException.class); @@ -90,14 +92,32 @@ void setUp() { @Test void 의뢰자는_프로젝트_지원자가_아닌_피플에게_미팅을_신청할_수_없다() { - given(checkProjectMeetingPort.existsApplicantByProjectIdAndMemberId(projectId, memberId)) + mockClientIsClientOfProject(); + mockPeopleIsNotApplicant(); + + assertThatThrownBy(() -> projectMeetingService.scheduleMeeting(command)) + .isInstanceOf(NotApplicantException.class); + } + + private void mockClientIsClientOfProject() { + final Client client = spy(ClientFixture.client(cmemberId)); + given(client.getId()).willReturn(clientId); + given(loadClientPort.loadBy(cmemberId)).willReturn(client); + } + + private void mockClientIsNotClientOfProject() { + final Client client = spy(ClientFixture.client(cmemberId)); + given(client.getId()).willReturn(new ClientId(2L)); + given(loadClientPort.loadBy(cmemberId)).willReturn(client); + } + + private void mockPeopleIsApplicant() { + given(checkProjectApplicationPort.existsBy(applicantId, projectId)) .willReturn(true); + } + + private void mockPeopleIsNotApplicant() { given(checkProjectApplicationPort.existsBy(applicantId, projectId)) .willReturn(false); - - final ScheduleMeetingCommand command = scheduleMeetingCommand(memberId, projectId, applicantId); - - assertThatThrownBy(() -> projectMeetingService.scheduleMeeting(command)) - .isInstanceOf(NotApplicantException.class); } } \ No newline at end of file diff --git a/src/test/java/es/princip/getp/application/project/meeting/ScheduleMeetingCommandFixture.java b/get-p-application/src/test/java/es/princip/getp/application/project/meeting/ScheduleMeetingCommandFixture.java similarity index 60% rename from src/test/java/es/princip/getp/application/project/meeting/ScheduleMeetingCommandFixture.java rename to get-p-application/src/test/java/es/princip/getp/application/project/meeting/ScheduleMeetingCommandFixture.java index 233c0a53..35abe8b5 100644 --- a/src/test/java/es/princip/getp/application/project/meeting/ScheduleMeetingCommandFixture.java +++ b/get-p-application/src/test/java/es/princip/getp/application/project/meeting/ScheduleMeetingCommandFixture.java @@ -1,6 +1,9 @@ package es.princip.getp.application.project.meeting; -import es.princip.getp.application.project.meeting.command.ScheduleMeetingCommand; +import es.princip.getp.application.project.meeting.dto.command.ScheduleMeetingCommand; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.commission.model.ProjectId; import static es.princip.getp.fixture.member.PhoneNumberFixture.phoneNumber; import static es.princip.getp.fixture.project.ProjectMeetingFixture.*; @@ -8,9 +11,9 @@ class ScheduleMeetingCommandFixture { public static ScheduleMeetingCommand scheduleMeetingCommand( - final Long memberId, - final Long projectId, - final Long applicantId + final MemberId memberId, + final ProjectId projectId, + final PeopleId applicantId ) { return new ScheduleMeetingCommand( memberId, diff --git a/get-p-batch/build.gradle b/get-p-batch/build.gradle new file mode 100644 index 00000000..f011e57c --- /dev/null +++ b/get-p-batch/build.gradle @@ -0,0 +1,24 @@ +dependencies { + implementation(project(":get-p-domain")) + implementation(project(":get-p-persistence")) + implementation(testFixtures(project(":get-p-domain"))) + testImplementation(testFixtures(project(':get-p-domain'))) + + // Spring Data + implementation 'org.springframework.boot:spring-boot-starter-data-jpa:3.3.5' + + // Flyway + implementation 'org.flywaydb:flyway-core:9.16.3' + implementation 'org.flywaydb:flyway-mysql:9.16.3' + + // JDBC MySQL 드라이버 + runtimeOnly 'com.mysql:mysql-connector-j:9.0.0' +} + +bootJar { + enabled = true +} + +jar { + enabled = false +} \ No newline at end of file diff --git a/get-p-batch/src/main/java/es/princip/getp/batch/BatchInsertionException.java b/get-p-batch/src/main/java/es/princip/getp/batch/BatchInsertionException.java new file mode 100644 index 00000000..41d0e2c3 --- /dev/null +++ b/get-p-batch/src/main/java/es/princip/getp/batch/BatchInsertionException.java @@ -0,0 +1,12 @@ +package es.princip.getp.batch; + +public class BatchInsertionException extends RuntimeException { + + public BatchInsertionException(final String message) { + super(message); + } + + public BatchInsertionException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/get-p-batch/src/main/java/es/princip/getp/batch/GetpBatchApplication.java b/get-p-batch/src/main/java/es/princip/getp/batch/GetpBatchApplication.java new file mode 100644 index 00000000..0f55cdb2 --- /dev/null +++ b/get-p-batch/src/main/java/es/princip/getp/batch/GetpBatchApplication.java @@ -0,0 +1,34 @@ +package es.princip.getp.batch; + +import es.princip.getp.batch.project.commission.BatchDeleteProjectService; +import es.princip.getp.batch.project.commission.ParallelBatchInsertProjectService; +import es.princip.getp.batch.project.apply.BatchDeleteProjectApplicationService; +import es.princip.getp.batch.project.apply.BatchInsertProjectApplicationService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class GetpBatchApplication implements CommandLineRunner { + + @Autowired private BatchDeleteProjectApplicationService batchDeleteProjectApplicationService; + @Autowired private BatchDeleteProjectService batchDeleteProjectService; + + @Autowired private BatchInsertProjectApplicationService batchInsertProjectApplicationService; + @Autowired private ParallelBatchInsertProjectService batchInsertProjectService; + + public static void main(String[] args) { + SpringApplication.run(GetpBatchApplication.class, args); + } + + private static final int PROJECT_SIZE = 100_000; + @Override + public void run(final String... args) { + batchDeleteProjectApplicationService.delete(); + batchDeleteProjectService.delete(); + + batchInsertProjectService.insert(PROJECT_SIZE); + batchInsertProjectApplicationService.insert(PROJECT_SIZE); + } +} diff --git a/get-p-batch/src/main/java/es/princip/getp/batch/UniqueLongGenerator.java b/get-p-batch/src/main/java/es/princip/getp/batch/UniqueLongGenerator.java new file mode 100644 index 00000000..86cfdcc2 --- /dev/null +++ b/get-p-batch/src/main/java/es/princip/getp/batch/UniqueLongGenerator.java @@ -0,0 +1,11 @@ +package es.princip.getp.batch; + +import java.util.concurrent.atomic.AtomicLong; + +public class UniqueLongGenerator { + private static final AtomicLong counter = new AtomicLong(); + + public static long generateUniqueLong() { + return counter.incrementAndGet(); + } +} \ No newline at end of file diff --git a/get-p-batch/src/main/java/es/princip/getp/batch/config/ExecutionTimer.java b/get-p-batch/src/main/java/es/princip/getp/batch/config/ExecutionTimer.java new file mode 100644 index 00000000..c6962c33 --- /dev/null +++ b/get-p-batch/src/main/java/es/princip/getp/batch/config/ExecutionTimer.java @@ -0,0 +1,30 @@ +package es.princip.getp.batch.config; + +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.stereotype.Component; + +@Slf4j +@Aspect +@Component +public class ExecutionTimer { + + @Pointcut("@annotation(es.princip.getp.batch.config.ExtendsWithExecutionTimer)") + private void timer() {} + + @Around("timer()") + public Object AssumeExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { + final long start = System.currentTimeMillis(); + try { + return joinPoint.proceed(); + } finally { + final long finish = System.currentTimeMillis(); + final long executionTime = finish - start; + final String signature = joinPoint.getSignature().toShortString(); + log.info("execution time of {}: {}ms", signature, executionTime); + } + } +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/common/mapper/CommandMapper.java b/get-p-batch/src/main/java/es/princip/getp/batch/config/ExtendsWithExecutionTimer.java similarity index 58% rename from src/main/java/es/princip/getp/api/controller/common/mapper/CommandMapper.java rename to get-p-batch/src/main/java/es/princip/getp/batch/config/ExtendsWithExecutionTimer.java index f5747596..bf601c80 100644 --- a/src/main/java/es/princip/getp/api/controller/common/mapper/CommandMapper.java +++ b/get-p-batch/src/main/java/es/princip/getp/batch/config/ExtendsWithExecutionTimer.java @@ -1,11 +1,11 @@ -package es.princip.getp.api.controller.common.mapper; +package es.princip.getp.batch.config; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@Target(ElementType.TYPE) +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) -public @interface CommandMapper { +public @interface ExtendsWithExecutionTimer { } diff --git a/get-p-batch/src/main/java/es/princip/getp/batch/parallel/ParallelBatchInsertService.java b/get-p-batch/src/main/java/es/princip/getp/batch/parallel/ParallelBatchInsertService.java new file mode 100644 index 00000000..e4bb801a --- /dev/null +++ b/get-p-batch/src/main/java/es/princip/getp/batch/parallel/ParallelBatchInsertService.java @@ -0,0 +1,51 @@ +package es.princip.getp.batch.parallel; + +import es.princip.getp.batch.BatchInsertionException; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@Slf4j +@Service +public class ParallelBatchInsertService { + + @Getter + private final int numThreads = Runtime.getRuntime().availableProcessors(); + + public void insert(final int size, final ParallelBatchInserter batchInserter) { + final ExecutorService executorService = Executors.newFixedThreadPool(numThreads); + final List> futures = new ArrayList<>(); + final int batchSize = size / numThreads; + for (int i = 0; i < numThreads; i++) { + final int start = i * batchSize + 1; + final int end = (i == numThreads - 1) ? size : start + batchSize - 1; + final CompletableFuture future = CompletableFuture.runAsync(() -> { + final String threadName = Thread.currentThread().getName(); + log.info("Thread {} is inserting projects from {} to {}", threadName, start, end); + try { + batchInserter.insert(start, end); + } catch (final Exception exception) { + throw new BatchInsertionException( + String.format( + "Thread %s encountered an error during batch insert for range %d to %d: ", + threadName, + start, + end + ), + exception + ); + } + }, executorService); + futures.add(future); + } + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); + executorService.shutdown(); + log.info("All threads completed. Executor service shutdown."); + } +} diff --git a/get-p-batch/src/main/java/es/princip/getp/batch/parallel/ParallelBatchInserter.java b/get-p-batch/src/main/java/es/princip/getp/batch/parallel/ParallelBatchInserter.java new file mode 100644 index 00000000..3f0fcf16 --- /dev/null +++ b/get-p-batch/src/main/java/es/princip/getp/batch/parallel/ParallelBatchInserter.java @@ -0,0 +1,6 @@ +package es.princip.getp.batch.parallel; + +@FunctionalInterface +public interface ParallelBatchInserter { + void insert(int start, int end); +} diff --git a/get-p-batch/src/main/java/es/princip/getp/batch/project/apply/BatchDeleteProjectApplicationService.java b/get-p-batch/src/main/java/es/princip/getp/batch/project/apply/BatchDeleteProjectApplicationService.java new file mode 100644 index 00000000..91feb494 --- /dev/null +++ b/get-p-batch/src/main/java/es/princip/getp/batch/project/apply/BatchDeleteProjectApplicationService.java @@ -0,0 +1,27 @@ +package es.princip.getp.batch.project.apply; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class BatchDeleteProjectApplicationService { + + private final JdbcTemplate jdbcTemplate; + + public void delete() { + jdbcTemplate.execute("delete from team_project_application_teammate"); + jdbcTemplate.execute("delete from team_project_application"); + jdbcTemplate.execute("delete from individual_project_application"); + jdbcTemplate.execute("delete from project_application_attachment_file"); + jdbcTemplate.execute("delete from project_application"); + log.info("Table \"team_project_application_teammate\" is dropped"); + log.info("Table \"team_project_application\" is dropped"); + log.info("Table \"individual_project_application\" is dropped"); + log.info("Table \"project_application_attachment_file\" is dropped"); + log.info("Table \"project_application\" is dropped"); + } +} diff --git a/get-p-batch/src/main/java/es/princip/getp/batch/project/apply/BatchInsertIndividualProjectApplicationJdbcService.java b/get-p-batch/src/main/java/es/princip/getp/batch/project/apply/BatchInsertIndividualProjectApplicationJdbcService.java new file mode 100644 index 00000000..55f5e111 --- /dev/null +++ b/get-p-batch/src/main/java/es/princip/getp/batch/project/apply/BatchInsertIndividualProjectApplicationJdbcService.java @@ -0,0 +1,45 @@ +package es.princip.getp.batch.project.apply; + +import es.princip.getp.domain.project.apply.model.IndividualProjectApplication; +import es.princip.getp.domain.project.apply.model.ProjectApplication; +import lombok.RequiredArgsConstructor; +import org.springframework.jdbc.core.BatchPreparedStatementSetter; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Service; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; + +@Service +@RequiredArgsConstructor +class BatchInsertIndividualProjectApplicationJdbcService { + + private final JdbcTemplate jdbcTemplate; + private static final String sql = + """ + insert into individual_project_application ( + project_application_id + ) values (?); + """; + + public void batchUpdate(final List applications) { + final List individuals = applications.stream() + .filter(IndividualProjectApplication.class::isInstance) + .map(IndividualProjectApplication.class::cast) + .toList(); + + jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { + @Override + public void setValues(final PreparedStatement ps, final int i) throws SQLException { + final IndividualProjectApplication application = individuals.get(i); + ps.setLong(1, application.getId().getValue()); + } + + @Override + public int getBatchSize() { + return individuals.size(); + } + }); + } +} diff --git a/get-p-batch/src/main/java/es/princip/getp/batch/project/apply/BatchInsertProjectApplicationAttachmentFileJdbcService.java b/get-p-batch/src/main/java/es/princip/getp/batch/project/apply/BatchInsertProjectApplicationAttachmentFileJdbcService.java new file mode 100644 index 00000000..0c7dadd4 --- /dev/null +++ b/get-p-batch/src/main/java/es/princip/getp/batch/project/apply/BatchInsertProjectApplicationAttachmentFileJdbcService.java @@ -0,0 +1,58 @@ +package es.princip.getp.batch.project.apply; + +import es.princip.getp.domain.common.model.AttachmentFile; +import es.princip.getp.domain.project.apply.model.ProjectApplication; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; +import lombok.RequiredArgsConstructor; +import org.springframework.jdbc.core.BatchPreparedStatementSetter; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Service; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; + +@Service +@RequiredArgsConstructor +class BatchInsertProjectApplicationAttachmentFileJdbcService { + + private record ProjectApplicationIdAttachmentFile( + ProjectApplicationId applicationId, + AttachmentFile attachmentFile + ) { + } + + private final JdbcTemplate jdbcTemplate; + private static final String sql = + """ + insert into project_application_attachment_file( + project_application_id, + attachment_files + ) values (?, ?); + """; + + public void batchUpdate(final List applications) { + final List attachmentFiles = applications.stream() + .flatMap(application -> application.getAttachmentFiles().stream() + .map(attachmentFile -> new ProjectApplicationIdAttachmentFile( + application.getId(), + attachmentFile + ))) + .toList(); + + jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { + @Override + public void setValues(final PreparedStatement ps, final int i) throws SQLException { + final ProjectApplicationId applicationId = attachmentFiles.get(i).applicationId(); + final AttachmentFile attachmentFile = attachmentFiles.get(i).attachmentFile(); + ps.setLong(1, applicationId.getValue()); + ps.setString(2, attachmentFile.getUrl().getValue()); + } + + @Override + public int getBatchSize() { + return attachmentFiles.size(); + } + }); + } +} diff --git a/get-p-batch/src/main/java/es/princip/getp/batch/project/apply/BatchInsertProjectApplicationJdbcService.java b/get-p-batch/src/main/java/es/princip/getp/batch/project/apply/BatchInsertProjectApplicationJdbcService.java new file mode 100644 index 00000000..73ca614d --- /dev/null +++ b/get-p-batch/src/main/java/es/princip/getp/batch/project/apply/BatchInsertProjectApplicationJdbcService.java @@ -0,0 +1,65 @@ +package es.princip.getp.batch.project.apply; + +import es.princip.getp.domain.project.apply.model.IndividualProjectApplication; +import es.princip.getp.domain.project.apply.model.ProjectApplication; +import es.princip.getp.domain.project.apply.model.TeamProjectApplication; +import lombok.RequiredArgsConstructor; +import org.springframework.jdbc.core.BatchPreparedStatementSetter; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Service; + +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; + +@Service +@RequiredArgsConstructor +class BatchInsertProjectApplicationJdbcService { + + private final BatchInsertProjectApplicationAttachmentFileJdbcService attachmentFileJdbcService; + private final BatchInsertTeamProjectApplicationJdbcService teamJdbcService; + private final BatchInsertIndividualProjectApplicationJdbcService individualJdbcService; + private final JdbcTemplate jdbcTemplate; + private static final String sql = + """ + insert into project_application ( + project_application_id, + expected_end_date, + expected_start_date, + description, + status, + people_id, + project_id, + dtype + ) values (?, ?, ?, ?, ?, ?, ?, ?); + """; + + public void batchUpdate(final List applications) { + jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { + @Override + public void setValues(final PreparedStatement ps, final int i) throws SQLException { + final ProjectApplication application = applications.get(i); + final String dtype = (application instanceof TeamProjectApplication) ? + TeamProjectApplication.TYPE : IndividualProjectApplication.TYPE; + ps.setLong(1, application.getId().getValue()); + ps.setDate(2, Date.valueOf(application.getExpectedDuration().getEndDate())); + ps.setDate(3, Date.valueOf(application.getExpectedDuration().getStartDate())); + ps.setString(4, application.getDescription()); + ps.setString(5, application.getStatus().toString()); + ps.setLong(6, application.getApplicantId().getValue()); + ps.setLong(7, application.getProjectId().getValue()); + ps.setString(8,dtype); + } + + @Override + public int getBatchSize() { + return applications.size(); + } + }); + + attachmentFileJdbcService.batchUpdate(applications); + individualJdbcService.batchUpdate(applications); + teamJdbcService.batchUpdate(applications); + } +} diff --git a/get-p-batch/src/main/java/es/princip/getp/batch/project/apply/BatchInsertProjectApplicationService.java b/get-p-batch/src/main/java/es/princip/getp/batch/project/apply/BatchInsertProjectApplicationService.java new file mode 100644 index 00000000..b392207e --- /dev/null +++ b/get-p-batch/src/main/java/es/princip/getp/batch/project/apply/BatchInsertProjectApplicationService.java @@ -0,0 +1,65 @@ +package es.princip.getp.batch.project.apply; + +import es.princip.getp.batch.UniqueLongGenerator; +import es.princip.getp.batch.config.ExtendsWithExecutionTimer; +import es.princip.getp.batch.parallel.ParallelBatchInsertService; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.apply.model.*; +import es.princip.getp.domain.project.commission.model.ProjectId; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import static es.princip.getp.fixture.project.ProjectApplicationFixture.*; + +@Slf4j +@Service +@RequiredArgsConstructor +public class BatchInsertProjectApplicationService { + + private final BatchInsertProjectApplicationJdbcService jdbcService; + private final ParallelBatchInsertService batchInsertService; + + @ExtendsWithExecutionTimer + public void insert(final int projectSize) { + batchInsertService.insert(projectSize, (start, end) -> { + final List applications = new ArrayList<>(); + for (int i = start; i <= end; i++) { + final ProjectId projectId = new ProjectId((long) i); + applications.add(individualProjectApplication( + new ProjectApplicationId(UniqueLongGenerator.generateUniqueLong()), + new PeopleId(1L), + projectId + )); + applications.add(teamProjectApplication( + new ProjectApplicationId(UniqueLongGenerator.generateUniqueLong()), + new PeopleId(2L), + projectId, + ProjectApplicationStatus.COMPLETED, + Set.of( + teammate( + new TeammateId(UniqueLongGenerator.generateUniqueLong()), + new PeopleId(3L), + TeammateStatus.APPROVED + ), + teammate( + new TeammateId(UniqueLongGenerator.generateUniqueLong()), + new PeopleId(4L), + TeammateStatus.APPROVED + ), + teammate( + new TeammateId(UniqueLongGenerator.generateUniqueLong()), + new PeopleId(5L), + TeammateStatus.APPROVED + ) + ) + )); + } + jdbcService.batchUpdate(applications); + }); + } +} diff --git a/get-p-batch/src/main/java/es/princip/getp/batch/project/apply/BatchInsertTeamProjectApplicationJdbcService.java b/get-p-batch/src/main/java/es/princip/getp/batch/project/apply/BatchInsertTeamProjectApplicationJdbcService.java new file mode 100644 index 00000000..8893e091 --- /dev/null +++ b/get-p-batch/src/main/java/es/princip/getp/batch/project/apply/BatchInsertTeamProjectApplicationJdbcService.java @@ -0,0 +1,48 @@ +package es.princip.getp.batch.project.apply; + +import es.princip.getp.domain.project.apply.model.ProjectApplication; +import es.princip.getp.domain.project.apply.model.TeamProjectApplication; +import lombok.RequiredArgsConstructor; +import org.springframework.jdbc.core.BatchPreparedStatementSetter; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Service; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; + +@Service +@RequiredArgsConstructor +class BatchInsertTeamProjectApplicationJdbcService { + + private final BatchInsertTeamProjectApplicationTeammateJdbcService teammateJdbcService; + private final JdbcTemplate jdbcTemplate; + private static final String sql = + """ + insert into team_project_application ( + project_application_id + ) values (?); + """; + + public void batchUpdate(final List applications) { + final List teams = applications.stream() + .filter(TeamProjectApplication.class::isInstance) + .map(TeamProjectApplication.class::cast) + .toList(); + + jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { + @Override + public void setValues(final PreparedStatement ps, final int i) throws SQLException { + final TeamProjectApplication application = teams.get(i); + ps.setLong(1, application.getId().getValue()); + } + + @Override + public int getBatchSize() { + return teams.size(); + } + }); + + teammateJdbcService.batchUpdate(teams); + } +} diff --git a/get-p-batch/src/main/java/es/princip/getp/batch/project/apply/BatchInsertTeamProjectApplicationTeammateJdbcService.java b/get-p-batch/src/main/java/es/princip/getp/batch/project/apply/BatchInsertTeamProjectApplicationTeammateJdbcService.java new file mode 100644 index 00000000..4e0c140c --- /dev/null +++ b/get-p-batch/src/main/java/es/princip/getp/batch/project/apply/BatchInsertTeamProjectApplicationTeammateJdbcService.java @@ -0,0 +1,59 @@ +package es.princip.getp.batch.project.apply; + +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; +import es.princip.getp.domain.project.apply.model.TeamProjectApplication; +import es.princip.getp.domain.project.apply.model.Teammate; +import lombok.RequiredArgsConstructor; +import org.springframework.jdbc.core.BatchPreparedStatementSetter; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Service; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; + +@Service +@RequiredArgsConstructor +class BatchInsertTeamProjectApplicationTeammateJdbcService { + + private record ProjectApplicationIdTeammate( + ProjectApplicationId applicationId, + Teammate teammate + ) { + } + + private final JdbcTemplate jdbcTemplate; + private static final String sql = + """ + insert into team_project_application_teammate ( + teammate_id, + people_id, + status, + project_application_id + ) values (?, ?, ?, ?) + """; + + public void batchUpdate(final List applications) { + final List teammates = applications.stream() + .flatMap(application -> application.getTeammates().stream() + .map(teammate -> new ProjectApplicationIdTeammate(application.getId(), teammate))) + .toList(); + + jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { + @Override + public void setValues(final PreparedStatement ps, final int i) throws SQLException { + final ProjectApplicationId applicationId = teammates.get(i).applicationId(); + final Teammate teammate = teammates.get(i).teammate(); + ps.setLong(1, teammate.getId().getValue()); + ps.setLong(2, teammate.getPeopleId().getValue()); + ps.setString(3, teammate.getStatus().toString()); + ps.setLong(4, applicationId.getValue()); + } + + @Override + public int getBatchSize() { + return teammates.size(); + } + }); + } +} diff --git a/get-p-batch/src/main/java/es/princip/getp/batch/project/commission/BatchDeleteProjectService.java b/get-p-batch/src/main/java/es/princip/getp/batch/project/commission/BatchDeleteProjectService.java new file mode 100644 index 00000000..60a0a8c2 --- /dev/null +++ b/get-p-batch/src/main/java/es/princip/getp/batch/project/commission/BatchDeleteProjectService.java @@ -0,0 +1,23 @@ +package es.princip.getp.batch.project.commission; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class BatchDeleteProjectService { + + private final JdbcTemplate jdbcTemplate; + + public void delete() { + jdbcTemplate.execute("delete from project_hashtag"); + jdbcTemplate.execute("delete from project_attachment_file"); + jdbcTemplate.execute("delete from project"); + log.info("Table \"project_hashtag\" is dropped"); + log.info("Table \"project_attachment_file\" is dropped"); + log.info("Table \"project\" is dropped"); + } +} diff --git a/get-p-batch/src/main/java/es/princip/getp/batch/project/commission/BatchInsertProjectAttachmentFileJdbcService.java b/get-p-batch/src/main/java/es/princip/getp/batch/project/commission/BatchInsertProjectAttachmentFileJdbcService.java new file mode 100644 index 00000000..d4c24504 --- /dev/null +++ b/get-p-batch/src/main/java/es/princip/getp/batch/project/commission/BatchInsertProjectAttachmentFileJdbcService.java @@ -0,0 +1,53 @@ +package es.princip.getp.batch.project.commission; + +import es.princip.getp.domain.common.model.AttachmentFile; +import es.princip.getp.domain.project.commission.model.Project; +import es.princip.getp.domain.project.commission.model.ProjectId; +import lombok.RequiredArgsConstructor; +import org.springframework.jdbc.core.BatchPreparedStatementSetter; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Service; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; + +@Service +@RequiredArgsConstructor +class BatchInsertProjectAttachmentFileJdbcService { + + private record ProjectIdAttachmentFile( + ProjectId projectId, + AttachmentFile attachmentFile + ) { + } + + private final JdbcTemplate jdbcTemplate; + private static final String sql = + """ + insert into project_attachment_file (project_id, attachment_files) + values (?, ?); + """; + + public void batchUpdate(final List projects) { + final List attachmentFiles = projects.stream() + .flatMap(project -> project.getAttachmentFiles().stream() + .map(file -> new ProjectIdAttachmentFile(project.getId(), file))) + .toList(); + + jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { + @Override + public void setValues(final PreparedStatement ps, final int i) throws SQLException { + final Long projectId = attachmentFiles.get(i).projectId().getValue(); + final String attachmentFileUrl = attachmentFiles.get(i).attachmentFile().getUrl().getValue(); + ps.setLong(1, projectId); + ps.setString(2, attachmentFileUrl); + } + + @Override + public int getBatchSize() { + return attachmentFiles.size(); + } + }); + } +} diff --git a/get-p-batch/src/main/java/es/princip/getp/batch/project/commission/BatchInsertProjectHashtagJdbcService.java b/get-p-batch/src/main/java/es/princip/getp/batch/project/commission/BatchInsertProjectHashtagJdbcService.java new file mode 100644 index 00000000..54aac7c9 --- /dev/null +++ b/get-p-batch/src/main/java/es/princip/getp/batch/project/commission/BatchInsertProjectHashtagJdbcService.java @@ -0,0 +1,53 @@ +package es.princip.getp.batch.project.commission; + +import es.princip.getp.domain.common.model.Hashtag; +import es.princip.getp.domain.project.commission.model.Project; +import es.princip.getp.domain.project.commission.model.ProjectId; +import lombok.RequiredArgsConstructor; +import org.springframework.jdbc.core.BatchPreparedStatementSetter; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Service; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; + +@Service +@RequiredArgsConstructor +class BatchInsertProjectHashtagJdbcService { + + private record ProjectIdHashtag( + ProjectId projectId, + Hashtag hashtag + ) { + } + + private final JdbcTemplate jdbcTemplate; + private static final String sql = + """ + insert into project_hashtag (project_id, hashtags) + values (?, ?) + """; + + public void batchUpdate(final List projects) { + final List hashtags = projects.stream() + .flatMap(project -> project.getHashtags().stream() + .map(hashtag -> new ProjectIdHashtag(project.getId(), hashtag))) + .toList(); + + jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { + @Override + public void setValues(final PreparedStatement ps, final int i) throws SQLException { + final Long projectId = hashtags.get(i).projectId().getValue(); + final String hashtag = hashtags.get(i).hashtag().getValue(); + ps.setLong(1, projectId); + ps.setString(2, hashtag); + } + + @Override + public int getBatchSize() { + return hashtags.size(); + } + }); + } +} diff --git a/get-p-batch/src/main/java/es/princip/getp/batch/project/commission/BatchInsertProjectJdbcService.java b/get-p-batch/src/main/java/es/princip/getp/batch/project/commission/BatchInsertProjectJdbcService.java new file mode 100644 index 00000000..618b562d --- /dev/null +++ b/get-p-batch/src/main/java/es/princip/getp/batch/project/commission/BatchInsertProjectJdbcService.java @@ -0,0 +1,74 @@ +package es.princip.getp.batch.project.commission; + +import es.princip.getp.domain.project.commission.model.Project; +import lombok.RequiredArgsConstructor; +import org.springframework.jdbc.core.BatchPreparedStatementSetter; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Service; + +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.time.LocalDateTime; +import java.util.List; + +@Service +@RequiredArgsConstructor +class BatchInsertProjectJdbcService { + + private final BatchInsertProjectHashtagJdbcService hashtagJdbcService; + private final BatchInsertProjectAttachmentFileJdbcService attachmentFileJdbcService; + private final JdbcTemplate jdbcTemplate; + private static final String sql = + """ + insert into project ( + project_id, + application_end_date, + application_start_date, + estimated_end_date, + estimated_start_date, + payment, + category, + description, + meeting_type, + status, + title, + client_id, + recruitment_count, + created_at, + updated_at + ) + values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """; + + public void batchUpdate(final List projects) { + jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { + @Override + public void setValues(final PreparedStatement ps, final int i) throws SQLException { + final Project project = projects.get(i); + ps.setLong(1, project.getId().getValue()); + ps.setDate(2, Date.valueOf(project.getApplicationDuration().getEndDate())); + ps.setDate(3, Date.valueOf(project.getApplicationDuration().getStartDate())); + ps.setDate(4, Date.valueOf(project.getEstimatedDuration().getEndDate())); + ps.setDate(5, Date.valueOf(project.getEstimatedDuration().getStartDate())); + ps.setLong(6, project.getPayment()); + ps.setString(7, project.getCategory().toString()); + ps.setString(8, project.getDescription()); + ps.setString(9, project.getMeetingType().toString()); + ps.setString(10, project.getStatus().toString()); + ps.setString(11, project.getTitle()); + ps.setLong(12, project.getClientId().getValue()); + ps.setLong(13, project.getRecruitmentCount()); + ps.setString(14, String.valueOf(LocalDateTime.now())); + ps.setString(15, String.valueOf(LocalDateTime.now())); + } + + @Override + public int getBatchSize() { + return projects.size(); + } + }); + hashtagJdbcService.batchUpdate(projects); + attachmentFileJdbcService.batchUpdate(projects); + } +} diff --git a/get-p-batch/src/main/java/es/princip/getp/batch/project/commission/BatchInsertProjectService.java b/get-p-batch/src/main/java/es/princip/getp/batch/project/commission/BatchInsertProjectService.java new file mode 100644 index 00000000..24ee5230 --- /dev/null +++ b/get-p-batch/src/main/java/es/princip/getp/batch/project/commission/BatchInsertProjectService.java @@ -0,0 +1,6 @@ +package es.princip.getp.batch.project.commission; + +public interface BatchInsertProjectService { + + void insert(int size); +} diff --git a/get-p-batch/src/main/java/es/princip/getp/batch/project/commission/ParallelBatchInsertProjectService.java b/get-p-batch/src/main/java/es/princip/getp/batch/project/commission/ParallelBatchInsertProjectService.java new file mode 100644 index 00000000..40ad36eb --- /dev/null +++ b/get-p-batch/src/main/java/es/princip/getp/batch/project/commission/ParallelBatchInsertProjectService.java @@ -0,0 +1,40 @@ +package es.princip.getp.batch.project.commission; + +import es.princip.getp.batch.config.ExtendsWithExecutionTimer; +import es.princip.getp.batch.parallel.ParallelBatchInsertService; +import es.princip.getp.domain.client.model.ClientId; +import es.princip.getp.domain.project.commission.model.Project; +import es.princip.getp.domain.project.commission.model.ProjectId; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.LongStream; + +import static es.princip.getp.domain.project.commission.model.ProjectStatus.APPLICATION_OPENED; +import static es.princip.getp.fixture.project.ProjectFixture.project; + +@Slf4j +@Service +@RequiredArgsConstructor +public class ParallelBatchInsertProjectService implements BatchInsertProjectService { + + private final BatchInsertProjectJdbcService jdbcService; + private final ParallelBatchInsertService batchInsertService; + + @ExtendsWithExecutionTimer + public void insert(final int size) { + batchInsertService.insert(size, (start, end) -> { + final List projects = LongStream.rangeClosed(start, end) + .boxed() + .map(id -> project( + new ProjectId(id), + new ClientId(id), + APPLICATION_OPENED + )) + .toList(); + jdbcService.batchUpdate(projects); + }); + } +} diff --git a/get-p-batch/src/main/java/es/princip/getp/batch/project/commission/SequentialBatchInsertProjectService.java b/get-p-batch/src/main/java/es/princip/getp/batch/project/commission/SequentialBatchInsertProjectService.java new file mode 100644 index 00000000..892ebb8e --- /dev/null +++ b/get-p-batch/src/main/java/es/princip/getp/batch/project/commission/SequentialBatchInsertProjectService.java @@ -0,0 +1,36 @@ +package es.princip.getp.batch.project.commission; + +import es.princip.getp.batch.config.ExtendsWithExecutionTimer; +import es.princip.getp.domain.client.model.ClientId; +import es.princip.getp.domain.project.commission.model.Project; +import es.princip.getp.domain.project.commission.model.ProjectId; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.LongStream; + +import static es.princip.getp.domain.project.commission.model.ProjectStatus.APPLICATION_OPENED; +import static es.princip.getp.fixture.project.ProjectFixture.project; + +@Slf4j +@Service +@RequiredArgsConstructor +public class SequentialBatchInsertProjectService implements BatchInsertProjectService { + + private final BatchInsertProjectJdbcService jdbcService; + + @ExtendsWithExecutionTimer + public void insert(final int size) { + final List projects = LongStream.rangeClosed(1, size) + .boxed() + .map(id -> project( + new ProjectId(id), + new ClientId(id), + APPLICATION_OPENED + )) + .toList(); + jdbcService.batchUpdate(projects); + } +} diff --git a/get-p-batch/src/main/resources/application.yml b/get-p-batch/src/main/resources/application.yml new file mode 100644 index 00000000..055dc92f --- /dev/null +++ b/get-p-batch/src/main/resources/application.yml @@ -0,0 +1,31 @@ +spring: + datasource: + url: ${DB_URL}?&rewriteBatchedStatements=true&profileSQL=true&logger=Slf4JLogger + username: ${DB_USERNAME} + password: ${DB_PASSWORD} + + flyway: + enabled: true + baseline-on-migrate: true + + jpa: + hibernate: + ddl-auto: validate + properties: + hibernate: + show_sql: true + format_sql: true + highlight_sql: true + use_sql_comments: true + jdbc: + time_zone: Asia/Seoul + default_batch_fetch_size: 20 + dialect: org.hibernate.dialect.MySQLDialect + +logging: + level: + org: + type: + descriptor: + sql: + BasicBinder: TRACE \ No newline at end of file diff --git a/get-p-domain/build.gradle b/get-p-domain/build.gradle new file mode 100644 index 00000000..27d0684a --- /dev/null +++ b/get-p-domain/build.gradle @@ -0,0 +1,12 @@ +dependencies { + // Spring Validation + implementation 'org.springframework.boot:spring-boot-starter-validation:3.3.5' +} + +bootJar { + enabled = false +} + +jar { + enabled = true +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/application/auth/service/EmailVerification.java b/get-p-domain/src/main/java/es/princip/getp/domain/auth/EmailVerification.java similarity index 61% rename from src/main/java/es/princip/getp/application/auth/service/EmailVerification.java rename to get-p-domain/src/main/java/es/princip/getp/domain/auth/EmailVerification.java index 3c899c93..c87d6a53 100644 --- a/src/main/java/es/princip/getp/application/auth/service/EmailVerification.java +++ b/get-p-domain/src/main/java/es/princip/getp/domain/auth/EmailVerification.java @@ -1,37 +1,41 @@ -package es.princip.getp.application.auth.service; +package es.princip.getp.domain.auth; import lombok.AccessLevel; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; -import org.springframework.data.annotation.Id; -import org.springframework.data.redis.core.RedisHash; -import org.springframework.data.redis.core.TimeToLive; import java.time.LocalDateTime; -import java.util.concurrent.TimeUnit; @Getter -@RedisHash(value = "email_verification") @EqualsAndHashCode(exclude = "expiration") @NoArgsConstructor(access = AccessLevel.PROTECTED) public class EmailVerification { - @Id private String email; - private String verificationCode; - - private LocalDateTime createdAt; - - @TimeToLive(unit = TimeUnit.MILLISECONDS) private Long expiration; + private LocalDateTime createdAt; - public EmailVerification(String email, String verificationCode, Long expiration) { + public EmailVerification( + final String email, + final String verificationCode, + final Long expiration + ) { this.email = email; this.verificationCode = verificationCode; - this.createdAt = LocalDateTime.now(); this.expiration = expiration; + this.createdAt = LocalDateTime.now(); + } + + public EmailVerification( + final String email, + final String verificationCode, + final Long expiration, + final LocalDateTime createdAt + ) { + this(email, verificationCode, expiration); + this.createdAt = createdAt; } public boolean verify(String verificationCode) { diff --git a/get-p-domain/src/main/java/es/princip/getp/domain/auth/RefreshToken.java b/get-p-domain/src/main/java/es/princip/getp/domain/auth/RefreshToken.java new file mode 100644 index 00000000..3981b2f7 --- /dev/null +++ b/get-p-domain/src/main/java/es/princip/getp/domain/auth/RefreshToken.java @@ -0,0 +1,18 @@ +package es.princip.getp.domain.auth; + +import es.princip.getp.domain.member.model.MemberId; +import lombok.Getter; + +@Getter +public class RefreshToken { + + private Long memberId; + private String refreshToken; + private Long expiration; + + public RefreshToken(final MemberId memberId, final String refreshToken, final Long expiration) { + this.memberId = memberId.getValue(); + this.refreshToken = refreshToken; + this.expiration = expiration; + } +} diff --git a/src/main/java/es/princip/getp/domain/client/model/Address.java b/get-p-domain/src/main/java/es/princip/getp/domain/client/model/Address.java similarity index 100% rename from src/main/java/es/princip/getp/domain/client/model/Address.java rename to get-p-domain/src/main/java/es/princip/getp/domain/client/model/Address.java diff --git a/src/main/java/es/princip/getp/domain/client/model/Client.java b/get-p-domain/src/main/java/es/princip/getp/domain/client/model/Client.java similarity index 68% rename from src/main/java/es/princip/getp/domain/client/model/Client.java rename to get-p-domain/src/main/java/es/princip/getp/domain/client/model/Client.java index 1ca92b55..241538ba 100644 --- a/src/main/java/es/princip/getp/domain/client/model/Client.java +++ b/get-p-domain/src/main/java/es/princip/getp/domain/client/model/Client.java @@ -1,7 +1,9 @@ package es.princip.getp.domain.client.model; -import es.princip.getp.domain.support.BaseEntity; +import es.princip.getp.domain.common.model.BankAccount; import es.princip.getp.domain.common.model.Email; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.support.BaseEntity; import jakarta.validation.constraints.NotNull; import lombok.Builder; import lombok.Getter; @@ -11,28 +13,26 @@ @Getter public class Client extends BaseEntity { - private Long clientId; + private ClientId id; @NotNull private Email email; private Address address; //TODO: 주소기반산업지원서비스 API로 확장 예정 private BankAccount bankAccount; - @NotNull private Long memberId; + @NotNull private MemberId memberId; @Builder public Client( - final Long clientId, + final ClientId id, final Email email, final Address address, - final BankAccount bankAccount, - final Long memberId, + final MemberId memberId, final LocalDateTime createdAt, final LocalDateTime updatedAt ) { super(createdAt, updatedAt); - this.clientId = clientId; + this.id = id; this.email = email; this.address = address; - this.bankAccount = bankAccount; this.memberId = memberId; validate(); @@ -52,16 +52,8 @@ private void setAddress(final Address address) { this.address = address; } - private void setBankAccount(final BankAccount bankAccount) { - if (bankAccount == null) { - throw new IllegalArgumentException(); - } - this.bankAccount = bankAccount; - } - - public void edit(final Email email, final Address address, final BankAccount bankAccount) { + public void edit(final Email email, final Address address) { setEmail(email); setAddress(address); - setBankAccount(bankAccount); } } \ No newline at end of file diff --git a/get-p-domain/src/main/java/es/princip/getp/domain/client/model/ClientId.java b/get-p-domain/src/main/java/es/princip/getp/domain/client/model/ClientId.java new file mode 100644 index 00000000..8663a0d9 --- /dev/null +++ b/get-p-domain/src/main/java/es/princip/getp/domain/client/model/ClientId.java @@ -0,0 +1,19 @@ +package es.princip.getp.domain.client.model; + +import es.princip.getp.domain.support.BaseModel; +import jakarta.validation.constraints.NotNull; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +@EqualsAndHashCode(callSuper = false) +public class ClientId extends BaseModel { + + @NotNull private final Long value; + + public ClientId(final Long value) { + this.value = value; + } +} diff --git a/src/main/java/es/princip/getp/domain/common/exception/StartDateIsAfterEndDateException.java b/get-p-domain/src/main/java/es/princip/getp/domain/common/exception/StartDateIsAfterEndDateException.java similarity index 100% rename from src/main/java/es/princip/getp/domain/common/exception/StartDateIsAfterEndDateException.java rename to get-p-domain/src/main/java/es/princip/getp/domain/common/exception/StartDateIsAfterEndDateException.java diff --git a/src/main/java/es/princip/getp/domain/common/exception/StartTimeIsAfterEndTimeException.java b/get-p-domain/src/main/java/es/princip/getp/domain/common/exception/StartTimeIsAfterEndTimeException.java similarity index 100% rename from src/main/java/es/princip/getp/domain/common/exception/StartTimeIsAfterEndTimeException.java rename to get-p-domain/src/main/java/es/princip/getp/domain/common/exception/StartTimeIsAfterEndTimeException.java diff --git a/src/main/java/es/princip/getp/domain/common/infrastructure/SystemClockHolder.java b/get-p-domain/src/main/java/es/princip/getp/domain/common/infrastructure/SystemClockHolder.java similarity index 100% rename from src/main/java/es/princip/getp/domain/common/infrastructure/SystemClockHolder.java rename to get-p-domain/src/main/java/es/princip/getp/domain/common/infrastructure/SystemClockHolder.java diff --git a/src/main/java/es/princip/getp/domain/common/model/AttachmentFile.java b/get-p-domain/src/main/java/es/princip/getp/domain/common/model/AttachmentFile.java similarity index 100% rename from src/main/java/es/princip/getp/domain/common/model/AttachmentFile.java rename to get-p-domain/src/main/java/es/princip/getp/domain/common/model/AttachmentFile.java diff --git a/src/main/java/es/princip/getp/domain/client/model/BankAccount.java b/get-p-domain/src/main/java/es/princip/getp/domain/common/model/BankAccount.java similarity index 93% rename from src/main/java/es/princip/getp/domain/client/model/BankAccount.java rename to get-p-domain/src/main/java/es/princip/getp/domain/common/model/BankAccount.java index f5330672..73344d72 100644 --- a/src/main/java/es/princip/getp/domain/client/model/BankAccount.java +++ b/get-p-domain/src/main/java/es/princip/getp/domain/common/model/BankAccount.java @@ -1,4 +1,4 @@ -package es.princip.getp.domain.client.model; +package es.princip.getp.domain.common.model; import es.princip.getp.domain.support.BaseModel; import jakarta.validation.constraints.NotBlank; diff --git a/src/main/java/es/princip/getp/domain/common/model/Duration.java b/get-p-domain/src/main/java/es/princip/getp/domain/common/model/Duration.java similarity index 100% rename from src/main/java/es/princip/getp/domain/common/model/Duration.java rename to get-p-domain/src/main/java/es/princip/getp/domain/common/model/Duration.java diff --git a/src/main/java/es/princip/getp/domain/common/model/Email.java b/get-p-domain/src/main/java/es/princip/getp/domain/common/model/Email.java similarity index 100% rename from src/main/java/es/princip/getp/domain/common/model/Email.java rename to get-p-domain/src/main/java/es/princip/getp/domain/common/model/Email.java diff --git a/src/main/java/es/princip/getp/domain/common/model/EmailPattern.java b/get-p-domain/src/main/java/es/princip/getp/domain/common/model/EmailPattern.java similarity index 100% rename from src/main/java/es/princip/getp/domain/common/model/EmailPattern.java rename to get-p-domain/src/main/java/es/princip/getp/domain/common/model/EmailPattern.java diff --git a/src/main/java/es/princip/getp/domain/common/model/Hashtag.java b/get-p-domain/src/main/java/es/princip/getp/domain/common/model/Hashtag.java similarity index 78% rename from src/main/java/es/princip/getp/domain/common/model/Hashtag.java rename to get-p-domain/src/main/java/es/princip/getp/domain/common/model/Hashtag.java index 424de08f..187d74d8 100644 --- a/src/main/java/es/princip/getp/domain/common/model/Hashtag.java +++ b/get-p-domain/src/main/java/es/princip/getp/domain/common/model/Hashtag.java @@ -1,18 +1,14 @@ package es.princip.getp.domain.common.model; -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; import jakarta.validation.constraints.NotBlank; import lombok.*; @Getter @ToString -@Embeddable @EqualsAndHashCode @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Hashtag { - @Column(name = "hashtag") @NotBlank private String value; diff --git a/src/main/java/es/princip/getp/domain/common/model/MeetingSchedule.java b/get-p-domain/src/main/java/es/princip/getp/domain/common/model/MeetingSchedule.java similarity index 100% rename from src/main/java/es/princip/getp/domain/common/model/MeetingSchedule.java rename to get-p-domain/src/main/java/es/princip/getp/domain/common/model/MeetingSchedule.java diff --git a/src/main/java/es/princip/getp/domain/common/model/PhoneNumber.java b/get-p-domain/src/main/java/es/princip/getp/domain/common/model/PhoneNumber.java similarity index 100% rename from src/main/java/es/princip/getp/domain/common/model/PhoneNumber.java rename to get-p-domain/src/main/java/es/princip/getp/domain/common/model/PhoneNumber.java diff --git a/src/main/java/es/princip/getp/domain/common/model/PhoneNumberPattern.java b/get-p-domain/src/main/java/es/princip/getp/domain/common/model/PhoneNumberPattern.java similarity index 100% rename from src/main/java/es/princip/getp/domain/common/model/PhoneNumberPattern.java rename to get-p-domain/src/main/java/es/princip/getp/domain/common/model/PhoneNumberPattern.java diff --git a/src/main/java/es/princip/getp/domain/common/model/TechStack.java b/get-p-domain/src/main/java/es/princip/getp/domain/common/model/TechStack.java similarity index 64% rename from src/main/java/es/princip/getp/domain/common/model/TechStack.java rename to get-p-domain/src/main/java/es/princip/getp/domain/common/model/TechStack.java index 94adb47c..cfa42cfa 100644 --- a/src/main/java/es/princip/getp/domain/common/model/TechStack.java +++ b/get-p-domain/src/main/java/es/princip/getp/domain/common/model/TechStack.java @@ -1,20 +1,14 @@ package es.princip.getp.domain.common.model; -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; import jakarta.validation.constraints.NotBlank; -import lombok.AccessLevel; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; -@Embeddable @Getter +@ToString @EqualsAndHashCode @NoArgsConstructor(access = AccessLevel.PROTECTED) public class TechStack { - @Column(name = "tech_stack") @NotBlank private String value; diff --git a/src/main/java/es/princip/getp/domain/common/model/URL.java b/get-p-domain/src/main/java/es/princip/getp/domain/common/model/URL.java similarity index 100% rename from src/main/java/es/princip/getp/domain/common/model/URL.java rename to get-p-domain/src/main/java/es/princip/getp/domain/common/model/URL.java diff --git a/src/main/java/es/princip/getp/domain/common/model/URLPattern.java b/get-p-domain/src/main/java/es/princip/getp/domain/common/model/URLPattern.java similarity index 100% rename from src/main/java/es/princip/getp/domain/common/model/URLPattern.java rename to get-p-domain/src/main/java/es/princip/getp/domain/common/model/URLPattern.java diff --git a/src/main/java/es/princip/getp/domain/common/service/ClockHolder.java b/get-p-domain/src/main/java/es/princip/getp/domain/common/service/ClockHolder.java similarity index 100% rename from src/main/java/es/princip/getp/domain/common/service/ClockHolder.java rename to get-p-domain/src/main/java/es/princip/getp/domain/common/service/ClockHolder.java diff --git a/src/main/java/es/princip/getp/domain/like/people/model/PeopleLike.java b/get-p-domain/src/main/java/es/princip/getp/domain/like/people/model/PeopleLike.java similarity index 64% rename from src/main/java/es/princip/getp/domain/like/people/model/PeopleLike.java rename to get-p-domain/src/main/java/es/princip/getp/domain/like/people/model/PeopleLike.java index 5ed364b7..8ab39f4c 100644 --- a/src/main/java/es/princip/getp/domain/like/people/model/PeopleLike.java +++ b/get-p-domain/src/main/java/es/princip/getp/domain/like/people/model/PeopleLike.java @@ -1,5 +1,7 @@ package es.princip.getp.domain.like.people.model; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.model.PeopleId; import es.princip.getp.domain.support.BaseEntity; import jakarta.validation.constraints.NotNull; import lombok.Builder; @@ -11,20 +13,20 @@ public class PeopleLike extends BaseEntity { private final Long id; - @NotNull private final Long clientId; - @NotNull private final Long peopleId; + @NotNull private final MemberId memberId; + @NotNull private final PeopleId peopleId; @Builder public PeopleLike( final Long id, - final Long clientId, - final Long peopleId, + final MemberId memberId, + final PeopleId peopleId, final LocalDateTime createdAt ) { super(createdAt, null); this.id = id; - this.clientId = clientId; + this.memberId = memberId; this.peopleId = peopleId; validate(); diff --git a/src/main/java/es/princip/getp/domain/like/project/model/ProjectLike.java b/get-p-domain/src/main/java/es/princip/getp/domain/like/project/model/ProjectLike.java similarity index 62% rename from src/main/java/es/princip/getp/domain/like/project/model/ProjectLike.java rename to get-p-domain/src/main/java/es/princip/getp/domain/like/project/model/ProjectLike.java index 20238eba..cec4a486 100644 --- a/src/main/java/es/princip/getp/domain/like/project/model/ProjectLike.java +++ b/get-p-domain/src/main/java/es/princip/getp/domain/like/project/model/ProjectLike.java @@ -1,5 +1,7 @@ package es.princip.getp.domain.like.project.model; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.project.commission.model.ProjectId; import es.princip.getp.domain.support.BaseEntity; import jakarta.validation.constraints.NotNull; import lombok.Builder; @@ -11,20 +13,20 @@ public class ProjectLike extends BaseEntity { private final Long id; - @NotNull private final Long peopleId; - @NotNull private final Long projectId; + @NotNull private final MemberId memberId; + @NotNull private final ProjectId projectId; @Builder public ProjectLike( final Long id, - final Long peopleId, - final Long projectId, + final MemberId memberId, + final ProjectId projectId, final LocalDateTime createdAt ) { super(createdAt, null); this.id = id; - this.peopleId = peopleId; + this.memberId = memberId; this.projectId = projectId; validate(); diff --git a/get-p-domain/src/main/java/es/princip/getp/domain/member/PasswordEncoder.java b/get-p-domain/src/main/java/es/princip/getp/domain/member/PasswordEncoder.java new file mode 100644 index 00000000..35e0e03c --- /dev/null +++ b/get-p-domain/src/main/java/es/princip/getp/domain/member/PasswordEncoder.java @@ -0,0 +1,8 @@ +package es.princip.getp.domain.member; + +public interface PasswordEncoder { + + String encode(final CharSequence rawPassword); + + boolean matches(final CharSequence rawPassword, final String encodedPassword); +} diff --git a/src/main/java/es/princip/getp/domain/member/exception/NotAgreedAllRequiredServiceTermException.java b/get-p-domain/src/main/java/es/princip/getp/domain/member/exception/NotAgreedAllRequiredServiceTermException.java similarity index 100% rename from src/main/java/es/princip/getp/domain/member/exception/NotAgreedAllRequiredServiceTermException.java rename to get-p-domain/src/main/java/es/princip/getp/domain/member/exception/NotAgreedAllRequiredServiceTermException.java diff --git a/src/main/java/es/princip/getp/domain/member/model/Member.java b/get-p-domain/src/main/java/es/princip/getp/domain/member/model/Member.java similarity index 94% rename from src/main/java/es/princip/getp/domain/member/model/Member.java rename to get-p-domain/src/main/java/es/princip/getp/domain/member/model/Member.java index 70ab9738..913ad80a 100644 --- a/src/main/java/es/princip/getp/domain/member/model/Member.java +++ b/get-p-domain/src/main/java/es/princip/getp/domain/member/model/Member.java @@ -1,14 +1,14 @@ package es.princip.getp.domain.member.model; -import es.princip.getp.domain.support.BaseEntity; import es.princip.getp.domain.common.model.Email; import es.princip.getp.domain.common.model.PhoneNumber; +import es.princip.getp.domain.member.PasswordEncoder; import es.princip.getp.domain.member.exception.NotAgreedAllRequiredServiceTermException; import es.princip.getp.domain.serviceTerm.model.ServiceTermTag; +import es.princip.getp.domain.support.BaseEntity; import jakarta.validation.constraints.NotNull; import lombok.Builder; import lombok.Getter; -import org.springframework.security.crypto.password.PasswordEncoder; import java.time.LocalDateTime; import java.util.Collections; @@ -20,7 +20,7 @@ @Getter public class Member extends BaseEntity { - private Long memberId; + private MemberId id; @NotNull private Email email; @NotNull private Password password; @NotNull private MemberType memberType; @@ -31,7 +31,7 @@ public class Member extends BaseEntity { @Builder public Member( - final Long memberId, + final MemberId id, final Email email, final Password password, final MemberType memberType, @@ -44,7 +44,7 @@ public Member( ) { super(createdAt, updatedAt); - this.memberId = memberId; + this.id = id; this.email = email; this.password = password; this.memberType = memberType; @@ -164,4 +164,12 @@ public void encodePassword(final PasswordEncoder encoder) { public Set getServiceTermAgreements() { return Collections.unmodifiableSet(serviceTermAgreements); } + + public boolean isPeople() { + return memberType.isPeople(); + } + + public boolean isClient() { + return memberType.isClient(); + } } diff --git a/get-p-domain/src/main/java/es/princip/getp/domain/member/model/MemberId.java b/get-p-domain/src/main/java/es/princip/getp/domain/member/model/MemberId.java new file mode 100644 index 00000000..0051c7ec --- /dev/null +++ b/get-p-domain/src/main/java/es/princip/getp/domain/member/model/MemberId.java @@ -0,0 +1,19 @@ +package es.princip.getp.domain.member.model; + +import es.princip.getp.domain.support.BaseModel; +import jakarta.validation.constraints.NotNull; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +@EqualsAndHashCode(callSuper = false) +public class MemberId extends BaseModel { + + @NotNull private final Long value; + + public MemberId(final Long value) { + this.value = value; + } +} diff --git a/src/main/java/es/princip/getp/domain/member/model/MemberType.java b/get-p-domain/src/main/java/es/princip/getp/domain/member/model/MemberType.java similarity index 100% rename from src/main/java/es/princip/getp/domain/member/model/MemberType.java rename to get-p-domain/src/main/java/es/princip/getp/domain/member/model/MemberType.java diff --git a/src/main/java/es/princip/getp/domain/member/model/Nickname.java b/get-p-domain/src/main/java/es/princip/getp/domain/member/model/Nickname.java similarity index 87% rename from src/main/java/es/princip/getp/domain/member/model/Nickname.java rename to get-p-domain/src/main/java/es/princip/getp/domain/member/model/Nickname.java index 14fab057..4844226a 100644 --- a/src/main/java/es/princip/getp/domain/member/model/Nickname.java +++ b/get-p-domain/src/main/java/es/princip/getp/domain/member/model/Nickname.java @@ -4,10 +4,8 @@ import jakarta.validation.constraints.NotNull; import lombok.EqualsAndHashCode; import lombok.Getter; -import lombok.ToString; @Getter -@ToString @EqualsAndHashCode(callSuper = false) public class Nickname extends BaseModel { @@ -22,4 +20,9 @@ public Nickname(final String value) { public static Nickname from(final String value) { return new Nickname(value); } + + @Override + public String toString() { + return value; + } } diff --git a/src/main/java/es/princip/getp/domain/member/model/Password.java b/get-p-domain/src/main/java/es/princip/getp/domain/member/model/Password.java similarity index 95% rename from src/main/java/es/princip/getp/domain/member/model/Password.java rename to get-p-domain/src/main/java/es/princip/getp/domain/member/model/Password.java index 33083ffc..e00cc784 100644 --- a/src/main/java/es/princip/getp/domain/member/model/Password.java +++ b/get-p-domain/src/main/java/es/princip/getp/domain/member/model/Password.java @@ -1,11 +1,11 @@ package es.princip.getp.domain.member.model; +import es.princip.getp.domain.member.PasswordEncoder; import es.princip.getp.domain.support.BaseModel; import jakarta.validation.constraints.NotNull; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; -import org.springframework.security.crypto.password.PasswordEncoder; @Getter @ToString diff --git a/src/main/java/es/princip/getp/domain/member/model/PasswordPattern.java b/get-p-domain/src/main/java/es/princip/getp/domain/member/model/PasswordPattern.java similarity index 100% rename from src/main/java/es/princip/getp/domain/member/model/PasswordPattern.java rename to get-p-domain/src/main/java/es/princip/getp/domain/member/model/PasswordPattern.java diff --git a/src/main/java/es/princip/getp/domain/member/model/ProfileImage.java b/get-p-domain/src/main/java/es/princip/getp/domain/member/model/ProfileImage.java similarity index 100% rename from src/main/java/es/princip/getp/domain/member/model/ProfileImage.java rename to get-p-domain/src/main/java/es/princip/getp/domain/member/model/ProfileImage.java diff --git a/src/main/java/es/princip/getp/domain/member/model/ServiceTermAgreement.java b/get-p-domain/src/main/java/es/princip/getp/domain/member/model/ServiceTermAgreement.java similarity index 100% rename from src/main/java/es/princip/getp/domain/member/model/ServiceTermAgreement.java rename to get-p-domain/src/main/java/es/princip/getp/domain/member/model/ServiceTermAgreement.java diff --git a/src/main/java/es/princip/getp/domain/member/model/ServiceTermAgreementData.java b/get-p-domain/src/main/java/es/princip/getp/domain/member/model/ServiceTermAgreementData.java similarity index 100% rename from src/main/java/es/princip/getp/domain/member/model/ServiceTermAgreementData.java rename to get-p-domain/src/main/java/es/princip/getp/domain/member/model/ServiceTermAgreementData.java diff --git a/src/main/java/es/princip/getp/domain/people/exception/AlreadyRegisteredPeopleProfileException.java b/get-p-domain/src/main/java/es/princip/getp/domain/people/exception/AlreadyRegisteredPeopleProfileException.java similarity index 100% rename from src/main/java/es/princip/getp/domain/people/exception/AlreadyRegisteredPeopleProfileException.java rename to get-p-domain/src/main/java/es/princip/getp/domain/people/exception/AlreadyRegisteredPeopleProfileException.java diff --git a/src/main/java/es/princip/getp/domain/people/exception/NotRegisteredPeopleProfileException.java b/get-p-domain/src/main/java/es/princip/getp/domain/people/exception/NotRegisteredPeopleProfileException.java similarity index 100% rename from src/main/java/es/princip/getp/domain/people/exception/NotRegisteredPeopleProfileException.java rename to get-p-domain/src/main/java/es/princip/getp/domain/people/exception/NotRegisteredPeopleProfileException.java diff --git a/src/main/java/es/princip/getp/domain/people/model/Education.java b/get-p-domain/src/main/java/es/princip/getp/domain/people/model/Education.java similarity index 100% rename from src/main/java/es/princip/getp/domain/people/model/Education.java rename to get-p-domain/src/main/java/es/princip/getp/domain/people/model/Education.java diff --git a/src/main/java/es/princip/getp/domain/people/model/People.java b/get-p-domain/src/main/java/es/princip/getp/domain/people/model/People.java similarity index 84% rename from src/main/java/es/princip/getp/domain/people/model/People.java rename to get-p-domain/src/main/java/es/princip/getp/domain/people/model/People.java index 96131902..2d356a8f 100644 --- a/src/main/java/es/princip/getp/domain/people/model/People.java +++ b/get-p-domain/src/main/java/es/princip/getp/domain/people/model/People.java @@ -1,9 +1,10 @@ package es.princip.getp.domain.people.model; -import es.princip.getp.domain.support.BaseEntity; import es.princip.getp.domain.common.model.Email; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.people.exception.AlreadyRegisteredPeopleProfileException; import es.princip.getp.domain.people.exception.NotRegisteredPeopleProfileException; +import es.princip.getp.domain.support.BaseEntity; import jakarta.validation.constraints.NotNull; import lombok.Getter; @@ -12,14 +13,14 @@ @Getter public class People extends BaseEntity { - private Long id; - @NotNull private Long memberId; + private PeopleId id; + @NotNull private MemberId memberId; private PeopleInfo info; private PeopleProfile profile; public People( - final Long id, - final Long memberId, + final PeopleId id, + final MemberId memberId, final PeopleInfo info, final PeopleProfile profile, final LocalDateTime createdAt, @@ -35,12 +36,12 @@ public People( validate(); } - public static People of(final Long memberId, final PeopleInfo info) { + public static People of(final MemberId memberId, final PeopleInfo info) { return new People(null, memberId, info, null, null, null); } - public void editInfo(final Email email, final PeopleType peopleType) { - this.info = new PeopleInfo(email, peopleType); + public void editInfo(final Email email) { + this.info = new PeopleInfo(email); } private PeopleProfile buildProfile(final PeopleProfileData data) { diff --git a/get-p-domain/src/main/java/es/princip/getp/domain/people/model/PeopleId.java b/get-p-domain/src/main/java/es/princip/getp/domain/people/model/PeopleId.java new file mode 100644 index 00000000..90395d14 --- /dev/null +++ b/get-p-domain/src/main/java/es/princip/getp/domain/people/model/PeopleId.java @@ -0,0 +1,19 @@ +package es.princip.getp.domain.people.model; + +import es.princip.getp.domain.support.BaseModel; +import jakarta.validation.constraints.NotNull; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +@EqualsAndHashCode(callSuper = false) +public class PeopleId extends BaseModel { + + @NotNull private final Long value; + + public PeopleId(final Long value) { + this.value = value; + } +} diff --git a/src/main/java/es/princip/getp/domain/people/model/PeopleInfo.java b/get-p-domain/src/main/java/es/princip/getp/domain/people/model/PeopleInfo.java similarity index 70% rename from src/main/java/es/princip/getp/domain/people/model/PeopleInfo.java rename to get-p-domain/src/main/java/es/princip/getp/domain/people/model/PeopleInfo.java index b2a8c649..b240f739 100644 --- a/src/main/java/es/princip/getp/domain/people/model/PeopleInfo.java +++ b/get-p-domain/src/main/java/es/princip/getp/domain/people/model/PeopleInfo.java @@ -1,7 +1,7 @@ package es.princip.getp.domain.people.model; -import es.princip.getp.domain.support.BaseModel; import es.princip.getp.domain.common.model.Email; +import es.princip.getp.domain.support.BaseModel; import jakarta.validation.constraints.NotNull; import lombok.Getter; @@ -9,11 +9,9 @@ public class PeopleInfo extends BaseModel { @NotNull private Email email; - @NotNull private PeopleType peopleType; - public PeopleInfo(final Email email, final PeopleType peopleType) { + public PeopleInfo(final Email email) { this.email = email; - this.peopleType = peopleType; validate(); } diff --git a/src/main/java/es/princip/getp/domain/people/model/PeopleProfile.java b/get-p-domain/src/main/java/es/princip/getp/domain/people/model/PeopleProfile.java similarity index 100% rename from src/main/java/es/princip/getp/domain/people/model/PeopleProfile.java rename to get-p-domain/src/main/java/es/princip/getp/domain/people/model/PeopleProfile.java diff --git a/src/main/java/es/princip/getp/domain/people/model/PeopleProfileData.java b/get-p-domain/src/main/java/es/princip/getp/domain/people/model/PeopleProfileData.java similarity index 100% rename from src/main/java/es/princip/getp/domain/people/model/PeopleProfileData.java rename to get-p-domain/src/main/java/es/princip/getp/domain/people/model/PeopleProfileData.java diff --git a/src/main/java/es/princip/getp/domain/people/model/PeopleType.java b/get-p-domain/src/main/java/es/princip/getp/domain/people/model/PeopleType.java similarity index 100% rename from src/main/java/es/princip/getp/domain/people/model/PeopleType.java rename to get-p-domain/src/main/java/es/princip/getp/domain/people/model/PeopleType.java diff --git a/src/main/java/es/princip/getp/domain/people/model/Portfolio.java b/get-p-domain/src/main/java/es/princip/getp/domain/people/model/Portfolio.java similarity index 100% rename from src/main/java/es/princip/getp/domain/people/model/Portfolio.java rename to get-p-domain/src/main/java/es/princip/getp/domain/people/model/Portfolio.java diff --git a/src/main/java/es/princip/getp/domain/project/apply/exception/ClosedProjectApplicationException.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/exception/ClosedProjectApplicationException.java similarity index 100% rename from src/main/java/es/princip/getp/domain/project/apply/exception/ClosedProjectApplicationException.java rename to get-p-domain/src/main/java/es/princip/getp/domain/project/apply/exception/ClosedProjectApplicationException.java diff --git a/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/IndividualProjectApplication.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/IndividualProjectApplication.java new file mode 100644 index 00000000..10295795 --- /dev/null +++ b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/IndividualProjectApplication.java @@ -0,0 +1,59 @@ +package es.princip.getp.domain.project.apply.model; + +import es.princip.getp.domain.common.model.AttachmentFile; +import es.princip.getp.domain.common.model.Duration; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.commission.model.ProjectId; +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDateTime; +import java.util.List; + +import static es.princip.getp.domain.project.apply.model.ProjectApplicationStatus.COMPLETED; + +@Getter +public class IndividualProjectApplication extends ProjectApplication { + + public static final String TYPE = "INDIVIDUAL"; + + @Builder + public IndividualProjectApplication( + final ProjectApplicationId id, + final PeopleId applicantId, + final ProjectId projectId, + final Duration expectedDuration, + final ProjectApplicationStatus status, + final String description, + final List attachmentFiles, + final LocalDateTime createdAt, + final LocalDateTime updatedAt + ) { + super( + id, + applicantId, + projectId, + expectedDuration, + status, + description, + attachmentFiles, + createdAt, + updatedAt + ); + } + + public static ProjectApplication of( + final PeopleId applicantId, + final ProjectId projectId, + final ProjectApplicationData data + ) { + return IndividualProjectApplication.builder() + .applicantId(applicantId) + .projectId(projectId) + .expectedDuration(data.getExpectedDuration()) + .description(data.getDescription()) + .attachmentFiles(data.getAttachmentFiles()) + .status(COMPLETED) + .build(); + } +} diff --git a/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplication.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplication.java new file mode 100644 index 00000000..a98fcc4c --- /dev/null +++ b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplication.java @@ -0,0 +1,57 @@ +package es.princip.getp.domain.project.apply.model; + +import es.princip.getp.domain.common.model.AttachmentFile; +import es.princip.getp.domain.common.model.Duration; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.commission.model.ProjectId; +import es.princip.getp.domain.support.BaseEntity; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; + +import java.time.LocalDateTime; +import java.util.List; + +@Getter +public abstract class ProjectApplication extends BaseEntity { + + protected ProjectApplicationId id; + @NotNull protected final PeopleId applicantId; + @NotNull protected final ProjectId projectId; + @NotNull protected Duration expectedDuration; + @NotNull protected ProjectApplicationStatus status; + @NotBlank protected String description; + protected final List<@NotNull AttachmentFile> attachmentFiles; + + public ProjectApplication( + final ProjectApplicationId id, + final PeopleId applicantId, + final ProjectId projectId, + final Duration expectedDuration, + final ProjectApplicationStatus status, + final String description, + final List attachmentFiles, + final LocalDateTime createdAt, + final LocalDateTime updatedAt + ) { + super(createdAt, updatedAt); + + this.id = id; + this.applicantId = applicantId; + this.projectId = projectId; + this.expectedDuration = expectedDuration; + this.status = status; + this.description = description; + this.attachmentFiles = attachmentFiles; + + validate(); + } + + public boolean isApplicant(final PeopleId peopleId) { + return this.applicantId.equals(peopleId); + } + + public boolean isCompleted() { + return this.status == ProjectApplicationStatus.COMPLETED; + } +} diff --git a/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplicationData.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplicationData.java new file mode 100644 index 00000000..e85542a1 --- /dev/null +++ b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplicationData.java @@ -0,0 +1,19 @@ +package es.princip.getp.domain.project.apply.model; + +import es.princip.getp.domain.common.model.AttachmentFile; +import es.princip.getp.domain.common.model.Duration; +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +@Getter +@Builder +@RequiredArgsConstructor +public class ProjectApplicationData { + + private final Duration expectedDuration; + private final String description; + private final List attachmentFiles; +} diff --git a/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplicationId.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplicationId.java new file mode 100644 index 00000000..fc13fed6 --- /dev/null +++ b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplicationId.java @@ -0,0 +1,19 @@ +package es.princip.getp.domain.project.apply.model; + +import es.princip.getp.domain.support.BaseModel; +import jakarta.validation.constraints.NotNull; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +@EqualsAndHashCode(callSuper = false) +public class ProjectApplicationId extends BaseModel { + + @NotNull private final Long value; + + public ProjectApplicationId(final Long value) { + this.value = value; + } +} diff --git a/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplicationStatus.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplicationStatus.java new file mode 100644 index 00000000..3d9f2d0a --- /dev/null +++ b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplicationStatus.java @@ -0,0 +1,10 @@ +package es.princip.getp.domain.project.apply.model; + +public enum ProjectApplicationStatus { + PENDING_TEAM_APPROVAL, // 팀원 승인 대기 + COMPLETED, // 지원 완료 + WAITING_MEETING, // 미팅 준비 + MEETING_COMPLETED, // 미팅 완료 + ACCEPTED, // 확정 + CLOSED // 모집 마감 +} diff --git a/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplicationType.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplicationType.java new file mode 100644 index 00000000..844457d9 --- /dev/null +++ b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplicationType.java @@ -0,0 +1,14 @@ +package es.princip.getp.domain.project.apply.model; + +public enum ProjectApplicationType { + INDIVIDUAL, // 개인 + TEAM; // 팀 + + public boolean isIndividual() { + return this == INDIVIDUAL; + } + + public boolean isTeam() { + return this == TEAM; + } +} diff --git a/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/TeamProjectApplication.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/TeamProjectApplication.java new file mode 100644 index 00000000..9ee38dbb --- /dev/null +++ b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/TeamProjectApplication.java @@ -0,0 +1,103 @@ +package es.princip.getp.domain.project.apply.model; + +import es.princip.getp.domain.common.model.AttachmentFile; +import es.princip.getp.domain.common.model.Duration; +import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.commission.model.ProjectId; +import jakarta.validation.constraints.NotNull; +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import static es.princip.getp.domain.project.apply.model.ProjectApplicationStatus.COMPLETED; +import static es.princip.getp.domain.project.apply.model.ProjectApplicationStatus.PENDING_TEAM_APPROVAL; +import static es.princip.getp.domain.project.apply.model.TeammateStatus.APPROVED; + +@Getter +public class TeamProjectApplication extends ProjectApplication { + + public static final String TYPE = "TEAM"; + private final Set<@NotNull Teammate> teammates; + + @Builder + public TeamProjectApplication( + final ProjectApplicationId id, + final PeopleId applicantId, + final ProjectId projectId, + final Duration expectedDuration, + final ProjectApplicationStatus status, + final String description, + final List attachmentFiles, + final Set teammates, + final LocalDateTime createdAt, + final LocalDateTime updatedAt + ) { + super( + id, + applicantId, + projectId, + expectedDuration, + status, + description, + attachmentFiles, + createdAt, + updatedAt + ); + this.teammates = teammates; + validate(); + } + + public static ProjectApplication of( + final PeopleId applicantId, + final ProjectId projectId, + final ProjectApplicationData data, + final Set teammates + ) { + final Set mapped = teammates.stream() + .map(teammate -> Teammate.builder() + .peopleId(teammate.getId()) + .status(TeammateStatus.PENDING) + .build()) + .collect(Collectors.toSet()); + return TeamProjectApplication.builder() + .applicantId(applicantId) + .projectId(projectId) + .expectedDuration(data.getExpectedDuration()) + .description(data.getDescription()) + .attachmentFiles(data.getAttachmentFiles()) + .teammates(mapped) + .status(PENDING_TEAM_APPROVAL) + .build(); + } + + public void approve(final People approver) { + if (status != PENDING_TEAM_APPROVAL) { + throw new IllegalStateException("팀원 승인 대기 상태가 아닙니다."); + } + final Teammate teammate = getTeammate(approver.getId()); + teammate.approve(); + if (isAllTeammatesApproved()) { + status = COMPLETED; + } + } + + private Teammate getTeammate(final PeopleId peopleId) { + return teammates.stream() + .filter(it -> it.getPeopleId().equals(peopleId)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("해당 팀으로부터 승인 신청을 받지 않았습니다.")); + } + + public boolean isTeammateApproved(final PeopleId peopleId) { + return getTeammate(peopleId).isApproved(); + } + + public boolean isAllTeammatesApproved() { + return teammates.stream().allMatch(it -> it.getStatus() == APPROVED); + } +} \ No newline at end of file diff --git a/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/Teammate.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/Teammate.java new file mode 100644 index 00000000..52af5e97 --- /dev/null +++ b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/Teammate.java @@ -0,0 +1,44 @@ +package es.princip.getp.domain.project.apply.model; + +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.support.BaseEntity; +import jakarta.validation.constraints.NotNull; +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDateTime; + +@Getter +public class Teammate extends BaseEntity { + + private TeammateId id; + @NotNull private final PeopleId peopleId; + @NotNull private TeammateStatus status; + + @Builder + public Teammate( + final TeammateId id, + final PeopleId peopleId, + final TeammateStatus status, + final LocalDateTime createdAt, + final LocalDateTime updatedAt + ) { + super(createdAt, updatedAt); + this.id = id; + this.peopleId = peopleId; + this.status = status; + validate(); + } + + public void approve() { + this.status = TeammateStatus.APPROVED; + } + + public void reject() { + this.status = TeammateStatus.REJECTED; + } + + public boolean isApproved() { + return this.status == TeammateStatus.APPROVED; + } +} diff --git a/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/TeammateId.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/TeammateId.java new file mode 100644 index 00000000..20653279 --- /dev/null +++ b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/TeammateId.java @@ -0,0 +1,19 @@ +package es.princip.getp.domain.project.apply.model; + +import es.princip.getp.domain.support.BaseModel; +import jakarta.validation.constraints.NotNull; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +@EqualsAndHashCode(callSuper = false) +public class TeammateId extends BaseModel { + + @NotNull private final Long value; + + public TeammateId(final Long value) { + this.value = value; + } +} diff --git a/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/TeammateStatus.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/TeammateStatus.java new file mode 100644 index 00000000..11f94671 --- /dev/null +++ b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/model/TeammateStatus.java @@ -0,0 +1,8 @@ +package es.princip.getp.domain.project.apply.model; + +public enum TeammateStatus { + PENDING, + APPROVED, + REJECTED, + EXPIRED +} diff --git a/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/service/IndividualProjectApplier.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/service/IndividualProjectApplier.java new file mode 100644 index 00000000..28e625d5 --- /dev/null +++ b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/service/IndividualProjectApplier.java @@ -0,0 +1,30 @@ +package es.princip.getp.domain.project.apply.service; + +import es.princip.getp.domain.people.exception.NotRegisteredPeopleProfileException; +import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.project.apply.exception.ClosedProjectApplicationException; +import es.princip.getp.domain.project.apply.model.IndividualProjectApplication; +import es.princip.getp.domain.project.apply.model.ProjectApplication; +import es.princip.getp.domain.project.apply.model.ProjectApplicationData; +import es.princip.getp.domain.project.commission.model.Project; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class IndividualProjectApplier { + + public ProjectApplication apply( + final People applicant, + final Project project, + final ProjectApplicationData data + ) { + if (project.isApplicationClosed()) { + throw new ClosedProjectApplicationException(); + } + if (!applicant.isProfileRegistered()) { + throw new NotRegisteredPeopleProfileException(); + } + return IndividualProjectApplication.of(applicant.getId(), project.getId(), data); + } +} diff --git a/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/service/TeamProjectApplier.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/service/TeamProjectApplier.java new file mode 100644 index 00000000..cd187e8e --- /dev/null +++ b/get-p-domain/src/main/java/es/princip/getp/domain/project/apply/service/TeamProjectApplier.java @@ -0,0 +1,38 @@ +package es.princip.getp.domain.project.apply.service; + +import es.princip.getp.domain.people.exception.NotRegisteredPeopleProfileException; +import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.project.apply.exception.ClosedProjectApplicationException; +import es.princip.getp.domain.project.apply.model.ProjectApplication; +import es.princip.getp.domain.project.apply.model.ProjectApplicationData; +import es.princip.getp.domain.project.apply.model.TeamProjectApplication; +import es.princip.getp.domain.project.commission.model.Project; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.Set; + +@Component +@RequiredArgsConstructor +public class TeamProjectApplier { + + public ProjectApplication apply( + final People applicant, + final Project project, + final ProjectApplicationData data, + final Set teammates + ) { + if (project.isApplicationClosed()) { + throw new ClosedProjectApplicationException(); + } + if (!applicant.isProfileRegistered()) { + throw new NotRegisteredPeopleProfileException(); + } + teammates.forEach(teammate -> { + if (!teammate.isProfileRegistered()) { + throw new NotRegisteredPeopleProfileException(); + } + }); + return TeamProjectApplication.of(applicant.getId(), project.getId(), data, teammates); + } +} diff --git a/src/main/java/es/princip/getp/domain/project/commission/exception/ApplicationDurationNotBeforeEstimatedDurationException.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/commission/exception/ApplicationDurationNotBeforeEstimatedDurationException.java similarity index 100% rename from src/main/java/es/princip/getp/domain/project/commission/exception/ApplicationDurationNotBeforeEstimatedDurationException.java rename to get-p-domain/src/main/java/es/princip/getp/domain/project/commission/exception/ApplicationDurationNotBeforeEstimatedDurationException.java diff --git a/src/main/java/es/princip/getp/domain/project/commission/exception/EndedApplicationDurationException.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/commission/exception/EndedApplicationDurationException.java similarity index 100% rename from src/main/java/es/princip/getp/domain/project/commission/exception/EndedApplicationDurationException.java rename to get-p-domain/src/main/java/es/princip/getp/domain/project/commission/exception/EndedApplicationDurationException.java diff --git a/src/main/java/es/princip/getp/domain/project/commission/model/MeetingType.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/commission/model/MeetingType.java similarity index 100% rename from src/main/java/es/princip/getp/domain/project/commission/model/MeetingType.java rename to get-p-domain/src/main/java/es/princip/getp/domain/project/commission/model/MeetingType.java diff --git a/src/main/java/es/princip/getp/domain/project/commission/model/Project.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/commission/model/Project.java similarity index 81% rename from src/main/java/es/princip/getp/domain/project/commission/model/Project.java rename to get-p-domain/src/main/java/es/princip/getp/domain/project/commission/model/Project.java index 5e760808..5e86acd0 100644 --- a/src/main/java/es/princip/getp/domain/project/commission/model/Project.java +++ b/get-p-domain/src/main/java/es/princip/getp/domain/project/commission/model/Project.java @@ -1,16 +1,16 @@ package es.princip.getp.domain.project.commission.model; -import es.princip.getp.domain.support.BaseEntity; import es.princip.getp.domain.client.model.Client; +import es.princip.getp.domain.client.model.ClientId; import es.princip.getp.domain.common.model.AttachmentFile; import es.princip.getp.domain.common.model.Duration; import es.princip.getp.domain.common.model.Hashtag; +import es.princip.getp.domain.support.BaseEntity; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Builder; import lombok.Getter; -import java.time.Clock; import java.time.LocalDateTime; import java.util.Collections; import java.util.List; @@ -18,33 +18,33 @@ @Getter public class Project extends BaseEntity { - private Long projectId; + private ProjectId id; @NotBlank private String title; // 제목 - @NotNull private Long payment; // 금액 + @NotNull private Long payment; // 성공 보수 + @NotNull private Long recruitmentCount; // 모집 인원 @NotNull private Duration applicationDuration; // 지원자 모집 기간 @NotNull private Duration estimatedDuration; // 예상 작업 기간 @NotBlank private String description; // 상세 설명 @NotNull private MeetingType meetingType; // 미팅 유형 @NotNull private ProjectCategory category; // 프로젝트 카테고리 @NotNull private ProjectStatus status; // 프로젝트 상태 - @NotNull private final Long clientId; // 의뢰자 - private int interestsCount; // 관심 수 + @NotNull private final ClientId clientId; // 의뢰자 private final List<@NotNull AttachmentFile> attachmentFiles; // 첨부 파일 목록 private final List<@NotNull Hashtag> hashtags; // 해시태그 목록 @Builder public Project( - final Long projectId, + final ProjectId id, final String title, final Long payment, + final Long recruitmentCount, final Duration applicationDuration, final Duration estimatedDuration, final String description, final MeetingType meetingType, final ProjectCategory category, final ProjectStatus status, - final Long clientId, - final int interestsCount, + final ClientId clientId, final List attachmentFiles, final List hashtags, final LocalDateTime createdAt, @@ -52,9 +52,10 @@ public Project( ) { super(createdAt, updatedAt); - this.projectId = projectId; + this.id = id; this.title = title; this.payment = payment; + this.recruitmentCount = recruitmentCount; this.applicationDuration = applicationDuration; this.estimatedDuration = estimatedDuration; this.description = description; @@ -62,7 +63,6 @@ public Project( this.category = category; this.status = status; this.clientId = clientId; - this.interestsCount = interestsCount; this.attachmentFiles = attachmentFiles; this.hashtags = hashtags; @@ -77,11 +77,11 @@ public List getHashtags() { return Collections.unmodifiableList(hashtags); } - public boolean isApplicationClosed(final Clock clock) { - return applicationDuration.isEnded(clock) || !status.isApplying(); + public boolean isApplicationClosed() { + return !status.isApplicationOpened(); } public boolean isClient(final Client client) { - return clientId.equals(client.getClientId()); + return clientId.equals(client.getId()); } } diff --git a/src/main/java/es/princip/getp/domain/project/commission/model/ProjectCategory.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/commission/model/ProjectCategory.java similarity index 68% rename from src/main/java/es/princip/getp/domain/project/commission/model/ProjectCategory.java rename to get-p-domain/src/main/java/es/princip/getp/domain/project/commission/model/ProjectCategory.java index 1a4ea5fc..f7ff3d9e 100644 --- a/src/main/java/es/princip/getp/domain/project/commission/model/ProjectCategory.java +++ b/get-p-domain/src/main/java/es/princip/getp/domain/project/commission/model/ProjectCategory.java @@ -1,5 +1,5 @@ package es.princip.getp.domain.project.commission.model; public enum ProjectCategory { - FRONTEND, BACKEND, WEB, PROGRAM + FRONTEND, BACKEND, WEB, PROGRAM, ETC } \ No newline at end of file diff --git a/src/main/java/es/princip/getp/domain/project/commission/model/ProjectData.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/commission/model/ProjectData.java similarity index 83% rename from src/main/java/es/princip/getp/domain/project/commission/model/ProjectData.java rename to get-p-domain/src/main/java/es/princip/getp/domain/project/commission/model/ProjectData.java index 15f481e2..b7b19586 100644 --- a/src/main/java/es/princip/getp/domain/project/commission/model/ProjectData.java +++ b/get-p-domain/src/main/java/es/princip/getp/domain/project/commission/model/ProjectData.java @@ -1,5 +1,6 @@ package es.princip.getp.domain.project.commission.model; +import es.princip.getp.domain.client.model.ClientId; import es.princip.getp.domain.common.model.AttachmentFile; import es.princip.getp.domain.common.model.Duration; import es.princip.getp.domain.common.model.Hashtag; @@ -9,12 +10,13 @@ public record ProjectData( String title, Long payment, + Long recruitmentCount, Duration applicationDuration, Duration estimatedDuration, String description, MeetingType meetingType, ProjectCategory category, - Long clientId, + ClientId clientId, List attachmentFiles, List hashtags ) { diff --git a/get-p-domain/src/main/java/es/princip/getp/domain/project/commission/model/ProjectId.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/commission/model/ProjectId.java new file mode 100644 index 00000000..bebfdeb1 --- /dev/null +++ b/get-p-domain/src/main/java/es/princip/getp/domain/project/commission/model/ProjectId.java @@ -0,0 +1,19 @@ +package es.princip.getp.domain.project.commission.model; + +import es.princip.getp.domain.support.BaseModel; +import jakarta.validation.constraints.NotNull; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +@EqualsAndHashCode(callSuper = false) +public class ProjectId extends BaseModel { + + @NotNull private final Long value; + + public ProjectId(final Long value) { + this.value = value; + } +} diff --git a/src/main/java/es/princip/getp/domain/project/commission/model/ProjectStatus.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/commission/model/ProjectStatus.java similarity index 78% rename from src/main/java/es/princip/getp/domain/project/commission/model/ProjectStatus.java rename to get-p-domain/src/main/java/es/princip/getp/domain/project/commission/model/ProjectStatus.java index e93e13e4..a606caa6 100644 --- a/src/main/java/es/princip/getp/domain/project/commission/model/ProjectStatus.java +++ b/get-p-domain/src/main/java/es/princip/getp/domain/project/commission/model/ProjectStatus.java @@ -7,13 +7,16 @@ public enum ProjectStatus { PREPARING, // 모집 준비중 - APPLYING, // 모집중 + APPLICATION_OPENED, // 모집중 + APPLICATION_CLOSED, // 모집 마감 + MEETING, // 미팅 중 + CONFIRMED, // 확정 PROGRESSING, // 진행중 COMPLETED, // 완료 CANCELLED; // 취소 /** - * 지원자 모집 기간을 기준으로 프로젝트 상태를 결정합니다. 지원자 모집 기간이 시작됐다면 APPLYING, 아니라면 PREPARING입니다. + * 지원자 모집 기간을 기준으로 프로젝트 상태를 결정합니다. 지원자 모집 기간이 시작됐다면 APPLICATION_OPENED, 아니라면 PREPARING입니다. * * @param applicationDuration 지원자 모집 기간 * @return 프로젝트 상태 @@ -21,7 +24,7 @@ public enum ProjectStatus { public static ProjectStatus determineStatus(final Duration applicationDuration, final Clock clock) { final LocalDate now = LocalDate.now(clock); if (applicationDuration.isBetween(now)) { - return APPLYING; + return APPLICATION_OPENED; } return PREPARING; } @@ -30,8 +33,8 @@ public boolean isPreparing() { return this == PREPARING; } - public boolean isApplying() { - return this == APPLYING; + public boolean isApplicationOpened() { + return this == APPLICATION_OPENED; } public boolean isProgressing() { diff --git a/src/main/java/es/princip/getp/domain/project/commission/service/ProjectCommissioner.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/commission/service/ProjectCommissioner.java similarity index 97% rename from src/main/java/es/princip/getp/domain/project/commission/service/ProjectCommissioner.java rename to get-p-domain/src/main/java/es/princip/getp/domain/project/commission/service/ProjectCommissioner.java index acc737c7..e1fc65cb 100644 --- a/src/main/java/es/princip/getp/domain/project/commission/service/ProjectCommissioner.java +++ b/get-p-domain/src/main/java/es/princip/getp/domain/project/commission/service/ProjectCommissioner.java @@ -39,6 +39,7 @@ private static Project buildProject(final ProjectData data, final ProjectStatus return Project.builder() .title(data.title()) .payment(data.payment()) + .recruitmentCount(data.recruitmentCount()) .applicationDuration(data.applicationDuration()) .estimatedDuration(data.estimatedDuration()) .description(data.description()) diff --git a/src/main/java/es/princip/getp/domain/project/meeting/model/ProjectMeeting.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/meeting/model/ProjectMeeting.java similarity index 82% rename from src/main/java/es/princip/getp/domain/project/meeting/model/ProjectMeeting.java rename to get-p-domain/src/main/java/es/princip/getp/domain/project/meeting/model/ProjectMeeting.java index a64c9ed1..48fac4f8 100644 --- a/src/main/java/es/princip/getp/domain/project/meeting/model/ProjectMeeting.java +++ b/get-p-domain/src/main/java/es/princip/getp/domain/project/meeting/model/ProjectMeeting.java @@ -1,8 +1,10 @@ package es.princip.getp.domain.project.meeting.model; -import es.princip.getp.domain.support.BaseEntity; import es.princip.getp.domain.common.model.MeetingSchedule; import es.princip.getp.domain.common.model.PhoneNumber; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.commission.model.ProjectId; +import es.princip.getp.domain.support.BaseEntity; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Builder; @@ -14,8 +16,8 @@ public class ProjectMeeting extends BaseEntity { private Long meetingId; - @NotNull private Long projectId; - @NotNull private Long applicantId; + @NotNull private ProjectId projectId; + @NotNull private PeopleId applicantId; @NotBlank private String location; @NotNull private MeetingSchedule schedule; @NotNull private PhoneNumber phoneNumber; @@ -24,8 +26,8 @@ public class ProjectMeeting extends BaseEntity { @Builder public ProjectMeeting( final Long meetingId, - final Long projectId, - final Long applicantId, + final ProjectId projectId, + final PeopleId applicantId, final String location, final MeetingSchedule schedule, final PhoneNumber phoneNumber, diff --git a/src/main/java/es/princip/getp/domain/project/meeting/model/ProjectMeetingId.java b/get-p-domain/src/main/java/es/princip/getp/domain/project/meeting/model/ProjectMeetingId.java similarity index 100% rename from src/main/java/es/princip/getp/domain/project/meeting/model/ProjectMeetingId.java rename to get-p-domain/src/main/java/es/princip/getp/domain/project/meeting/model/ProjectMeetingId.java diff --git a/src/main/java/es/princip/getp/domain/serviceTerm/model/ServiceTerm.java b/get-p-domain/src/main/java/es/princip/getp/domain/serviceTerm/model/ServiceTerm.java similarity index 100% rename from src/main/java/es/princip/getp/domain/serviceTerm/model/ServiceTerm.java rename to get-p-domain/src/main/java/es/princip/getp/domain/serviceTerm/model/ServiceTerm.java diff --git a/src/main/java/es/princip/getp/domain/serviceTerm/model/ServiceTermTag.java b/get-p-domain/src/main/java/es/princip/getp/domain/serviceTerm/model/ServiceTermTag.java similarity index 100% rename from src/main/java/es/princip/getp/domain/serviceTerm/model/ServiceTermTag.java rename to get-p-domain/src/main/java/es/princip/getp/domain/serviceTerm/model/ServiceTermTag.java diff --git a/src/main/java/es/princip/getp/domain/support/BaseEntity.java b/get-p-domain/src/main/java/es/princip/getp/domain/support/BaseEntity.java similarity index 100% rename from src/main/java/es/princip/getp/domain/support/BaseEntity.java rename to get-p-domain/src/main/java/es/princip/getp/domain/support/BaseEntity.java diff --git a/src/main/java/es/princip/getp/domain/support/BaseModel.java b/get-p-domain/src/main/java/es/princip/getp/domain/support/BaseModel.java similarity index 100% rename from src/main/java/es/princip/getp/domain/support/BaseModel.java rename to get-p-domain/src/main/java/es/princip/getp/domain/support/BaseModel.java diff --git a/src/main/java/es/princip/getp/domain/support/DomainLogicException.java b/get-p-domain/src/main/java/es/princip/getp/domain/support/DomainLogicException.java similarity index 100% rename from src/main/java/es/princip/getp/domain/support/DomainLogicException.java rename to get-p-domain/src/main/java/es/princip/getp/domain/support/DomainLogicException.java diff --git a/src/main/java/es/princip/getp/domain/support/ErrorDescription.java b/get-p-domain/src/main/java/es/princip/getp/domain/support/ErrorDescription.java similarity index 100% rename from src/main/java/es/princip/getp/domain/support/ErrorDescription.java rename to get-p-domain/src/main/java/es/princip/getp/domain/support/ErrorDescription.java diff --git a/src/main/java/es/princip/getp/domain/support/ErrorDescriptionException.java b/get-p-domain/src/main/java/es/princip/getp/domain/support/ErrorDescriptionException.java similarity index 100% rename from src/main/java/es/princip/getp/domain/support/ErrorDescriptionException.java rename to get-p-domain/src/main/java/es/princip/getp/domain/support/ErrorDescriptionException.java diff --git a/src/main/java/es/princip/getp/domain/support/NotValidDomainModelException.java b/get-p-domain/src/main/java/es/princip/getp/domain/support/NotValidDomainModelException.java similarity index 100% rename from src/main/java/es/princip/getp/domain/support/NotValidDomainModelException.java rename to get-p-domain/src/main/java/es/princip/getp/domain/support/NotValidDomainModelException.java diff --git a/get-p-domain/src/main/resources/junit-platform.properties b/get-p-domain/src/main/resources/junit-platform.properties new file mode 100644 index 00000000..0627373a --- /dev/null +++ b/get-p-domain/src/main/resources/junit-platform.properties @@ -0,0 +1,5 @@ +junit.jupiter.execution.parallel.enabled=true +junit.jupiter.execution.parallel.mode.classes.default=concurrent +junit.jupiter.execution.parallel.mode.default=same_thread +junit.jupiter.execution.parallel.config.strategy=dynamic +junit.jupiter.execution.parallel.config.dynamic.factor=1 \ No newline at end of file diff --git a/src/test/java/es/princip/getp/domain/client/model/AddressTest.java b/get-p-domain/src/test/java/es/princip/getp/domain/client/model/AddressTest.java similarity index 100% rename from src/test/java/es/princip/getp/domain/client/model/AddressTest.java rename to get-p-domain/src/test/java/es/princip/getp/domain/client/model/AddressTest.java diff --git a/src/test/java/es/princip/getp/domain/common/infrastructure/StubClockHolder.java b/get-p-domain/src/test/java/es/princip/getp/domain/common/infrastructure/StubClockHolder.java similarity index 100% rename from src/test/java/es/princip/getp/domain/common/infrastructure/StubClockHolder.java rename to get-p-domain/src/test/java/es/princip/getp/domain/common/infrastructure/StubClockHolder.java diff --git a/src/test/java/es/princip/getp/domain/client/model/BankAccountTest.java b/get-p-domain/src/test/java/es/princip/getp/domain/common/model/BankAccountTest.java similarity index 97% rename from src/test/java/es/princip/getp/domain/client/model/BankAccountTest.java rename to get-p-domain/src/test/java/es/princip/getp/domain/common/model/BankAccountTest.java index f3b426f3..8ae298c2 100644 --- a/src/test/java/es/princip/getp/domain/client/model/BankAccountTest.java +++ b/get-p-domain/src/test/java/es/princip/getp/domain/common/model/BankAccountTest.java @@ -1,4 +1,4 @@ -package es.princip.getp.domain.client.model; +package es.princip.getp.domain.common.model; import es.princip.getp.domain.support.NotValidDomainModelException; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/es/princip/getp/domain/common/model/DurationTest.java b/get-p-domain/src/test/java/es/princip/getp/domain/common/model/DurationTest.java similarity index 100% rename from src/test/java/es/princip/getp/domain/common/model/DurationTest.java rename to get-p-domain/src/test/java/es/princip/getp/domain/common/model/DurationTest.java diff --git a/src/test/java/es/princip/getp/domain/common/model/EmailTest.java b/get-p-domain/src/test/java/es/princip/getp/domain/common/model/EmailTest.java similarity index 100% rename from src/test/java/es/princip/getp/domain/common/model/EmailTest.java rename to get-p-domain/src/test/java/es/princip/getp/domain/common/model/EmailTest.java diff --git a/src/test/java/es/princip/getp/domain/member/infra/SimplePasswordEncoder.java b/get-p-domain/src/test/java/es/princip/getp/domain/member/infra/SimplePasswordEncoder.java similarity index 85% rename from src/test/java/es/princip/getp/domain/member/infra/SimplePasswordEncoder.java rename to get-p-domain/src/test/java/es/princip/getp/domain/member/infra/SimplePasswordEncoder.java index d2c346b3..19a5717b 100644 --- a/src/test/java/es/princip/getp/domain/member/infra/SimplePasswordEncoder.java +++ b/get-p-domain/src/test/java/es/princip/getp/domain/member/infra/SimplePasswordEncoder.java @@ -1,8 +1,10 @@ package es.princip.getp.domain.member.infra; -import org.springframework.security.crypto.password.PasswordEncoder; + +import es.princip.getp.domain.member.PasswordEncoder; public class SimplePasswordEncoder implements PasswordEncoder { + @Override public String encode(final CharSequence rawPassword) { return rawPassword.toString(); diff --git a/src/test/java/es/princip/getp/domain/member/model/MemberTest.java b/get-p-domain/src/test/java/es/princip/getp/domain/member/model/MemberTest.java similarity index 98% rename from src/test/java/es/princip/getp/domain/member/model/MemberTest.java rename to get-p-domain/src/test/java/es/princip/getp/domain/member/model/MemberTest.java index 3e5a737c..10fe0c14 100644 --- a/src/test/java/es/princip/getp/domain/member/model/MemberTest.java +++ b/get-p-domain/src/test/java/es/princip/getp/domain/member/model/MemberTest.java @@ -1,6 +1,7 @@ package es.princip.getp.domain.member.model; import es.princip.getp.domain.common.model.PhoneNumber; +import es.princip.getp.domain.member.PasswordEncoder; import es.princip.getp.domain.member.exception.NotAgreedAllRequiredServiceTermException; import es.princip.getp.domain.member.infra.SimplePasswordEncoder; import es.princip.getp.domain.serviceTerm.model.ServiceTermTag; @@ -9,7 +10,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.security.crypto.password.PasswordEncoder; import java.util.Set; diff --git a/src/test/java/es/princip/getp/domain/member/model/PasswordTest.java b/get-p-domain/src/test/java/es/princip/getp/domain/member/model/PasswordTest.java similarity index 97% rename from src/test/java/es/princip/getp/domain/member/model/PasswordTest.java rename to get-p-domain/src/test/java/es/princip/getp/domain/member/model/PasswordTest.java index 846bddd4..27a370c3 100644 --- a/src/test/java/es/princip/getp/domain/member/model/PasswordTest.java +++ b/get-p-domain/src/test/java/es/princip/getp/domain/member/model/PasswordTest.java @@ -1,13 +1,13 @@ package es.princip.getp.domain.member.model; -import es.princip.getp.domain.support.NotValidDomainModelException; +import es.princip.getp.domain.member.PasswordEncoder; import es.princip.getp.domain.member.infra.SimplePasswordEncoder; +import es.princip.getp.domain.support.NotValidDomainModelException; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.security.crypto.password.PasswordEncoder; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/src/test/java/es/princip/getp/domain/people/model/PeopleTest.java b/get-p-domain/src/test/java/es/princip/getp/domain/people/model/PeopleTest.java similarity index 100% rename from src/test/java/es/princip/getp/domain/people/model/PeopleTest.java rename to get-p-domain/src/test/java/es/princip/getp/domain/people/model/PeopleTest.java diff --git a/get-p-domain/src/test/java/es/princip/getp/domain/project/apply/model/TeamProjectApplicationTest.java b/get-p-domain/src/test/java/es/princip/getp/domain/project/apply/model/TeamProjectApplicationTest.java new file mode 100644 index 00000000..194f4dc6 --- /dev/null +++ b/get-p-domain/src/test/java/es/princip/getp/domain/project/apply/model/TeamProjectApplicationTest.java @@ -0,0 +1,113 @@ +package es.princip.getp.domain.project.apply.model; + +import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.commission.model.ProjectId; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Set; + +import static es.princip.getp.domain.project.apply.model.ProjectApplicationStatus.PENDING_TEAM_APPROVAL; +import static es.princip.getp.fixture.project.ProjectApplicationFixture.teamProjectApplication; +import static es.princip.getp.fixture.project.ProjectApplicationFixture.teammate; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.params.provider.EnumSource.Mode.EXCLUDE; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +@ExtendWith(MockitoExtension.class) +class TeamProjectApplicationTest { + + @Test + void 피플은_팀원_신청을_승인할_수_있다() { + final PeopleId applicantId = new PeopleId(1L); + final ProjectId projectId = new ProjectId(1L); + final People teammate = mock(People.class); + final PeopleId teammateId = new PeopleId(2L); + given(teammate.getId()).willReturn(teammateId); + final Set teammates = Set.of( + teammate(teammateId, TeammateStatus.PENDING), + teammate(new PeopleId(3L), TeammateStatus.PENDING) + ); + final TeamProjectApplication application = teamProjectApplication( + applicantId, + projectId, + PENDING_TEAM_APPROVAL, + teammates + ); + + application.approve(teammate); + + assertThat(application.isTeammateApproved(teammateId)).isTrue(); + assertThat(application.isCompleted()).isFalse(); + } + + @Test + void 팀원이_모두_승인해야_프로젝트_지원이_완료된다() { + final PeopleId applicantId = new PeopleId(1L); + final ProjectId projectId = new ProjectId(1L); + final People teammate = mock(People.class); + final PeopleId teammateId = new PeopleId(2L); + given(teammate.getId()).willReturn(teammateId); + final Set teammates = Set.of( + teammate(teammateId, TeammateStatus.PENDING) + ); + final TeamProjectApplication application = teamProjectApplication( + applicantId, + projectId, + PENDING_TEAM_APPROVAL, + teammates + ); + + application.approve(teammate); + + assertThat(application.isCompleted()).isTrue(); + } + + @ParameterizedTest + @EnumSource(mode = EXCLUDE, names = {"PENDING_TEAM_APPROVAL"}) + void 팀원_승인_대기_상태가_아니면_승인할_수_없다(final ProjectApplicationStatus status) { + final PeopleId applicantId = new PeopleId(1L); + final ProjectId projectId = new ProjectId(1L); + final People teammate = mock(People.class); + final PeopleId teammateId = new PeopleId(2L); + final Set teammates = Set.of( + teammate(teammateId, TeammateStatus.PENDING) + ); + final TeamProjectApplication application = teamProjectApplication( + applicantId, + projectId, + status, + teammates + ); + + assertThatThrownBy(() -> application.approve(teammate)) + .isInstanceOf(IllegalStateException.class); + } + + @Test + void 팀원_승인_신청을_받지_않으면_승인할_수_없다() { + final PeopleId applicantId = new PeopleId(1L); + final ProjectId projectId = new ProjectId(1L); + final People teammate = mock(People.class); + final PeopleId teammateId = new PeopleId(2L); + given(teammate.getId()).willReturn(teammateId); + final Set teammates = Set.of( + teammate(new PeopleId(3L), TeammateStatus.PENDING) + ); + final TeamProjectApplication application = teamProjectApplication( + applicantId, + projectId, + PENDING_TEAM_APPROVAL, + teammates + ); + + assertThatThrownBy(() -> application.approve(teammate)) + .isInstanceOf(IllegalArgumentException.class); + } +} \ No newline at end of file diff --git a/get-p-domain/src/test/java/es/princip/getp/domain/project/apply/service/IndividualProjectApplierTest.java b/get-p-domain/src/test/java/es/princip/getp/domain/project/apply/service/IndividualProjectApplierTest.java new file mode 100644 index 00000000..2ec8fac6 --- /dev/null +++ b/get-p-domain/src/test/java/es/princip/getp/domain/project/apply/service/IndividualProjectApplierTest.java @@ -0,0 +1,86 @@ +package es.princip.getp.domain.project.apply.service; + +import es.princip.getp.domain.client.model.ClientId; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.exception.NotRegisteredPeopleProfileException; +import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.apply.exception.ClosedProjectApplicationException; +import es.princip.getp.domain.project.apply.model.IndividualProjectApplication; +import es.princip.getp.domain.project.apply.model.ProjectApplication; +import es.princip.getp.domain.project.apply.model.ProjectApplicationData; +import es.princip.getp.domain.project.commission.model.Project; +import es.princip.getp.domain.project.commission.model.ProjectId; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import static es.princip.getp.domain.project.commission.model.ProjectStatus.APPLICATION_OPENED; +import static es.princip.getp.domain.project.commission.model.ProjectStatus.PROGRESSING; +import static es.princip.getp.fixture.people.PeopleFixture.people; +import static es.princip.getp.fixture.people.PeopleFixture.peopleWithoutProfile; +import static es.princip.getp.fixture.project.AttachmentFileFixture.attachmentFiles; +import static es.princip.getp.fixture.project.ProjectApplicationFixture.DESCRIPTION; +import static es.princip.getp.fixture.project.ProjectApplicationFixture.expectedDuration; +import static es.princip.getp.fixture.project.ProjectFixture.project; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.spy; + +@ExtendWith(MockitoExtension.class) +class IndividualProjectApplierTest { + + private final IndividualProjectApplier applier = new IndividualProjectApplier(); + + @Test + void 피플은_개인으로_프로젝트에_지원할_수_있다() { + final People applicant = spy(people(new MemberId(1L))); + given(applicant.getId()).willReturn(new PeopleId(1L)); + final Project project = spy(project(new ClientId(1L), APPLICATION_OPENED)); + given(project.getId()).willReturn(new ProjectId(1L)); + final ProjectApplicationData data = new ProjectApplicationData( + expectedDuration(), + DESCRIPTION, + attachmentFiles() + ); + final ProjectApplication expected = IndividualProjectApplication.of( + applicant.getId(), + project.getId(), + data + ); + + final ProjectApplication application = applier.apply(applicant, project, data); + + assertThat(application).isInstanceOf(IndividualProjectApplication.class); + assertThat(application).usingRecursiveComparison().isEqualTo(expected); + } + + @Test + void 피플은_프로필을_등록하지_않으면_지원할_수_없다() { + final People applicant = spy(peopleWithoutProfile(new MemberId(1L))); + final Project project = spy(project(new ClientId(1L), APPLICATION_OPENED)); + final ProjectApplicationData data = new ProjectApplicationData( + expectedDuration(), + DESCRIPTION, + attachmentFiles() + ); + + assertThatThrownBy(() -> applier.apply(applicant, project, data)) + .isInstanceOf(NotRegisteredPeopleProfileException.class); + } + + @Test + void 프로젝트_지원이_닫힌_경우_지원할_수_없다() { + final People applicant = spy(people(new MemberId(1L))); + final Project project = spy(project(new ClientId(1L), PROGRESSING)); + final ProjectApplicationData data = new ProjectApplicationData( + expectedDuration(), + DESCRIPTION, + attachmentFiles() + ); + + assertThatThrownBy(() -> applier.apply(applicant, project, data)) + .isInstanceOf(ClosedProjectApplicationException.class); + } +} \ No newline at end of file diff --git a/get-p-domain/src/test/java/es/princip/getp/domain/project/apply/service/TeamProjectApplierTest.java b/get-p-domain/src/test/java/es/princip/getp/domain/project/apply/service/TeamProjectApplierTest.java new file mode 100644 index 00000000..6c3221f8 --- /dev/null +++ b/get-p-domain/src/test/java/es/princip/getp/domain/project/apply/service/TeamProjectApplierTest.java @@ -0,0 +1,123 @@ +package es.princip.getp.domain.project.apply.service; + +import es.princip.getp.domain.client.model.ClientId; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.exception.NotRegisteredPeopleProfileException; +import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.apply.exception.ClosedProjectApplicationException; +import es.princip.getp.domain.project.apply.model.ProjectApplication; +import es.princip.getp.domain.project.apply.model.ProjectApplicationData; +import es.princip.getp.domain.project.apply.model.TeamProjectApplication; +import es.princip.getp.domain.project.commission.model.Project; +import es.princip.getp.domain.project.commission.model.ProjectId; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Set; + +import static es.princip.getp.domain.project.commission.model.ProjectStatus.APPLICATION_OPENED; +import static es.princip.getp.domain.project.commission.model.ProjectStatus.PROGRESSING; +import static es.princip.getp.fixture.people.PeopleFixture.people; +import static es.princip.getp.fixture.people.PeopleFixture.peopleWithoutProfile; +import static es.princip.getp.fixture.project.AttachmentFileFixture.attachmentFiles; +import static es.princip.getp.fixture.project.ProjectApplicationFixture.DESCRIPTION; +import static es.princip.getp.fixture.project.ProjectApplicationFixture.expectedDuration; +import static es.princip.getp.fixture.project.ProjectFixture.project; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.spy; + +@ExtendWith(MockitoExtension.class) +class TeamProjectApplierTest { + + private final TeamProjectApplier applier = new TeamProjectApplier(); + + @Test + void 피플은_팀으로_프로젝트에_지원할_수_있다() { + final People applicant = spy(people(new MemberId(1L))); + given(applicant.getId()).willReturn(new PeopleId(1L)); + final Project project = spy(project(new ClientId(1L), APPLICATION_OPENED)); + given(project.getId()).willReturn(new ProjectId(1L)); + final ProjectApplicationData data = new ProjectApplicationData( + expectedDuration(), + DESCRIPTION, + attachmentFiles() + ); + final Set teammates = Set.of( + spy(people(new MemberId(2L))), + spy(people(new MemberId(3L))) + ); + teammates.forEach(teammate -> { + final Long value = teammate.getMemberId().getValue(); + given(teammate.getId()).willReturn(new PeopleId(value)); + }); + final ProjectApplication expected = TeamProjectApplication.of( + applicant.getId(), + project.getId(), + data, + teammates + ); + + final ProjectApplication application = applier.apply(applicant, project, data, teammates); + + assertThat(application).isInstanceOf(TeamProjectApplication.class); + assertThat(application).usingRecursiveComparison().isEqualTo(expected); + } + + @Test + void 피플은_지원자가_프로필을_등록하지_않으면_지원할_수_없다() { + final People applicant = spy(peopleWithoutProfile(new MemberId(1L))); + final Project project = spy(project(new ClientId(1L), APPLICATION_OPENED)); + final ProjectApplicationData data = new ProjectApplicationData( + expectedDuration(), + DESCRIPTION, + attachmentFiles() + ); + final Set teammates = Set.of( + people(new MemberId(2L)), + people(new MemberId(3L)) + ); + + assertThatThrownBy(() -> applier.apply(applicant, project, data, teammates)) + .isInstanceOf(NotRegisteredPeopleProfileException.class); + } + + @Test + void 피플은_팀원이_모두_프로필을_등록하지_않으면_지원할_수_없다() { + final People applicant = spy(people(new MemberId(1L))); + final Project project = spy(project(new ClientId(1L), APPLICATION_OPENED)); + final ProjectApplicationData data = new ProjectApplicationData( + expectedDuration(), + DESCRIPTION, + attachmentFiles() + ); + final Set teammates = Set.of( + people(new MemberId(2L)), + peopleWithoutProfile(new MemberId(3L)) + ); + + assertThatThrownBy(() -> applier.apply(applicant, project, data, teammates)) + .isInstanceOf(NotRegisteredPeopleProfileException.class); + } + + @Test + void 프로젝트_지원이_닫힌_경우_지원할_수_없다() { + final People applicant = spy(people(new MemberId(1L))); + final Project project = spy(project(new ClientId(1L), PROGRESSING)); + final ProjectApplicationData data = new ProjectApplicationData( + expectedDuration(), + DESCRIPTION, + attachmentFiles() + ); + final Set teammates = Set.of( + people(new MemberId(2L)), + people(new MemberId(3L)) + ); + + assertThatThrownBy(() -> applier.apply(applicant, project, data, teammates)) + .isInstanceOf(ClosedProjectApplicationException.class); + } +} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/domain/project/commission/model/ProjectStatusTest.java b/get-p-domain/src/test/java/es/princip/getp/domain/project/commission/model/ProjectStatusTest.java similarity index 94% rename from src/test/java/es/princip/getp/domain/project/commission/model/ProjectStatusTest.java rename to get-p-domain/src/test/java/es/princip/getp/domain/project/commission/model/ProjectStatusTest.java index ee193ac9..b2f66377 100644 --- a/src/test/java/es/princip/getp/domain/project/commission/model/ProjectStatusTest.java +++ b/get-p-domain/src/test/java/es/princip/getp/domain/project/commission/model/ProjectStatusTest.java @@ -23,7 +23,7 @@ class ProjectStatusTest { final ProjectStatus status = ProjectStatus.determineStatus(applicationDuration, clock); - assertThat(status).isEqualTo(ProjectStatus.APPLYING); + assertThat(status).isEqualTo(ProjectStatus.APPLICATION_OPENED); } @Test diff --git a/src/test/java/es/princip/getp/domain/project/commission/model/ProjectTest.java b/get-p-domain/src/test/java/es/princip/getp/domain/project/commission/model/ProjectTest.java similarity index 59% rename from src/test/java/es/princip/getp/domain/project/commission/model/ProjectTest.java rename to get-p-domain/src/test/java/es/princip/getp/domain/project/commission/model/ProjectTest.java index 065be3fc..c5f4be79 100644 --- a/src/test/java/es/princip/getp/domain/project/commission/model/ProjectTest.java +++ b/get-p-domain/src/test/java/es/princip/getp/domain/project/commission/model/ProjectTest.java @@ -1,7 +1,7 @@ package es.princip.getp.domain.project.commission.model; import es.princip.getp.domain.client.model.Client; -import es.princip.getp.domain.common.infrastructure.StubClockHolder; +import es.princip.getp.domain.client.model.ClientId; import es.princip.getp.domain.common.model.Duration; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -11,10 +11,9 @@ import org.junit.jupiter.params.provider.EnumSource; import org.mockito.junit.jupiter.MockitoExtension; -import java.time.Clock; import java.time.LocalDate; -import static es.princip.getp.domain.project.commission.model.ProjectStatus.APPLYING; +import static es.princip.getp.domain.project.commission.model.ProjectStatus.APPLICATION_OPENED; import static es.princip.getp.fixture.project.ProjectFixture.project; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; @@ -27,42 +26,13 @@ class ProjectTest { @Nested class 지원이_가능한지_확인한다 { - private final LocalDate now = LocalDate.of(2024, 7, 1); - private final Clock clock = new StubClockHolder(now).getClock(); - - @Test - void 지원자_모집_기간이_아직_남았으면_지원이_가능하다() { - final Project project = project( - 1L, - APPLYING, - Duration.of( - LocalDate.of(2024, 7, 1), - LocalDate.of(2024, 7, 31) - ) - ); - - assertThat(project.isApplicationClosed(clock)).isFalse(); - } - - @Test - void 지원자_모집_기간이_끝나면_지원은_할_수_없다() { - final Project project = project( - 1L, - APPLYING, - Duration.of( - LocalDate.of(2024, 6, 1), - LocalDate.of(2024, 6, 30) - ) - ); - - assertThat(project.isApplicationClosed(clock)).isTrue(); - } + private final ClientId clientId = new ClientId(1L); @ParameterizedTest @EnumSource(value = ProjectStatus.class, names = {"PREPARING", "PROGRESSING", "COMPLETED", "CANCELLED"}) void 프로젝트_상태가_모집_중이_아니면_지원은_할_수_없다(final ProjectStatus status) { final Project project = project( - 1L, + clientId, status, Duration.of( LocalDate.of(2024, 7, 1), @@ -70,26 +40,27 @@ class 지원이_가능한지_확인한다 { ) ); - assertThat(project.isApplicationClosed(clock)).isTrue(); + assertThat(project.isApplicationClosed()).isTrue(); } } @Nested class 주어진_의뢰자가_프로젝트의_의뢰자인지_확인한다 { - private final Project project = project(1L, APPLYING); + private final ClientId clientId = new ClientId(1L); + private final Project project = project(clientId, APPLICATION_OPENED); private final Client client = mock(Client.class); @Test void 주어진_의뢰자와_의뢰자_ID가_같으면_프로젝트의_의뢰자다() { - given(client.getClientId()).willReturn(1L); + given(client.getId()).willReturn(clientId); assertThat(project.isClient(client)).isTrue(); } @Test void 주어진_의뢰자와_의뢰자_ID가_다르면_프로젝트의_의뢰자가_아니다() { - given(client.getClientId()).willReturn(2L); + given(client.getId()).willReturn(new ClientId(2L)); assertThat(project.isClient(client)).isFalse(); } diff --git a/src/test/java/es/princip/getp/domain/project/commission/service/ProjectCommissionerTest.java b/get-p-domain/src/test/java/es/princip/getp/domain/project/commission/service/ProjectCommissionerTest.java similarity index 76% rename from src/test/java/es/princip/getp/domain/project/commission/service/ProjectCommissionerTest.java rename to get-p-domain/src/test/java/es/princip/getp/domain/project/commission/service/ProjectCommissionerTest.java index 2af5923d..22011189 100644 --- a/src/test/java/es/princip/getp/domain/project/commission/service/ProjectCommissionerTest.java +++ b/get-p-domain/src/test/java/es/princip/getp/domain/project/commission/service/ProjectCommissionerTest.java @@ -1,5 +1,6 @@ package es.princip.getp.domain.project.commission.service; +import es.princip.getp.domain.client.model.ClientId; import es.princip.getp.domain.common.infrastructure.StubClockHolder; import es.princip.getp.domain.common.model.Duration; import es.princip.getp.domain.common.service.ClockHolder; @@ -15,6 +16,7 @@ import static es.princip.getp.fixture.common.HashtagFixture.hashtags; import static es.princip.getp.fixture.project.AttachmentFileFixture.attachmentFiles; +import static es.princip.getp.fixture.project.ProjectFixture.*; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -25,10 +27,7 @@ class ProjectCommissionerTest { @Test void 지원자_모집_기간이_오늘보다_이전일_수_없다() { - final Long clientId = 1L; - final ProjectData data = new ProjectData( - "프로젝트 제목", - 1_000_000L, + final ProjectData data = projectData( Duration.of( LocalDate.of(2024, 6, 1), LocalDate.of(2024, 6, 30) @@ -36,13 +35,7 @@ class ProjectCommissionerTest { Duration.of( LocalDate.of(2024, 7, 1), LocalDate.of(2024, 7, 31) - ), - "프로젝트 설명", - MeetingType.IN_PERSON, - ProjectCategory.BACKEND, - clientId, - attachmentFiles(), - hashtags() + ) ); assertThatThrownBy(() -> projectCommissioner.commissionProject(data)) @@ -51,10 +44,7 @@ class ProjectCommissionerTest { @Test void 지원자_모집_기간이_오늘일_수_있다() { - final Long clientId = 1L; - final ProjectData data = new ProjectData( - "프로젝트 제목", - 1_000_000L, + final ProjectData data = projectData( Duration.of( LocalDate.of(2024, 7, 1), LocalDate.of(2024, 7, 31) @@ -62,13 +52,7 @@ class ProjectCommissionerTest { Duration.of( LocalDate.of(2024, 8, 1), LocalDate.of(2024, 8, 31) - ), - "프로젝트 설명", - MeetingType.IN_PERSON, - ProjectCategory.BACKEND, - clientId, - attachmentFiles(), - hashtags() + ) ); final Project project = projectCommissioner.commissionProject(data); @@ -78,10 +62,7 @@ class ProjectCommissionerTest { @Test void 지원자_모집_기간은_예상_작업_기간보다_빠를_수_없다() { - final Long clientId = 1L; - final ProjectData data = new ProjectData( - "프로젝트 제목", - 1_000_000L, + final ProjectData data = projectData( Duration.of( LocalDate.of(2024, 7, 1), LocalDate.of(2024, 7, 31) @@ -89,16 +70,26 @@ class ProjectCommissionerTest { Duration.of( LocalDate.of(2024, 6, 1), LocalDate.of(2024, 6, 30) - ), - "프로젝트 설명", + ) + ); + + assertThatThrownBy(() -> projectCommissioner.commissionProject(data)) + .isInstanceOf(ApplicationDurationNotBeforeEstimatedDurationException.class); + } + + private ProjectData projectData(final Duration applicationDuration, final Duration estimatedDuration) { + return new ProjectData( + TITLE, + PAYMENT, + RECRUITMENT_COUNT, + applicationDuration, + estimatedDuration, + DESCRIPTION, MeetingType.IN_PERSON, ProjectCategory.BACKEND, - clientId, + new ClientId(1L), attachmentFiles(), hashtags() ); - - assertThatThrownBy(() -> projectCommissioner.commissionProject(data)) - .isInstanceOf(ApplicationDurationNotBeforeEstimatedDurationException.class); } } \ No newline at end of file diff --git a/src/test/java/es/princip/getp/fixture/auth/EmailVerificationFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/auth/EmailVerificationFixture.java similarity index 84% rename from src/test/java/es/princip/getp/fixture/auth/EmailVerificationFixture.java rename to get-p-domain/src/testFixtures/java/es/princip/getp/fixture/auth/EmailVerificationFixture.java index 79b6919d..579ffa2c 100644 --- a/src/test/java/es/princip/getp/fixture/auth/EmailVerificationFixture.java +++ b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/auth/EmailVerificationFixture.java @@ -1,6 +1,6 @@ package es.princip.getp.fixture.auth; -import es.princip.getp.application.auth.service.EmailVerification; +import es.princip.getp.domain.auth.EmailVerification; import es.princip.getp.domain.common.model.Email; public class EmailVerificationFixture { diff --git a/src/test/java/es/princip/getp/fixture/client/AddressFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/client/AddressFixture.java similarity index 100% rename from src/test/java/es/princip/getp/fixture/client/AddressFixture.java rename to get-p-domain/src/testFixtures/java/es/princip/getp/fixture/client/AddressFixture.java diff --git a/src/test/java/es/princip/getp/fixture/client/BankAccountFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/client/BankAccountFixture.java similarity index 87% rename from src/test/java/es/princip/getp/fixture/client/BankAccountFixture.java rename to get-p-domain/src/testFixtures/java/es/princip/getp/fixture/client/BankAccountFixture.java index 1931d6ff..ed8dc427 100644 --- a/src/test/java/es/princip/getp/fixture/client/BankAccountFixture.java +++ b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/client/BankAccountFixture.java @@ -1,6 +1,6 @@ package es.princip.getp.fixture.client; -import es.princip.getp.domain.client.model.BankAccount; +import es.princip.getp.domain.common.model.BankAccount; public class BankAccountFixture { diff --git a/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/client/ClientFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/client/ClientFixture.java new file mode 100644 index 00000000..99a0be1c --- /dev/null +++ b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/client/ClientFixture.java @@ -0,0 +1,18 @@ +package es.princip.getp.fixture.client; + +import es.princip.getp.domain.client.model.Client; +import es.princip.getp.domain.member.model.MemberId; + +import static es.princip.getp.fixture.client.AddressFixture.address; +import static es.princip.getp.fixture.common.EmailFixture.email; + +public class ClientFixture { + + public static Client client(final MemberId memberId) { + return Client.builder() + .memberId(memberId) + .email(email()) + .address(address()) + .build(); + } +} diff --git a/src/test/java/es/princip/getp/fixture/common/EmailFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/common/EmailFixture.java similarity index 100% rename from src/test/java/es/princip/getp/fixture/common/EmailFixture.java rename to get-p-domain/src/testFixtures/java/es/princip/getp/fixture/common/EmailFixture.java diff --git a/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/common/HashtagFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/common/HashtagFixture.java new file mode 100644 index 00000000..bd66116d --- /dev/null +++ b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/common/HashtagFixture.java @@ -0,0 +1,15 @@ +package es.princip.getp.fixture.common; + +import es.princip.getp.domain.common.model.Hashtag; + +import java.util.List; + +public class HashtagFixture { + + public static List hashtags() { + return List.of( + Hashtag.from("#해시태그1"), + Hashtag.from("#해시태그2") + ); + } +} diff --git a/src/test/java/es/princip/getp/fixture/common/TechStackFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/common/TechStackFixture.java similarity index 61% rename from src/test/java/es/princip/getp/fixture/common/TechStackFixture.java rename to get-p-domain/src/testFixtures/java/es/princip/getp/fixture/common/TechStackFixture.java index 3a5547df..472dd13b 100644 --- a/src/test/java/es/princip/getp/fixture/common/TechStackFixture.java +++ b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/common/TechStackFixture.java @@ -13,12 +13,4 @@ public static List techStacks() { TechStack.from("JPA") ); } - - public static List techStacksRequest() { - return List.of("Java", "Spring", "JPA"); - } - - public static List techStacksResponse() { - return List.of("Java", "Spring", "JPA"); - } } diff --git a/src/test/java/es/princip/getp/fixture/like/PeopleLikeFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/like/PeopleLikeFixture.java similarity index 51% rename from src/test/java/es/princip/getp/fixture/like/PeopleLikeFixture.java rename to get-p-domain/src/testFixtures/java/es/princip/getp/fixture/like/PeopleLikeFixture.java index bb582af2..4825c238 100644 --- a/src/test/java/es/princip/getp/fixture/like/PeopleLikeFixture.java +++ b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/like/PeopleLikeFixture.java @@ -1,11 +1,13 @@ package es.princip.getp.fixture.like; import es.princip.getp.domain.like.people.model.PeopleLike; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.model.PeopleId; public class PeopleLikeFixture { - public static PeopleLike peopleLike(Long clientId, Long peopleId) { + public static PeopleLike peopleLike(MemberId memberId, PeopleId peopleId) { return PeopleLike.builder() - .clientId(clientId) + .memberId(memberId) .peopleId(peopleId) .build(); } diff --git a/src/test/java/es/princip/getp/fixture/like/ProjectLikeFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/like/ProjectLikeFixture.java similarity index 50% rename from src/test/java/es/princip/getp/fixture/like/ProjectLikeFixture.java rename to get-p-domain/src/testFixtures/java/es/princip/getp/fixture/like/ProjectLikeFixture.java index 28e9c8ad..a873efcf 100644 --- a/src/test/java/es/princip/getp/fixture/like/ProjectLikeFixture.java +++ b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/like/ProjectLikeFixture.java @@ -1,11 +1,13 @@ package es.princip.getp.fixture.like; import es.princip.getp.domain.like.project.model.ProjectLike; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.project.commission.model.ProjectId; public class ProjectLikeFixture { - public static ProjectLike projectLike(Long peopleId, Long projectId) { + public static ProjectLike projectLike(MemberId memberId, ProjectId projectId) { return ProjectLike.builder() - .peopleId(peopleId) + .memberId(memberId) .projectId(projectId) .build(); } diff --git a/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/member/MemberFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/member/MemberFixture.java new file mode 100644 index 00000000..620d82e1 --- /dev/null +++ b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/member/MemberFixture.java @@ -0,0 +1,13 @@ +package es.princip.getp.fixture.member; + +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberType; + +import static es.princip.getp.fixture.common.EmailFixture.email; + +public class MemberFixture { + + public static Member member(final MemberType memberType) { + return Member.of(email(), PasswordFixture.password(), memberType); + } +} diff --git a/src/test/java/es/princip/getp/fixture/member/NicknameFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/member/NicknameFixture.java similarity index 100% rename from src/test/java/es/princip/getp/fixture/member/NicknameFixture.java rename to get-p-domain/src/testFixtures/java/es/princip/getp/fixture/member/NicknameFixture.java diff --git a/src/test/java/es/princip/getp/fixture/member/PasswordFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/member/PasswordFixture.java similarity index 100% rename from src/test/java/es/princip/getp/fixture/member/PasswordFixture.java rename to get-p-domain/src/testFixtures/java/es/princip/getp/fixture/member/PasswordFixture.java diff --git a/src/test/java/es/princip/getp/fixture/member/PhoneNumberFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/member/PhoneNumberFixture.java similarity index 100% rename from src/test/java/es/princip/getp/fixture/member/PhoneNumberFixture.java rename to get-p-domain/src/testFixtures/java/es/princip/getp/fixture/member/PhoneNumberFixture.java diff --git a/src/test/java/es/princip/getp/fixture/member/ProfileImageFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/member/ProfileImageFixture.java similarity index 73% rename from src/test/java/es/princip/getp/fixture/member/ProfileImageFixture.java rename to get-p-domain/src/testFixtures/java/es/princip/getp/fixture/member/ProfileImageFixture.java index 636b3b4e..6b1c7f11 100644 --- a/src/test/java/es/princip/getp/fixture/member/ProfileImageFixture.java +++ b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/member/ProfileImageFixture.java @@ -1,5 +1,6 @@ package es.princip.getp.fixture.member; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.member.model.ProfileImage; import java.net.URI; @@ -10,8 +11,8 @@ public class ProfileImageFixture { private static final String FILE_NAME = "image.jpg"; - public static ProfileImage profileImage(final Long memberId) { - final String profileImageUri = String.format("/images/%d/profile/%s", memberId, FILE_NAME); + public static ProfileImage profileImage(final MemberId memberId) { + final String profileImageUri = String.format("/images/%d/profile/%s", memberId.getValue(), FILE_NAME); final URI uri = URI.create(BASE_URI).resolve(profileImageUri); return ProfileImage.from(uri.toString()); } diff --git a/src/test/java/es/princip/getp/fixture/people/ActivityAreaFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/people/ActivityAreaFixture.java similarity index 100% rename from src/test/java/es/princip/getp/fixture/people/ActivityAreaFixture.java rename to get-p-domain/src/testFixtures/java/es/princip/getp/fixture/people/ActivityAreaFixture.java diff --git a/src/test/java/es/princip/getp/fixture/people/EducationFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/people/EducationFixture.java similarity index 100% rename from src/test/java/es/princip/getp/fixture/people/EducationFixture.java rename to get-p-domain/src/testFixtures/java/es/princip/getp/fixture/people/EducationFixture.java diff --git a/src/test/java/es/princip/getp/fixture/people/IntroductionFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/people/IntroductionFixture.java similarity index 100% rename from src/test/java/es/princip/getp/fixture/people/IntroductionFixture.java rename to get-p-domain/src/testFixtures/java/es/princip/getp/fixture/people/IntroductionFixture.java diff --git a/src/test/java/es/princip/getp/fixture/people/PeopleFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/people/PeopleFixture.java similarity index 58% rename from src/test/java/es/princip/getp/fixture/people/PeopleFixture.java rename to get-p-domain/src/testFixtures/java/es/princip/getp/fixture/people/PeopleFixture.java index 33b57a77..abd8d0b7 100644 --- a/src/test/java/es/princip/getp/fixture/people/PeopleFixture.java +++ b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/people/PeopleFixture.java @@ -1,12 +1,9 @@ package es.princip.getp.fixture.people; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.people.model.People; import es.princip.getp.domain.people.model.PeopleInfo; import es.princip.getp.domain.people.model.PeopleProfileData; -import es.princip.getp.domain.people.model.PeopleType; - -import java.util.List; -import java.util.stream.LongStream; import static es.princip.getp.fixture.common.EmailFixture.email; import static es.princip.getp.fixture.common.HashtagFixture.hashtags; @@ -18,12 +15,12 @@ public class PeopleFixture { - private static PeopleInfo peopleInfo(final PeopleType peopleType) { - return new PeopleInfo(email(), peopleType); + private static PeopleInfo peopleInfo() { + return new PeopleInfo(email()); } - public static People people(Long memberId, PeopleType peopleType) { - final People people = People.of(memberId, peopleInfo(peopleType)); + public static People people(final MemberId memberId) { + final People people = People.of(memberId, peopleInfo()); final PeopleProfileData data = new PeopleProfileData( introduction(), activityArea(), @@ -36,13 +33,7 @@ public static People people(Long memberId, PeopleType peopleType) { return people; } - public static People peopleWithoutProfile(Long memberId, PeopleType peopleType) { - return People.of(memberId, peopleInfo(peopleType)); - } - - public static List peopleList(int size, Long memberIdBias, PeopleType peopleType) { - return LongStream.range(0, size) - .mapToObj(i -> people(memberIdBias + i, peopleType)) - .toList(); + public static People peopleWithoutProfile(final MemberId memberId) { + return People.of(memberId, peopleInfo()); } } diff --git a/src/test/java/es/princip/getp/fixture/people/PeopleProfileFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/people/PeopleProfileFixture.java similarity index 100% rename from src/test/java/es/princip/getp/fixture/people/PeopleProfileFixture.java rename to get-p-domain/src/testFixtures/java/es/princip/getp/fixture/people/PeopleProfileFixture.java diff --git a/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/people/PortfolioFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/people/PortfolioFixture.java new file mode 100644 index 00000000..375ffe48 --- /dev/null +++ b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/people/PortfolioFixture.java @@ -0,0 +1,15 @@ +package es.princip.getp.fixture.people; + +import es.princip.getp.domain.people.model.Portfolio; + +import java.util.List; + +public class PortfolioFixture { + + public static List portfolios() { + return List.of( + Portfolio.of("포트폴리오1 내용", "https://github.com/scv1702/1"), + Portfolio.of("포트폴리오2 내용", "https://github.com/scv1702/2") + ); + } +} diff --git a/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/project/ApplicantsCountFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/project/ApplicantsCountFixture.java new file mode 100644 index 00000000..3246e703 --- /dev/null +++ b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/project/ApplicantsCountFixture.java @@ -0,0 +1,5 @@ +package es.princip.getp.fixture.project; + +public class ApplicantsCountFixture { + public static final Long APPLICANTS_COUNT = 5L; +} diff --git a/src/test/java/es/princip/getp/fixture/project/AttachmentFileFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/project/AttachmentFileFixture.java similarity index 100% rename from src/test/java/es/princip/getp/fixture/project/AttachmentFileFixture.java rename to get-p-domain/src/testFixtures/java/es/princip/getp/fixture/project/AttachmentFileFixture.java diff --git a/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/project/ProjectApplicationFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/project/ProjectApplicationFixture.java new file mode 100644 index 00000000..93d2a779 --- /dev/null +++ b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/project/ProjectApplicationFixture.java @@ -0,0 +1,113 @@ +package es.princip.getp.fixture.project; + +import es.princip.getp.domain.common.model.Duration; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.apply.model.*; +import es.princip.getp.domain.project.commission.model.ProjectId; + +import java.time.LocalDate; +import java.util.Set; + +import static es.princip.getp.domain.project.apply.model.ProjectApplicationStatus.ACCEPTED; +import static es.princip.getp.fixture.project.AttachmentFileFixture.attachmentFiles; + +public class ProjectApplicationFixture { + + public static final String DESCRIPTION = "지원 내용"; + + public static Duration expectedDuration() { + return Duration.of( + LocalDate.of(2024, 7, 1), + LocalDate.of(2024, 7, 31) + ); + } + + private static IndividualProjectApplication.IndividualProjectApplicationBuilder individualBuilder() { + return IndividualProjectApplication.builder() + .expectedDuration(expectedDuration()) + .status(ACCEPTED) + .description(DESCRIPTION) + .attachmentFiles(attachmentFiles()); + } + + private static TeamProjectApplication.TeamProjectApplicationBuilder teamBuilder() { + return TeamProjectApplication.builder() + .expectedDuration(expectedDuration()) + .description(DESCRIPTION) + .attachmentFiles(attachmentFiles()); + } + + public static ProjectApplication individualProjectApplication( + final PeopleId peopleId, + final ProjectId projectId + ) { + return individualBuilder() + .applicantId(peopleId) + .projectId(projectId) + .build(); + } + + public static ProjectApplication individualProjectApplication( + final ProjectApplicationId applicationId, + final PeopleId peopleId, + final ProjectId projectId + ) { + return individualBuilder() + .id(applicationId) + .applicantId(peopleId) + .projectId(projectId) + .build(); + } + + public static TeamProjectApplication teamProjectApplication( + final PeopleId peopleId, + final ProjectId projectId, + final ProjectApplicationStatus status, + final Set teammates + ) { + return teamBuilder() + .applicantId(peopleId) + .projectId(projectId) + .status(status) + .teammates(teammates) + .build(); + } + + public static TeamProjectApplication teamProjectApplication( + final ProjectApplicationId applicationId, + final PeopleId peopleId, + final ProjectId projectId, + final ProjectApplicationStatus status, + final Set teammates + ) { + return teamBuilder() + .id(applicationId) + .applicantId(peopleId) + .projectId(projectId) + .status(status) + .teammates(teammates) + .build(); + } + + public static Teammate teammate( + final PeopleId peopleId, + final TeammateStatus status + ) { + return Teammate.builder() + .peopleId(peopleId) + .status(status) + .build(); + } + + public static Teammate teammate( + final TeammateId teammateId, + final PeopleId peopleId, + final TeammateStatus status + ) { + return Teammate.builder() + .id(teammateId) + .peopleId(peopleId) + .status(status) + .build(); + } +} diff --git a/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/project/ProjectFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/project/ProjectFixture.java new file mode 100644 index 00000000..257b81f8 --- /dev/null +++ b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/project/ProjectFixture.java @@ -0,0 +1,77 @@ +package es.princip.getp.fixture.project; + +import es.princip.getp.domain.client.model.ClientId; +import es.princip.getp.domain.common.model.Duration; +import es.princip.getp.domain.project.commission.model.*; + +import java.time.LocalDate; + +import static es.princip.getp.fixture.common.HashtagFixture.hashtags; +import static es.princip.getp.fixture.project.AttachmentFileFixture.attachmentFiles; + +public class ProjectFixture { + + public static final Long PAYMENT = 1_000_000L; + public static final Long RECRUITMENT_COUNT = 1L; + public static final String TITLE = "프로젝트 제목"; + public static final String DESCRIPTION = "프로젝트 설명"; + public static final LocalDate APPLICATION_START_DATE = LocalDate.of(2024, 7, 1); + public static final LocalDate APPLICATION_END_DATE = LocalDate.of(2024, 7, 31); + public static final LocalDate ESTIMATED_START_DATE = LocalDate.of(2024, 8, 1); + public static final LocalDate ESTIMATED_END_DATE = LocalDate.of(2024, 8, 31); + + public static Project.ProjectBuilder builder() { + return Project.builder() + .category(ProjectCategory.BACKEND) + .attachmentFiles(attachmentFiles()) + .payment(PAYMENT) + .recruitmentCount(RECRUITMENT_COUNT) + .title(TITLE) + .description(DESCRIPTION) + .meetingType(MeetingType.IN_PERSON) + .estimatedDuration(Duration.of( + ESTIMATED_START_DATE, + ESTIMATED_END_DATE + )) + .hashtags(hashtags()); + } + + public static Project project( + final ProjectId projectId, + final ClientId clientId, + final ProjectStatus status + ) { + return builder() + .id(projectId) + .clientId(clientId) + .status(status) + .applicationDuration(Duration.of( + APPLICATION_START_DATE, + APPLICATION_END_DATE + )) + .build(); + } + + public static Project project(final ClientId clientId, final ProjectStatus status) { + return builder() + .clientId(clientId) + .status(status) + .applicationDuration(Duration.of( + APPLICATION_START_DATE, + APPLICATION_END_DATE + )) + .build(); + } + + public static Project project( + final ClientId clientId, + final ProjectStatus status, + final Duration applicationDuration + ) { + return builder() + .clientId(clientId) + .status(status) + .applicationDuration(applicationDuration) + .build(); + } +} diff --git a/src/test/java/es/princip/getp/fixture/project/ProjectMeetingFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/project/ProjectMeetingFixture.java similarity index 80% rename from src/test/java/es/princip/getp/fixture/project/ProjectMeetingFixture.java rename to get-p-domain/src/testFixtures/java/es/princip/getp/fixture/project/ProjectMeetingFixture.java index b307b993..7d73de13 100644 --- a/src/test/java/es/princip/getp/fixture/project/ProjectMeetingFixture.java +++ b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/project/ProjectMeetingFixture.java @@ -1,6 +1,8 @@ package es.princip.getp.fixture.project; import es.princip.getp.domain.common.model.MeetingSchedule; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.commission.model.ProjectId; import es.princip.getp.domain.project.meeting.model.ProjectMeeting; import java.time.LocalDate; @@ -18,7 +20,7 @@ public class ProjectMeetingFixture { ); public final static String DESCRIPTION = "미팅 요구사항"; - public static ProjectMeeting projectMeeting(final Long projectId, final Long applicantId) { + public static ProjectMeeting projectMeeting(final ProjectId projectId, final PeopleId applicantId) { return ProjectMeeting.builder() .projectId(projectId) .applicantId(applicantId) diff --git a/src/test/java/es/princip/getp/fixture/storage/StorageFixture.java b/get-p-domain/src/testFixtures/java/es/princip/getp/fixture/storage/StorageFixture.java similarity index 100% rename from src/test/java/es/princip/getp/fixture/storage/StorageFixture.java rename to get-p-domain/src/testFixtures/java/es/princip/getp/fixture/storage/StorageFixture.java diff --git a/get-p-infrastructure/build.gradle b/get-p-infrastructure/build.gradle new file mode 100644 index 00000000..dd50e793 --- /dev/null +++ b/get-p-infrastructure/build.gradle @@ -0,0 +1,17 @@ +dependencies { + // 모듈 의존성 + implementation(project(':get-p-application')) + implementation(project(':get-p-domain')) + testImplementation(testFixtures(project(':get-p-domain'))) + + // Spring Web + implementation 'org.springframework.boot:spring-boot-starter-web:3.3.5' +} + +bootJar { + enabled = false +} + +jar { + enabled = true +} diff --git a/src/main/java/es/princip/getp/infrastructure/adapter/storage/FileLocalStorageAdapter.java b/get-p-infrastructure/src/main/java/es/princip/getp/infrastructure/adapter/storage/FileLocalStorageAdapter.java similarity index 92% rename from src/main/java/es/princip/getp/infrastructure/adapter/storage/FileLocalStorageAdapter.java rename to get-p-infrastructure/src/main/java/es/princip/getp/infrastructure/adapter/storage/FileLocalStorageAdapter.java index c66c6271..51875d9e 100644 --- a/src/main/java/es/princip/getp/infrastructure/adapter/storage/FileLocalStorageAdapter.java +++ b/get-p-infrastructure/src/main/java/es/princip/getp/infrastructure/adapter/storage/FileLocalStorageAdapter.java @@ -2,8 +2,8 @@ import es.princip.getp.application.storage.port.out.DeleteFilePort; import es.princip.getp.application.storage.port.out.StoreFilePort; -import es.princip.getp.infrastructure.adapter.storage.exception.FailedFileDeleteException; -import es.princip.getp.infrastructure.adapter.storage.exception.FailedFileSaveException; +import es.princip.getp.application.storage.exception.FailedFileDeleteException; +import es.princip.getp.application.storage.exception.FailedFileSaveException; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; diff --git a/get-p-infrastructure/src/main/resources/infra-config.yml b/get-p-infrastructure/src/main/resources/infra-config.yml new file mode 100644 index 00000000..e2186413 --- /dev/null +++ b/get-p-infrastructure/src/main/resources/infra-config.yml @@ -0,0 +1,5 @@ +spring: + storage: + local: + path: ${STORAGE_PATH} + base-uri: ${STORAGE_BASE_URI} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/infrastructure/adapter/storage/FileLocalStorageAdapterTest.java b/get-p-infrastructure/src/test/java/es/princip/getp/infrastructure/adapter/storage/FileLocalStorageAdapterTest.java similarity index 93% rename from src/test/java/es/princip/getp/infrastructure/adapter/storage/FileLocalStorageAdapterTest.java rename to get-p-infrastructure/src/test/java/es/princip/getp/infrastructure/adapter/storage/FileLocalStorageAdapterTest.java index dce092f0..3dd81575 100644 --- a/src/test/java/es/princip/getp/infrastructure/adapter/storage/FileLocalStorageAdapterTest.java +++ b/get-p-infrastructure/src/test/java/es/princip/getp/infrastructure/adapter/storage/FileLocalStorageAdapterTest.java @@ -9,9 +9,9 @@ import java.net.URI; import java.nio.file.Path; -import static es.princip.getp.fixture.storage.MultipartFileFixture.fileMultiPartFile; import static es.princip.getp.fixture.storage.StorageFixture.BASE_URI; import static es.princip.getp.fixture.storage.StorageFixture.STORAGE_PATH; +import static es.princip.getp.infrastructure.adapter.storage.fixure.MultipartFileFixture.fileMultiPartFile; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatPath; diff --git a/get-p-infrastructure/src/test/java/es/princip/getp/infrastructure/adapter/storage/fixure/MultipartFileFixture.java b/get-p-infrastructure/src/test/java/es/princip/getp/infrastructure/adapter/storage/fixure/MultipartFileFixture.java new file mode 100644 index 00000000..6d7c78ff --- /dev/null +++ b/get-p-infrastructure/src/test/java/es/princip/getp/infrastructure/adapter/storage/fixure/MultipartFileFixture.java @@ -0,0 +1,13 @@ +package es.princip.getp.infrastructure.adapter.storage.fixure; + +import org.springframework.mock.web.MockMultipartFile; + +public class MultipartFileFixture { + + public static MockMultipartFile fileMultiPartFile() { + return new MockMultipartFile( + "file", + "dummy".getBytes() + ); + } +} diff --git a/get-p-infrastructure/src/test/resources/application.yml b/get-p-infrastructure/src/test/resources/application.yml new file mode 100644 index 00000000..e2186413 --- /dev/null +++ b/get-p-infrastructure/src/test/resources/application.yml @@ -0,0 +1,5 @@ +spring: + storage: + local: + path: ${STORAGE_PATH} + base-uri: ${STORAGE_BASE_URI} \ No newline at end of file diff --git a/get-p-persistence/build.gradle b/get-p-persistence/build.gradle new file mode 100644 index 00000000..447f2b14 --- /dev/null +++ b/get-p-persistence/build.gradle @@ -0,0 +1,44 @@ +dependencies { + // 모듈 의존성 + implementation(project(':get-p-domain')) + implementation(project(':get-p-application')) + testImplementation(testFixtures(project(':get-p-domain'))) + + // Spring Validation + implementation 'org.springframework.boot:spring-boot-starter-validation:3.3.5' + + // Spring Data + implementation 'org.springframework.boot:spring-boot-starter-data-jpa:3.3.5' + implementation 'org.springframework.boot:spring-boot-starter-data-redis:3.3.5' + + // Flyway + implementation 'org.flywaydb:flyway-core:9.16.3' + implementation 'org.flywaydb:flyway-mysql:9.16.3' + + // JDBC MySQL 드라이버 + runtimeOnly 'com.mysql:mysql-connector-j:9.0.0' + + // QueryDSL + implementation "com.querydsl:querydsl-jpa:5.0.0:jakarta" + annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api:3.0.0" + annotationProcessor "jakarta.persistence:jakarta.persistence-api:3.0.0" + + // Testcontainers + testImplementation 'org.springframework.boot:spring-boot-testcontainers:3.3.5' + testImplementation 'org.testcontainers:testcontainers:1.19.3' + testImplementation 'org.testcontainers:junit-jupiter:1.19.3' + testImplementation 'org.testcontainers:mysql:1.20.0' +} + +ext { + snippetsDir = file('build/generated-snippets') +} + +bootJar { + enabled = false +} + +jar { + enabled = true +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/persistence/adapter/BaseTimeJpaEntity.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/BaseTimeJpaEntity.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/BaseTimeJpaEntity.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/BaseTimeJpaEntity.java diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/auth/EmailVerificationKeyValueRepository.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/auth/EmailVerificationKeyValueRepository.java new file mode 100644 index 00000000..7c5a8672 --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/auth/EmailVerificationKeyValueRepository.java @@ -0,0 +1,7 @@ +package es.princip.getp.persistence.adapter.auth; + +import org.springframework.data.keyvalue.repository.KeyValueRepository; + +public interface EmailVerificationKeyValueRepository extends KeyValueRepository { + +} \ No newline at end of file diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/auth/EmailVerificationRedisEntity.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/auth/EmailVerificationRedisEntity.java new file mode 100644 index 00000000..cf99714c --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/auth/EmailVerificationRedisEntity.java @@ -0,0 +1,39 @@ +package es.princip.getp.persistence.adapter.auth; + +import es.princip.getp.domain.auth.EmailVerification; +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; +import org.springframework.data.redis.core.TimeToLive; + +import java.time.LocalDateTime; +import java.util.concurrent.TimeUnit; + +@Getter +@RedisHash(value = "email_verification") +@EqualsAndHashCode(exclude = "expiration") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class EmailVerificationRedisEntity { + + @Id + private String email; + + private String verificationCode; + + private LocalDateTime createdAt; + + @TimeToLive(unit = TimeUnit.MILLISECONDS) + private Long expiration; + + public static EmailVerificationRedisEntity from(final EmailVerification verification) { + final EmailVerificationRedisEntity entity = new EmailVerificationRedisEntity(); + entity.email = verification.getEmail(); + entity.verificationCode = verification.getVerificationCode(); + entity.createdAt = verification.getCreatedAt(); + entity.expiration = verification.getExpiration(); + return entity; + } +} diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/auth/EmailVerificationRedisRepository.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/auth/EmailVerificationRedisRepository.java new file mode 100644 index 00000000..5385a2f7 --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/auth/EmailVerificationRedisRepository.java @@ -0,0 +1,37 @@ +package es.princip.getp.persistence.adapter.auth; + +import es.princip.getp.application.auth.exception.NotFoundVerificationException; +import es.princip.getp.application.auth.service.EmailVerificationRepository; +import es.princip.getp.domain.auth.EmailVerification; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +class EmailVerificationRedisRepository implements EmailVerificationRepository { + + private final EmailVerificationKeyValueRepository repository; + + @Override + public void deleteById(final String email) { + repository.deleteById(email); + } + + @Override + public void save(final EmailVerification verification) { + final EmailVerificationRedisEntity entity = EmailVerificationRedisEntity.from(verification); + repository.save(entity); + } + + @Override + public EmailVerification findById(String email) { + return repository.findById(email) + .map(entity -> new EmailVerification( + entity.getEmail(), + entity.getVerificationCode(), + entity.getExpiration(), + entity.getCreatedAt() + )) + .orElseThrow(NotFoundVerificationException::new); + } +} \ No newline at end of file diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/auth/RefreshTokenKeyValueRepository.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/auth/RefreshTokenKeyValueRepository.java new file mode 100644 index 00000000..a3ec4498 --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/auth/RefreshTokenKeyValueRepository.java @@ -0,0 +1,8 @@ +package es.princip.getp.persistence.adapter.auth; + +import org.springframework.data.keyvalue.repository.KeyValueRepository; + +public interface RefreshTokenKeyValueRepository extends KeyValueRepository { + + boolean existsByRefreshToken(String refreshToken); +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/application/auth/service/RefreshToken.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/auth/RefreshTokenRedisEntity.java similarity index 51% rename from src/main/java/es/princip/getp/application/auth/service/RefreshToken.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/auth/RefreshTokenRedisEntity.java index 6d715895..455ac389 100644 --- a/src/main/java/es/princip/getp/application/auth/service/RefreshToken.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/auth/RefreshTokenRedisEntity.java @@ -1,5 +1,6 @@ -package es.princip.getp.application.auth.service; +package es.princip.getp.persistence.adapter.auth; +import es.princip.getp.domain.auth.RefreshToken; import lombok.Getter; import org.springframework.data.annotation.Id; import org.springframework.data.redis.core.RedisHash; @@ -10,7 +11,7 @@ @Getter @RedisHash(value = "token_verification") -public class RefreshToken { +public class RefreshTokenRedisEntity { @Id private Long memberId; @@ -21,9 +22,11 @@ public class RefreshToken { @TimeToLive(unit = TimeUnit.MILLISECONDS) private Long expiration; - public RefreshToken(final Long memberId, final String refreshToken, final Long expiration) { - this.memberId = memberId; - this.refreshToken = refreshToken; - this.expiration = expiration; + public static RefreshTokenRedisEntity from(final RefreshToken token) { + final RefreshTokenRedisEntity entity = new RefreshTokenRedisEntity(); + entity.memberId = token.getMemberId(); + entity.refreshToken = token.getRefreshToken(); + entity.expiration = token.getExpiration(); + return entity; } } diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/auth/RefreshTokenRedisRepository.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/auth/RefreshTokenRedisRepository.java new file mode 100644 index 00000000..bb5e8199 --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/auth/RefreshTokenRedisRepository.java @@ -0,0 +1,24 @@ +package es.princip.getp.persistence.adapter.auth; + +import es.princip.getp.application.auth.service.RefreshTokenRepository; +import es.princip.getp.domain.auth.RefreshToken; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +class RefreshTokenRedisRepository implements RefreshTokenRepository { + + private final RefreshTokenKeyValueRepository repository; + + @Override + public boolean existsByRefreshToken(final String refreshToken) { + return repository.existsByRefreshToken(refreshToken); + } + + @Override + public void save(final RefreshToken refreshToken) { + final RefreshTokenRedisEntity entity = RefreshTokenRedisEntity.from(refreshToken); + repository.save(entity); + } +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/persistence/adapter/client/AddressJpaVO.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/client/AddressJpaVO.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/client/AddressJpaVO.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/client/AddressJpaVO.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/client/BankAccountJpaVO.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/client/BankAccountJpaVO.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/client/BankAccountJpaVO.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/client/BankAccountJpaVO.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/client/ClientJpaEntity.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/client/ClientJpaEntity.java similarity index 92% rename from src/main/java/es/princip/getp/persistence/adapter/client/ClientJpaEntity.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/client/ClientJpaEntity.java index 585c24eb..42610303 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/client/ClientJpaEntity.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/client/ClientJpaEntity.java @@ -18,7 +18,7 @@ class ClientJpaEntity extends BaseTimeJpaEntity { @Id @Column(name = "client_id") @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long clientId; + private Long id; @Column(name = "email") private String email; @@ -34,7 +34,7 @@ class ClientJpaEntity extends BaseTimeJpaEntity { @Builder public ClientJpaEntity( - final Long clientId, + final Long id, final String email, final AddressJpaVO address, final BankAccountJpaVO bankAccount, @@ -44,7 +44,7 @@ public ClientJpaEntity( ) { super(createdAt, updatedAt); - this.clientId = clientId; + this.id = id; this.email = email; this.address = address; this.bankAccount = bankAccount; diff --git a/src/main/java/es/princip/getp/persistence/adapter/client/ClientJpaRepository.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/client/ClientJpaRepository.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/client/ClientJpaRepository.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/client/ClientJpaRepository.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/client/ClientPersistenceAdapter.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/client/ClientPersistenceAdapter.java similarity index 55% rename from src/main/java/es/princip/getp/persistence/adapter/client/ClientPersistenceAdapter.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/client/ClientPersistenceAdapter.java index 605b482b..e6c7cabc 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/client/ClientPersistenceAdapter.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/client/ClientPersistenceAdapter.java @@ -1,7 +1,10 @@ package es.princip.getp.persistence.adapter.client; +import es.princip.getp.application.client.exception.NotFoundClientException; import es.princip.getp.application.client.port.out.*; import es.princip.getp.domain.client.model.Client; +import es.princip.getp.domain.client.model.ClientId; +import es.princip.getp.domain.member.model.MemberId; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; @@ -18,30 +21,33 @@ class ClientPersistenceAdapter implements SaveClientPort, private final ClientJpaRepository clientJpaRepository; @Override - public boolean existsBy(final Long memberId) { - return clientJpaRepository.existsByMemberId(memberId); + public boolean existsBy(final MemberId memberId) { + return clientJpaRepository.existsByMemberId(memberId.getValue()); } @Override public void delete(final Client client) { - clientJpaRepository.deleteById(client.getClientId()); + final Long id = client.getId().getValue(); + clientJpaRepository.deleteById(id); } @Override - public Client loadBy(final Long memberId) { - return mapper.mapToDomain(clientJpaRepository.findByMemberId(memberId) + public Client loadBy(final MemberId memberId) { + return mapper.mapToDomain(clientJpaRepository.findByMemberId(memberId.getValue()) .orElseThrow(NotFoundClientException::new)); } @Override - public Long save(final Client client) { - return clientJpaRepository.save(mapper.mapToJpa(client)) - .getClientId(); + public ClientId save(final Client client) { + final Long id = clientJpaRepository.save(mapper.mapToJpa(client)) + .getId(); + return new ClientId(id); } @Override public void update(final Client client) { - if (!clientJpaRepository.existsById(client.getClientId())) { + final Long id = client.getId().getValue(); + if (!clientJpaRepository.existsById(id)) { throw new NotFoundClientException(); } save(client); diff --git a/src/main/java/es/princip/getp/persistence/adapter/client/ClientPersistenceMapper.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/client/ClientPersistenceMapper.java similarity index 59% rename from src/main/java/es/princip/getp/persistence/adapter/client/ClientPersistenceMapper.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/client/ClientPersistenceMapper.java index 660d9051..f042f72c 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/client/ClientPersistenceMapper.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/client/ClientPersistenceMapper.java @@ -1,21 +1,27 @@ package es.princip.getp.persistence.adapter.client; +import es.princip.getp.application.common.dto.response.AddressResponse; import es.princip.getp.domain.client.model.Address; -import es.princip.getp.domain.client.model.BankAccount; import es.princip.getp.domain.client.model.Client; +import es.princip.getp.domain.common.model.BankAccount; +import org.mapstruct.InheritInverseConfiguration; import org.mapstruct.Mapper; import org.mapstruct.Mapping; @Mapper(componentModel = "spring") interface ClientPersistenceMapper { + @Mapping(source = "id", target = "id.value") + @Mapping(source = "memberId", target = "memberId.value") @Mapping(source = "email", target = "email.value") Client mapToDomain(ClientJpaEntity clientJpaEntity); - @Mapping(target = "email", source = "email.value") + @InheritInverseConfiguration ClientJpaEntity mapToJpa(Client client); Address mapToDomain(AddressJpaVO addressJpaVO); + AddressResponse mapToResponse(AddressJpaVO addressJpaVO); + BankAccount mapToDomain(BankAccountJpaVO bankAccountJpaVO); } diff --git a/src/main/java/es/princip/getp/persistence/adapter/client/ClientQueryDslQuery.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/client/ClientQueryDslQuery.java similarity index 69% rename from src/main/java/es/princip/getp/persistence/adapter/client/ClientQueryDslQuery.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/client/ClientQueryDslQuery.java index 83926942..fc7021c6 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/client/ClientQueryDslQuery.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/client/ClientQueryDslQuery.java @@ -1,9 +1,12 @@ package es.princip.getp.persistence.adapter.client; import com.querydsl.core.Tuple; -import es.princip.getp.api.controller.client.query.dto.ClientResponse; -import es.princip.getp.api.controller.project.query.dto.ProjectClientResponse; +import es.princip.getp.application.client.dto.response.ClientResponse; +import es.princip.getp.application.client.exception.NotFoundClientException; +import es.princip.getp.application.project.commission.dto.response.ProjectClientResponse; import es.princip.getp.application.client.port.out.ClientQuery; +import es.princip.getp.domain.client.model.ClientId; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.persistence.adapter.member.QMemberJpaEntity; import es.princip.getp.persistence.support.QueryDslSupport; import lombok.RequiredArgsConstructor; @@ -26,76 +29,73 @@ private void validateTuple(final Tuple result) { private ClientResponse mapToClientResponse(final Tuple result) { return new ClientResponse( - result.get(client.clientId), + result.get(client.id), result.get(member.nickname), result.get(member.phoneNumber), result.get(client.email), result.get(member.profileImage), mapper.mapToDomain(result.get(client.address)), - mapper.mapToDomain(result.get(client.bankAccount)), result.get(client.createdAt), result.get(client.updatedAt) ); } @Override - public ClientResponse findClientById(final Long clientId) { + public ClientResponse findClientBy(final ClientId clientId) { final Tuple result = queryFactory.select( - client.clientId, + client.id, member.nickname, member.phoneNumber, client.email, member.profileImage, client.address, - client.bankAccount, client.createdAt, client.updatedAt ) .from(client) - .join(member).on(client.memberId.eq(member.memberId)) - .where(client.clientId.eq(clientId)) + .join(member).on(client.memberId.eq(member.id)) + .where(client.id.eq(clientId.getValue())) .fetchOne(); validateTuple(result); return mapToClientResponse(result); } @Override - public ClientResponse findClientByMemberId(final Long memberId) { + public ClientResponse findClientBy(final MemberId memberId) { final Tuple result = queryFactory.select( - client.clientId, + client.id, member.nickname, member.phoneNumber, client.email, member.profileImage, client.address, - client.bankAccount, client.createdAt, client.updatedAt ) .from(client) - .join(member).on(client.memberId.eq(member.memberId)) - .where(client.memberId.eq(memberId)) + .join(member).on(client.memberId.eq(member.id)) + .where(client.memberId.eq(memberId.getValue())) .fetchOne(); validateTuple(result); return mapToClientResponse(result); } @Override - public ProjectClientResponse findProjectClientById(final Long clientId) { + public ProjectClientResponse findProjectClientBy(final ClientId clientId) { final Tuple result = queryFactory.select( - client.clientId, + client.id, member.nickname, client.address ) .from(client) - .join(member).on(client.memberId.eq(member.memberId)) - .where(client.clientId.eq(clientId)) + .join(member).on(client.memberId.eq(member.id)) + .where(client.id.eq(clientId.getValue())) .fetchOne(); validateTuple(result); return new ProjectClientResponse( - result.get(client.clientId), + result.get(client.id), result.get(member.nickname), - mapper.mapToDomain(result.get(client.address)) + mapper.mapToResponse(result.get(client.address)) ); } } diff --git a/src/main/java/es/princip/getp/persistence/adapter/common/DurationJpaVO.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/common/DurationJpaVO.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/common/DurationJpaVO.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/common/DurationJpaVO.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/common/mapper/AttachmentFilePersistenceMapper.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/common/mapper/AttachmentFilePersistenceMapper.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/common/mapper/AttachmentFilePersistenceMapper.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/common/mapper/AttachmentFilePersistenceMapper.java diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/common/mapper/DurationPersistenceMapper.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/common/mapper/DurationPersistenceMapper.java new file mode 100644 index 00000000..2af39082 --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/common/mapper/DurationPersistenceMapper.java @@ -0,0 +1,12 @@ +package es.princip.getp.persistence.adapter.common.mapper; + +import org.mapstruct.Mapper; + +import es.princip.getp.domain.common.model.Duration; +import es.princip.getp.persistence.adapter.common.DurationJpaVO; + +@Mapper(componentModel = "spring") +public interface DurationPersistenceMapper { + + Duration mapToDomain(DurationJpaVO durationJpaVO); +} diff --git a/src/main/java/es/princip/getp/persistence/adapter/common/mapper/HashtagPersistenceMapper.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/common/mapper/HashtagPersistenceMapper.java similarity index 62% rename from src/main/java/es/princip/getp/persistence/adapter/common/mapper/HashtagPersistenceMapper.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/common/mapper/HashtagPersistenceMapper.java index 92531825..2905e305 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/common/mapper/HashtagPersistenceMapper.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/common/mapper/HashtagPersistenceMapper.java @@ -1,11 +1,8 @@ package es.princip.getp.persistence.adapter.common.mapper; -import es.princip.getp.api.controller.common.dto.HashtagsResponse; import es.princip.getp.domain.common.model.Hashtag; import org.mapstruct.Mapper; -import java.util.List; - @Mapper(componentModel = "spring") public interface HashtagPersistenceMapper { @@ -17,8 +14,4 @@ default String mapToString(Hashtag hashtag) { } return hashtag.getValue(); } - - default HashtagsResponse mapToResponse(List hashtags) { - return HashtagsResponse.from(hashtags.stream().map(this::mapToHashtag).toList()); - } } diff --git a/src/main/java/es/princip/getp/persistence/adapter/common/mapper/PhoneNumberPersistenceMapper.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/common/mapper/PhoneNumberPersistenceMapper.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/common/mapper/PhoneNumberPersistenceMapper.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/common/mapper/PhoneNumberPersistenceMapper.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/common/mapper/TechStackPersistenceMapper.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/common/mapper/TechStackPersistenceMapper.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/common/mapper/TechStackPersistenceMapper.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/common/mapper/TechStackPersistenceMapper.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/common/mapper/URLPersistenceMapper.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/common/mapper/URLPersistenceMapper.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/common/mapper/URLPersistenceMapper.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/common/mapper/URLPersistenceMapper.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/like/people/CountPeopleLikeAdapter.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/people/CountPeopleLikeAdapter.java similarity index 63% rename from src/main/java/es/princip/getp/persistence/adapter/like/people/CountPeopleLikeAdapter.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/people/CountPeopleLikeAdapter.java index c55fa09e..59f92869 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/like/people/CountPeopleLikeAdapter.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/people/CountPeopleLikeAdapter.java @@ -1,6 +1,7 @@ package es.princip.getp.persistence.adapter.like.people; import es.princip.getp.application.like.people.port.out.CountPeopleLikePort; +import es.princip.getp.domain.people.model.PeopleId; import es.princip.getp.persistence.support.QueryDslSupport; import org.springframework.stereotype.Repository; @@ -16,26 +17,30 @@ public class CountPeopleLikeAdapter extends QueryDslSupport implements CountPeop private static final QPeopleLikeJpaEntity peopleLike = QPeopleLikeJpaEntity.peopleLikeJpaEntity; @Override - public Long countBy(Long peopleId) { + public Long countBy(PeopleId peopleId) { return queryFactory.select(peopleLike.count()) .from(peopleLike) - .where(peopleLike.peopleId.eq(peopleId)) + .where(peopleLike.peopleId.eq(peopleId.getValue())) .fetchOne(); } @Override - public Map countBy(Long... peopleIds) { - final Map counts = Arrays.stream(peopleIds) + public Map countBy(PeopleId... peopleIds) { + final Map counts = Arrays.stream(peopleIds) .collect(toMap(id -> id, id -> 0L)); - final Map result = queryFactory.select(peopleLike.peopleId, peopleLike.count()) + final Map result = queryFactory.select(peopleLike.peopleId, peopleLike.count()) .from(peopleLike) - .where(peopleLike.peopleId.in(peopleIds)) + .where(peopleLike.peopleId.in( + Arrays.stream(peopleIds) + .map(PeopleId::getValue) + .toArray(Long[]::new)) + ) .groupBy(peopleLike.peopleId) .fetch() .stream() .collect( toMap( - tuple -> tuple.get(peopleLike.peopleId), + tuple -> new PeopleId(tuple.get(peopleLike.peopleId)), tuple -> Optional.ofNullable(tuple.get(peopleLike.count())) .orElse(0L) ) diff --git a/src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikeJpaEntity.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikeJpaEntity.java similarity index 89% rename from src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikeJpaEntity.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikeJpaEntity.java index a35d63c2..c9492443 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikeJpaEntity.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikeJpaEntity.java @@ -22,8 +22,8 @@ public class PeopleLikeJpaEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Column(name = "client_id") - private Long clientId; + @Column(name = "member_id") + private Long memberId; @Column(name = "people_id") private Long peopleId; @@ -35,12 +35,12 @@ public class PeopleLikeJpaEntity { @Builder public PeopleLikeJpaEntity( final Long id, - final Long clientId, + final Long memberId, final Long peopleId, final LocalDateTime createdAt ) { this.id = id; - this.clientId = clientId; + this.memberId = memberId; this.peopleId = peopleId; this.createdAt = createdAt; } diff --git a/src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikeJpaRepository.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikeJpaRepository.java similarity index 61% rename from src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikeJpaRepository.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikeJpaRepository.java index 66113d69..dde8ab8a 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikeJpaRepository.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikeJpaRepository.java @@ -6,7 +6,7 @@ public interface PeopleLikeJpaRepository extends JpaRepository { - boolean existsByClientIdAndPeopleId(Long clientId, Long peopleId); + boolean existsByMemberIdAndPeopleId(Long memberId, Long peopleId); - Optional findByClientIdAndPeopleId(Long clientId, Long peopleId); + Optional findByMemberIdAndPeopleId(Long memberId, Long peopleId); } \ No newline at end of file diff --git a/src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikePersistenceAdapter.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikePersistenceAdapter.java similarity index 58% rename from src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikePersistenceAdapter.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikePersistenceAdapter.java index 8159756f..fb538173 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikePersistenceAdapter.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikePersistenceAdapter.java @@ -5,7 +5,10 @@ import es.princip.getp.application.like.people.port.out.LoadPeopleLikePort; import es.princip.getp.application.like.people.port.out.SavePeopleLikePort; import es.princip.getp.domain.like.people.model.PeopleLike; -import es.princip.getp.persistence.adapter.like.exception.NotFoundLikeException; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.application.like.exception.NotFoundLikeException; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; @@ -26,11 +29,6 @@ public void delete(final PeopleLike like) { repository.deleteById(entity.getId()); } - @Override - public boolean existsBy(final Long clientId, final Long peopleId) { - return repository.existsByClientIdAndPeopleId(clientId, peopleId); - } - @Override public void save(final PeopleLike like) { final PeopleLikeJpaEntity entity = mapper.mapToJpa(like); @@ -38,9 +36,28 @@ public void save(final PeopleLike like) { } @Override - public PeopleLike loadBy(final Long clientId, final Long peopleId) { - final PeopleLikeJpaEntity entity = repository.findByClientIdAndPeopleId(clientId, peopleId) + public PeopleLike loadBy(final MemberId memberId, final PeopleId peopleId) { + final PeopleLikeJpaEntity entity = repository.findByMemberIdAndPeopleId( + memberId.getValue(), + peopleId.getValue() + ) .orElseThrow(NotFoundLikeException::new); return mapper.mapToDomain(entity); } -} + + @Override + public boolean existsBy(final MemberId memberId, final PeopleId peopleId) { + return repository.existsByMemberIdAndPeopleId( + memberId.getValue(), + peopleId.getValue() + ); + } + + @Override + public Boolean existsBy(final Member member, final PeopleId peopleId) { + if (member == null || member.isPeople()) { + return null; + } + return existsBy(member.getId(), peopleId); + } +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikePersistenceMapper.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikePersistenceMapper.java similarity index 60% rename from src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikePersistenceMapper.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikePersistenceMapper.java index f0556cf1..4be2a801 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikePersistenceMapper.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/people/PeopleLikePersistenceMapper.java @@ -1,11 +1,17 @@ package es.princip.getp.persistence.adapter.like.people; import es.princip.getp.domain.like.people.model.PeopleLike; +import org.mapstruct.InheritInverseConfiguration; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; @Mapper(componentModel = "spring") public interface PeopleLikePersistenceMapper { + + @Mapping(source = "memberId", target = "memberId.value") + @Mapping(source = "peopleId", target = "peopleId.value") PeopleLike mapToDomain(PeopleLikeJpaEntity peopleLikeJpaEntity); + @InheritInverseConfiguration PeopleLikeJpaEntity mapToJpa(PeopleLike peopleLike); } diff --git a/src/main/java/es/princip/getp/persistence/adapter/like/project/CountProjectLikeAdapter.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/project/CountProjectLikeAdapter.java similarity index 58% rename from src/main/java/es/princip/getp/persistence/adapter/like/project/CountProjectLikeAdapter.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/project/CountProjectLikeAdapter.java index 6e5e174c..a1d86ea2 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/like/project/CountProjectLikeAdapter.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/project/CountProjectLikeAdapter.java @@ -1,6 +1,7 @@ package es.princip.getp.persistence.adapter.like.project; import es.princip.getp.application.like.project.port.out.CountProjectLikePort; +import es.princip.getp.domain.project.commission.model.ProjectId; import es.princip.getp.persistence.support.QueryDslSupport; import org.springframework.stereotype.Repository; @@ -11,32 +12,36 @@ import static java.util.stream.Collectors.toMap; @Repository -public class CountProjectLikeAdapter extends QueryDslSupport implements CountProjectLikePort { +class CountProjectLikeAdapter extends QueryDslSupport implements CountProjectLikePort { private static final QProjectLikeJpaEntity projectLike = QProjectLikeJpaEntity.projectLikeJpaEntity; @Override // TODO: 좋아요 수 조회 성능 개선 필요 - public Long countBy(final Long projectId) { + public Long countBy(final ProjectId projectId) { return queryFactory.select(projectLike.count()) .from(projectLike) - .where(projectLike.projectId.eq(projectId)) + .where(projectLike.projectId.eq(projectId.getValue())) .fetchOne(); } @Override - public Map countBy(final Long... projectIds) { - final Map counts = Arrays.stream(projectIds) + public Map countBy(final ProjectId... projectIds) { + final Map counts = Arrays.stream(projectIds) .collect(toMap(id -> id, id -> 0L)); - final Map result = queryFactory.select(projectLike.projectId, projectLike.count()) + final Map result = queryFactory.select(projectLike.projectId, projectLike.count()) .from(projectLike) - .where(projectLike.projectId.in(projectIds)) + .where(projectLike.projectId.in( + Arrays.stream(projectIds) + .map(ProjectId::getValue) + .toArray(Long[]::new) + )) .groupBy(projectLike.projectId) .fetch() .stream() .collect( toMap( - tuple -> tuple.get(projectLike.projectId), + tuple -> new ProjectId(tuple.get(projectLike.projectId)), tuple -> Optional.ofNullable(tuple.get(projectLike.count())) .orElse(0L) ) diff --git a/src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikeJpaEntity.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikeJpaEntity.java similarity index 89% rename from src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikeJpaEntity.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikeJpaEntity.java index 932dff4b..a225eb67 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikeJpaEntity.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikeJpaEntity.java @@ -22,8 +22,8 @@ public class ProjectLikeJpaEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Column(name = "people_id") - private Long peopleId; + @Column(name = "member_id") + private Long memberId; @Column(name = "project_id") private Long projectId; @@ -35,12 +35,12 @@ public class ProjectLikeJpaEntity { @Builder public ProjectLikeJpaEntity( final Long id, - final Long peopleId, + final Long memberId, final Long projectId, final LocalDateTime createdAt ) { this.id = id; - this.peopleId = peopleId; + this.memberId = memberId; this.projectId = projectId; this.createdAt = createdAt; } diff --git a/src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikeJpaRepository.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikeJpaRepository.java similarity index 61% rename from src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikeJpaRepository.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikeJpaRepository.java index 010355de..7408ff3e 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikeJpaRepository.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikeJpaRepository.java @@ -6,7 +6,7 @@ public interface ProjectLikeJpaRepository extends JpaRepository { - boolean existsByPeopleIdAndProjectId(Long peopleId, Long projectId); + boolean existsByMemberIdAndProjectId(Long memberId, Long projectId); - Optional findByPeopleIdAndProjectId(Long peopleId, Long projectId); + Optional findByMemberIdAndProjectId(Long memberId, Long projectId); } diff --git a/src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikePersistenceAdapter.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikePersistenceAdapter.java similarity index 61% rename from src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikePersistenceAdapter.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikePersistenceAdapter.java index e3b82092..1cac6339 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikePersistenceAdapter.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikePersistenceAdapter.java @@ -5,7 +5,10 @@ import es.princip.getp.application.like.project.port.out.LoadProjectLikePort; import es.princip.getp.application.like.project.port.out.SaveProjectLikePort; import es.princip.getp.domain.like.project.model.ProjectLike; -import es.princip.getp.persistence.adapter.like.exception.NotFoundLikeException; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.project.commission.model.ProjectId; +import es.princip.getp.application.like.exception.NotFoundLikeException; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; @@ -27,8 +30,11 @@ public void delete(final ProjectLike projectLike) { } @Override - public boolean existsBy(final Long peopleId, Long projectId) { - return repository.existsByPeopleIdAndProjectId(peopleId, projectId); + public Boolean existsBy(final Member member, final ProjectId projectId) { + if (member == null || member.isClient()) { + return null; + } + return existsBy(member.getId(), projectId); } @Override @@ -38,9 +44,20 @@ public void save(final ProjectLike like) { } @Override - public ProjectLike loadBy(final Long peopleId, final Long projectId) { - final ProjectLikeJpaEntity jpaEntity = repository.findByPeopleIdAndProjectId(peopleId, projectId) + public ProjectLike loadBy(final MemberId memberId, final ProjectId projectId) { + final ProjectLikeJpaEntity jpaEntity = repository.findByMemberIdAndProjectId( + memberId.getValue(), + projectId.getValue() + ) .orElseThrow(NotFoundLikeException::new); return mapper.mapToDomain(jpaEntity); } + + @Override + public boolean existsBy(final MemberId memberId, final ProjectId projectId) { + return repository.existsByMemberIdAndProjectId( + memberId.getValue(), + projectId.getValue() + ); + } } \ No newline at end of file diff --git a/src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikePersistenceMapper.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikePersistenceMapper.java similarity index 60% rename from src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikePersistenceMapper.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikePersistenceMapper.java index 71edf083..f6f42768 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikePersistenceMapper.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/like/project/ProjectLikePersistenceMapper.java @@ -1,11 +1,17 @@ package es.princip.getp.persistence.adapter.like.project; import es.princip.getp.domain.like.project.model.ProjectLike; +import org.mapstruct.InheritInverseConfiguration; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; @Mapper(componentModel = "spring") public interface ProjectLikePersistenceMapper { + + @Mapping(source = "projectId", target = "projectId.value") + @Mapping(source = "memberId", target = "memberId.value") ProjectLike mapToDomain(ProjectLikeJpaEntity projectLikeJpaEntity); + @InheritInverseConfiguration ProjectLikeJpaEntity mapToJpa(ProjectLike projectLike); } diff --git a/src/main/java/es/princip/getp/persistence/adapter/member/MemberJpaEntity.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/member/MemberJpaEntity.java similarity index 96% rename from src/main/java/es/princip/getp/persistence/adapter/member/MemberJpaEntity.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/member/MemberJpaEntity.java index 73cce09c..3c361886 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/member/MemberJpaEntity.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/member/MemberJpaEntity.java @@ -26,7 +26,7 @@ public class MemberJpaEntity extends BaseTimeJpaEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "member_id") - private Long memberId; + private Long id; @Column(name = "email") private String email; @@ -56,7 +56,7 @@ public class MemberJpaEntity extends BaseTimeJpaEntity { @Builder public MemberJpaEntity( - final Long memberId, + final Long id, final String email, final String password, final MemberType memberType, @@ -69,7 +69,7 @@ public MemberJpaEntity( ) { super(createdAt, updatedAt); - this.memberId = memberId; + this.id = id; this.email = email; this.password = password; this.memberType = memberType; diff --git a/src/main/java/es/princip/getp/persistence/adapter/member/MemberJpaRepository.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/member/MemberJpaRepository.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/member/MemberJpaRepository.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/member/MemberJpaRepository.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/member/MemberPersistenceAdapter.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/member/MemberPersistenceAdapter.java similarity index 63% rename from src/main/java/es/princip/getp/persistence/adapter/member/MemberPersistenceAdapter.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/member/MemberPersistenceAdapter.java index c6f03619..6b987bc7 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/member/MemberPersistenceAdapter.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/member/MemberPersistenceAdapter.java @@ -1,11 +1,13 @@ package es.princip.getp.persistence.adapter.member; +import es.princip.getp.application.member.exception.NotFoundMemberException; import es.princip.getp.application.member.port.out.CheckMemberPort; import es.princip.getp.application.member.port.out.LoadMemberPort; import es.princip.getp.application.member.port.out.SaveMemberPort; import es.princip.getp.application.member.port.out.UpdateMemberPort; import es.princip.getp.domain.common.model.Email; import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; @@ -14,39 +16,39 @@ public class MemberPersistenceAdapter implements SaveMemberPort, LoadMemberPort, CheckMemberPort, UpdateMemberPort { private final MemberPersistenceMapper mapper; - - private final MemberJpaRepository memberJpaRepository; + private final MemberJpaRepository repository; @Override public Member loadBy(final Email email) { - final MemberJpaEntity memberJpaEntity = memberJpaRepository.findByEmail(email.getValue()) + final MemberJpaEntity memberJpaEntity = repository.findByEmail(email.getValue()) .orElseThrow(NotFoundMemberException::new); return mapper.mapToDomain(memberJpaEntity); } @Override - public Member loadBy(final Long memberId) { - final MemberJpaEntity memberJpaEntity = memberJpaRepository.findById(memberId) + public Member loadBy(final MemberId memberId) { + final MemberJpaEntity memberJpaEntity = repository.findById(memberId.getValue()) .orElseThrow(NotFoundMemberException::new); return mapper.mapToDomain(memberJpaEntity); } @Override - public Long save(final Member member) { + public MemberId save(final Member member) { final MemberJpaEntity memberJpaEntity = mapper.mapToJpa(member); - return memberJpaRepository.save(memberJpaEntity).getMemberId(); + final Long id = repository.save(memberJpaEntity).getId(); + return new MemberId(id); } @Override public void update(final Member member) { - if (!memberJpaRepository.existsById(member.getMemberId())) { + if (!repository.existsById(member.getId().getValue())) { throw new NotFoundMemberException(); } save(member); } @Override - public boolean existsByEmail(final Email email) { - return memberJpaRepository.existsByEmail(email.getValue()); + public boolean existsBy(final Email email) { + return repository.existsByEmail(email.getValue()); } } diff --git a/src/main/java/es/princip/getp/persistence/adapter/member/MemberPersistenceMapper.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/member/MemberPersistenceMapper.java similarity index 94% rename from src/main/java/es/princip/getp/persistence/adapter/member/MemberPersistenceMapper.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/member/MemberPersistenceMapper.java index dbfc0d5a..3f2b4a33 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/member/MemberPersistenceMapper.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/member/MemberPersistenceMapper.java @@ -11,11 +11,13 @@ @Mapper(componentModel = "spring", uses = {PhoneNumberPersistenceMapper.class}) public interface MemberPersistenceMapper { + @Mapping(source = "id", target = "id.value") @Mapping(source = "email", target = "email.value") @Mapping(source = "password", target = "password.value") @Mapping(constant = "true", target = "password.encoded") Member mapToDomain(MemberJpaEntity memberJpaEntity); + @Mapping(target = "id", source = "id.value") @Mapping(target = "email", source = "email.value") @Mapping(target = "password", source = "password.value") @Mapping(target = "nickname", source = "nickname.value") diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/member/MemberPersistenceUtil.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/member/MemberPersistenceUtil.java new file mode 100644 index 00000000..c4f0fb05 --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/member/MemberPersistenceUtil.java @@ -0,0 +1,18 @@ +package es.princip.getp.persistence.adapter.member; + +import com.querydsl.core.types.dsl.BooleanExpression; +import es.princip.getp.domain.member.model.MemberId; + +import java.util.Optional; + +public class MemberPersistenceUtil { + + private static final QMemberJpaEntity member = QMemberJpaEntity.memberJpaEntity; + + public static BooleanExpression memberIdEq(final MemberId memberId) { + return Optional.ofNullable(memberId) + .map(MemberId::getValue) + .map(member.id::eq) + .orElse(null); + } +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/persistence/adapter/member/ServiceTermAgreementJpaVO.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/member/ServiceTermAgreementJpaVO.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/member/ServiceTermAgreementJpaVO.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/member/ServiceTermAgreementJpaVO.java diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/FindMyPeopleAdapter.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/FindMyPeopleAdapter.java new file mode 100644 index 00000000..705cfaa0 --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/FindMyPeopleAdapter.java @@ -0,0 +1,68 @@ +package es.princip.getp.persistence.adapter.people; + +import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.Expressions; +import es.princip.getp.application.people.dto.response.people.MyPeopleResponse; +import es.princip.getp.application.people.dto.response.peopleProfile.PeopleProfileDetailResponse; +import es.princip.getp.application.people.exception.NotFoundPeopleException; +import es.princip.getp.application.people.port.out.FindMyPeoplePort; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.exception.NotRegisteredPeopleProfileException; +import es.princip.getp.persistence.adapter.member.QMemberJpaEntity; +import es.princip.getp.persistence.adapter.people.mapper.PeopleQueryMapper; +import es.princip.getp.persistence.adapter.people.model.PeopleProfileJpaVO; +import es.princip.getp.persistence.adapter.people.model.QPeopleJpaEntity; +import es.princip.getp.persistence.support.QueryDslSupport; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +// TODO: 조회 성능 개선 필요 +class FindMyPeopleAdapter extends QueryDslSupport implements FindMyPeoplePort { + + private static final QPeopleJpaEntity people = QPeopleJpaEntity.peopleJpaEntity; + private static final QMemberJpaEntity member = QMemberJpaEntity.memberJpaEntity; + + private final PeopleQueryMapper mapper; + + @Override + public MyPeopleResponse findBy(final MemberId memberId) { + return Optional.ofNullable( + queryFactory.select( + Projections.constructor( + MyPeopleResponse.class, + people.id, + people.email, + member.nickname, + member.phoneNumber, + member.profileImage, + Expressions.asNumber(0).as("completedProjectsCount"), + Expressions.asNumber(0).as("likesCount"), + people.createdAt, + people.updatedAt + ) + ) + .from(people) + .join(member).on(people.memberId.eq(member.id)) + .where(people.memberId.eq(memberId.getValue())) + .fetchOne() + ).orElseThrow(NotFoundPeopleException::new); + } + + @Override + public PeopleProfileDetailResponse findDetailProfileBy(final MemberId memberId) { + final PeopleProfileJpaVO profile = Optional.ofNullable( + queryFactory.select(people) + .from(people) + .where(people.memberId.eq(memberId.getValue()) + .and(people.profile.isNotNull())) + .fetchOne() + ) + .orElseThrow(NotRegisteredPeopleProfileException::new) + .getProfile(); + return mapper.mapToDetailResponse(profile); + } +} \ No newline at end of file diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/FindPeopleAdapter.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/FindPeopleAdapter.java new file mode 100644 index 00000000..2856888b --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/FindPeopleAdapter.java @@ -0,0 +1,181 @@ +package es.princip.getp.persistence.adapter.people; + +import com.querydsl.core.Tuple; +import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.jpa.impl.JPAQuery; +import es.princip.getp.application.people.dto.response.people.CardPeopleResponse; +import es.princip.getp.application.people.dto.response.people.PeopleDetailResponse; +import es.princip.getp.application.like.people.port.out.CheckPeopleLikePort; +import es.princip.getp.application.like.people.port.out.CountPeopleLikePort; +import es.princip.getp.application.people.dto.command.PeopleSearchFilter; +import es.princip.getp.application.people.exception.NotFoundPeopleException; +import es.princip.getp.application.people.port.out.FindPeoplePort; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.persistence.adapter.like.people.QPeopleLikeJpaEntity; +import es.princip.getp.persistence.adapter.member.QMemberJpaEntity; +import es.princip.getp.persistence.adapter.people.mapper.PeopleQueryMapper; +import es.princip.getp.persistence.adapter.people.model.PeopleJpaEntity; +import es.princip.getp.persistence.adapter.people.model.PeopleProfileJpaVO; +import es.princip.getp.persistence.adapter.people.model.QPeopleJpaEntity; +import es.princip.getp.persistence.support.QueryDslSupport; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Repository; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + +import static es.princip.getp.persistence.adapter.member.MemberPersistenceUtil.memberIdEq; +import static es.princip.getp.persistence.adapter.people.PeoplePersistenceUtil.getPeopleIds; +import static java.util.stream.Collectors.toMap; + +@Repository +@RequiredArgsConstructor +// TODO: 조회 성능 개선 필요 +class FindPeopleAdapter extends QueryDslSupport implements FindPeoplePort { + + private static final QPeopleJpaEntity people = QPeopleJpaEntity.peopleJpaEntity; + private static final QMemberJpaEntity qMember = QMemberJpaEntity.memberJpaEntity; + private static final QPeopleLikeJpaEntity like = QPeopleLikeJpaEntity.peopleLikeJpaEntity; + + private final CheckPeopleLikePort checkPeopleLikePort; + private final CountPeopleLikePort countPeopleLikePort; + + private final PeopleQueryMapper mapper; + + private Map findMemberAndPeopleBy(final PeopleId... peopleIds) { + final Long[] ids = Arrays.stream(peopleIds) + .map(PeopleId::getValue) + .toArray(Long[]::new); + return queryFactory.select( + qMember.nickname, + qMember.profileImage, + people.id + ) + .from(people) + .join(qMember) + .on(people.memberId.eq(qMember.id)) + .where(people.id.in(ids)).fetch().stream() + .collect(toMap(tuple -> tuple.get(people.id), Function.identity())); + } + + private Optional findMemberAndPeopleBy(final PeopleId peopleId) { + return Optional.ofNullable( + queryFactory.select( + qMember.nickname, + qMember.profileImage, + people.id + ) + .from(people) + .join(qMember).on(people.memberId.eq(qMember.id)) + .where(people.id.eq(peopleId.getValue())) + .fetchOne() + ); + } + + private JPAQuery buildQuery( + final JPAQuery selectFrom, + final PeopleSearchFilter filter, + final MemberId memberId + ) { + if (filter.isLiked()) { + selectFrom.join(like).on(people.id.eq(like.peopleId)) + .join(qMember).on(like.memberId.eq(qMember.id)) + .where(memberIdEq(memberId)); + } + if (!StringUtils.isAllBlank(filter.getKeyword())) { + selectFrom.join(qMember).on(people.memberId.eq(qMember.id)) + .where(qMember.nickname.startsWith(filter.getKeyword())); + } + selectFrom.where(people.profile.isNotNull()); + return selectFrom; + } + + private List getCardPeopleContent( + final Pageable pageable, + final PeopleSearchFilter filter, + final MemberId memberId + ) { + final List content = buildQuery(queryFactory.selectFrom(people), filter, memberId) + .orderBy(getOrderSpecifiers(pageable.getSort())) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + final PeopleId[] peopleIds = getPeopleIds(content); + + final Map likesCounts = countPeopleLikePort.countBy(peopleIds); + final Map memberAndPeople = findMemberAndPeopleBy(peopleIds); + + return content.stream().map(people -> { + final Long peopleId = people.getId(); + return new CardPeopleResponse( + peopleId, + memberAndPeople.get(peopleId).get(qMember.nickname), + memberAndPeople.get(peopleId).get(qMember.profileImage), + 0, + likesCounts.get(new PeopleId(peopleId)), + mapper.mapToCardResponse(people.getProfile()) + ); + }).toList(); + } + + private static OrderSpecifier[] getOrderSpecifiers(final Sort sort) { + return sort.stream() + .map(PeoplePersistenceUtil::getOrderSpecifier) + .toArray(OrderSpecifier[]::new); + } + + @Override + public Page findCardBy( + final Pageable pageable, + final PeopleSearchFilter filter, + final MemberId memberId + ) { + return paginate( + pageable, + getCardPeopleContent(pageable, filter, memberId), + countQuery -> buildQuery(countQuery.select(people.count()).from(people), filter, memberId) + ); + } + + private PeopleProfileJpaVO fetchPeopleProfile(final PeopleId peopleId) { + return Optional.ofNullable( + queryFactory.select(people) + .from(people) + .where(people.id.eq(peopleId.getValue()) + .and(people.profile.isNotNull())) + .fetchOne() + ) + .orElseThrow(NotFoundPeopleException::new) + .getProfile(); + } + + @Override + public PeopleDetailResponse findDetailBy(final Member member, final PeopleId peopleId) { + final PeopleProfileJpaVO profile = fetchPeopleProfile(peopleId); + final Tuple memberAndPeople = findMemberAndPeopleBy(peopleId) + .orElseThrow(NotFoundPeopleException::new); + final long completedProjectsCount = 0; + final Long likesCount = countPeopleLikePort.countBy(peopleId); + final Boolean liked = checkPeopleLikePort.existsBy(member, peopleId); + + return new PeopleDetailResponse( + peopleId.getValue(), + memberAndPeople.get(qMember.nickname), + memberAndPeople.get(qMember.profileImage), + completedProjectsCount, + likesCount, + liked, + mapper.mapToDetailResponse(profile) + ); + } +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/persistence/adapter/people/PeopleJpaRepository.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/PeopleJpaRepository.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/people/PeopleJpaRepository.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/PeopleJpaRepository.java diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/PeoplePersistenceAdapter.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/PeoplePersistenceAdapter.java new file mode 100644 index 00000000..18f28d87 --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/PeoplePersistenceAdapter.java @@ -0,0 +1,90 @@ +package es.princip.getp.persistence.adapter.people; + +import es.princip.getp.application.people.exception.NotFoundPeopleException; +import es.princip.getp.application.people.port.out.*; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.persistence.adapter.people.mapper.PeoplePersistenceMapper; +import es.princip.getp.persistence.adapter.people.model.PeopleJpaEntity; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +@Repository +@RequiredArgsConstructor +class PeoplePersistenceAdapter implements + SavePeoplePort, + LoadPeoplePort, + CheckPeoplePort, + UpdatePeoplePort, + DeletePeoplePort { + + private final PeoplePersistenceMapper mapper; + private final PeopleJpaRepository repository; + + @Override + public boolean existsBy(final MemberId memberId) { + return repository.existsByMemberId(memberId.getValue()); + } + + @Override + public People loadBy(final MemberId memberId) { + final PeopleJpaEntity peopleJpaEntity = repository.findByMemberId(memberId.getValue()) + .orElseThrow(NotFoundPeopleException::new); + return mapper.mapToDomain(peopleJpaEntity); + } + + @Override + public People loadBy(final PeopleId peopleId) { + final PeopleJpaEntity peopleJpaEntity = repository.findById(peopleId.getValue()) + .orElseThrow(NotFoundPeopleException::new); + return mapper.mapToDomain(peopleJpaEntity); + } + + @Override + public Set loadBy(final Set peopleIds) { + final Set ids = peopleIds.stream() + .map(PeopleId::getValue) + .collect(Collectors.toSet()); + final Set entities = new HashSet<>(repository.findAllById(ids)); + final Set founded = entities.stream() + .map(PeopleJpaEntity::getId) + .collect(Collectors.toSet()); + ids.removeAll(founded); + if (!ids.isEmpty()) { + throw NotFoundPeopleException.from(ids); + } + return entities.stream() + .map(mapper::mapToDomain) + .collect(Collectors.toSet()); + } + + @Override + public PeopleId save(final People people) { + final PeopleJpaEntity peopleJpaEntity = mapper.mapToJpa(people); + final Long id = repository.save(peopleJpaEntity).getId(); + return new PeopleId(id); + } + + @Override + public void update(final People people) { + final Long id = people.getId().getValue(); + if (!repository.existsById(id)) { + throw new NotFoundPeopleException(); + } + repository.save(mapper.mapToJpa(people)); + } + + @Override + public void delete(final PeopleId peopleId) { + final Long id = peopleId.getValue(); + if (!repository.existsById(id)) { + throw new NotFoundPeopleException(); + } + repository.deleteById(id); + } +} diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/PeoplePersistenceUtil.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/PeoplePersistenceUtil.java new file mode 100644 index 00000000..aa28ca4e --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/PeoplePersistenceUtil.java @@ -0,0 +1,38 @@ +package es.princip.getp.persistence.adapter.people; + +import com.querydsl.core.types.Order; +import com.querydsl.core.types.OrderSpecifier; +import es.princip.getp.application.people.dto.command.PeopleSearchOrder; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.persistence.adapter.people.model.PeopleJpaEntity; +import es.princip.getp.persistence.adapter.people.model.QPeopleJpaEntity; +import org.springframework.data.domain.Sort; + +import java.util.List; + +import static com.querydsl.core.types.Order.ASC; +import static com.querydsl.core.types.Order.DESC; + +public class PeoplePersistenceUtil { + + private static final QPeopleJpaEntity people = QPeopleJpaEntity.peopleJpaEntity; + + public static PeopleId[] getPeopleIds(final List peopleList) { + return peopleList.stream() + .map(PeopleJpaEntity::getId) + .map(PeopleId::new) + .toArray(PeopleId[]::new); + } + + public static OrderSpecifier getOrderSpecifier(final Sort.Order order) { + if (order == null) { + return new OrderSpecifier<>(DESC, people.id); + } + final String property = order.getProperty(); + final Order converted = order.isAscending() ? ASC : DESC; + return switch (PeopleSearchOrder.from(property)) { + case CREATED_AT -> new OrderSpecifier<>(converted, people.createdAt); + default -> new OrderSpecifier<>(converted, people.id); + }; + } +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/persistence/adapter/people/mapper/PeoplePersistenceMapper.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/mapper/PeoplePersistenceMapper.java similarity index 79% rename from src/main/java/es/princip/getp/persistence/adapter/people/mapper/PeoplePersistenceMapper.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/mapper/PeoplePersistenceMapper.java index 145e91bf..fae44994 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/people/mapper/PeoplePersistenceMapper.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/mapper/PeoplePersistenceMapper.java @@ -1,6 +1,7 @@ package es.princip.getp.persistence.adapter.people.mapper; import es.princip.getp.domain.people.model.People; +import es.princip.getp.domain.people.model.PeopleId; import es.princip.getp.domain.people.model.PeopleProfile; import es.princip.getp.domain.people.model.Portfolio; import es.princip.getp.persistence.adapter.common.mapper.HashtagPersistenceMapper; @@ -14,12 +15,15 @@ @Mapper( componentModel = "spring", - uses = {TechStackPersistenceMapper.class, HashtagPersistenceMapper.class} + uses = { + TechStackPersistenceMapper.class, + HashtagPersistenceMapper.class + } ) public abstract class PeoplePersistenceMapper { + @Mapping(source = "memberId", target = "memberId.value") @Mapping(source = "email", target = "info.email.value") - @Mapping(source = "peopleType", target = "info.peopleType") public abstract People mapToDomain(PeopleJpaEntity people); @Mapping(source = "school", target = "education.school") @@ -37,4 +41,12 @@ public abstract class PeoplePersistenceMapper { @InheritInverseConfiguration protected abstract PortfolioJpaVO mapToJpa(Portfolio portfolio); + + public PeopleId mapToDomain(final Long id) { + return new PeopleId(id); + } + + public Long mapToJpa(final PeopleId id) { + return id == null ? null : id.getValue(); + } } diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/mapper/PeopleQueryMapper.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/mapper/PeopleQueryMapper.java new file mode 100644 index 00000000..59a0c1f6 --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/mapper/PeopleQueryMapper.java @@ -0,0 +1,29 @@ +package es.princip.getp.persistence.adapter.people.mapper; + +import es.princip.getp.application.people.dto.response.peopleProfile.CardPeopleProfileResponse; +import es.princip.getp.application.people.dto.response.peopleProfile.PeopleProfileDetailResponse; +import es.princip.getp.application.people.dto.response.peopleProfile.PortfolioResponse; +import es.princip.getp.persistence.adapter.common.mapper.TechStackPersistenceMapper; +import es.princip.getp.persistence.adapter.people.model.PeopleProfileJpaVO; +import es.princip.getp.persistence.adapter.people.model.PortfolioJpaVO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper( + componentModel = "spring", + uses = { + PeoplePersistenceMapper.class, + TechStackPersistenceMapper.class + } +) +public abstract class PeopleQueryMapper { + + protected abstract PortfolioResponse mapToResponse(PortfolioJpaVO portfolio); + + public abstract CardPeopleProfileResponse mapToCardResponse(PeopleProfileJpaVO profile); + + @Mapping(source = "school", target = "education.school") + @Mapping(source = "major", target = "education.major") + public abstract PeopleProfileDetailResponse mapToDetailResponse(PeopleProfileJpaVO profile); + +} diff --git a/src/main/java/es/princip/getp/persistence/adapter/people/model/PeopleJpaEntity.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/model/PeopleJpaEntity.java similarity index 84% rename from src/main/java/es/princip/getp/persistence/adapter/people/model/PeopleJpaEntity.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/model/PeopleJpaEntity.java index 0be1f053..950fba7b 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/people/model/PeopleJpaEntity.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/model/PeopleJpaEntity.java @@ -1,6 +1,5 @@ package es.princip.getp.persistence.adapter.people.model; -import es.princip.getp.domain.people.model.PeopleType; import es.princip.getp.persistence.adapter.BaseTimeJpaEntity; import jakarta.persistence.*; import lombok.AccessLevel; @@ -27,10 +26,6 @@ public class PeopleJpaEntity extends BaseTimeJpaEntity { @Column(name = "email") private String email; - @Enumerated(EnumType.STRING) - @Column(name = "people_type") - private PeopleType peopleType; - @Embedded private PeopleProfileJpaVO profile; @@ -46,7 +41,6 @@ public PeopleJpaEntity( final Long id, final Long memberId, final String email, - final PeopleType peopleType, final PeopleProfileJpaVO profile, final LocalDateTime createdAt, final LocalDateTime updatedAt @@ -56,7 +50,6 @@ public PeopleJpaEntity( this.id = id; this.memberId = memberId; this.email = email; - this.peopleType = peopleType; this.profile = profile; } } diff --git a/src/main/java/es/princip/getp/persistence/adapter/people/model/PeopleProfileJpaVO.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/model/PeopleProfileJpaVO.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/people/model/PeopleProfileJpaVO.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/model/PeopleProfileJpaVO.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/people/model/PortfolioJpaVO.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/model/PortfolioJpaVO.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/people/model/PortfolioJpaVO.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/people/model/PortfolioJpaVO.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/ProjectPersistenceMapper.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/ProjectPersistenceMapper.java similarity index 60% rename from src/main/java/es/princip/getp/persistence/adapter/project/ProjectPersistenceMapper.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/ProjectPersistenceMapper.java index 79b469f6..b635c976 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/project/ProjectPersistenceMapper.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/ProjectPersistenceMapper.java @@ -4,12 +4,23 @@ import es.princip.getp.persistence.adapter.common.mapper.AttachmentFilePersistenceMapper; import es.princip.getp.persistence.adapter.common.mapper.HashtagPersistenceMapper; import es.princip.getp.persistence.adapter.project.commission.ProjectJpaEntity; +import org.mapstruct.InheritInverseConfiguration; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; -@Mapper(componentModel = "spring", uses = {AttachmentFilePersistenceMapper.class, HashtagPersistenceMapper.class}) +@Mapper( + componentModel = "spring", + uses = { + AttachmentFilePersistenceMapper.class, + HashtagPersistenceMapper.class + } +) public interface ProjectPersistenceMapper { + @Mapping(source = "id", target = "id.value") + @Mapping(source = "clientId", target = "clientId.value") Project mapToDomain(ProjectJpaEntity projectJpaEntity); + @InheritInverseConfiguration ProjectJpaEntity mapToJpa(Project project); } diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/ProjectPersistenceUtil.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/ProjectPersistenceUtil.java similarity index 51% rename from src/main/java/es/princip/getp/persistence/adapter/project/ProjectPersistenceUtil.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/ProjectPersistenceUtil.java index 09d5f74f..7a5bc99e 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/project/ProjectPersistenceUtil.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/ProjectPersistenceUtil.java @@ -1,14 +1,15 @@ package es.princip.getp.persistence.adapter.project; import es.princip.getp.domain.project.commission.model.Project; +import es.princip.getp.domain.project.commission.model.ProjectId; import java.util.List; public class ProjectPersistenceUtil { - public static Long[] toProjectIds(final List projects) { + public static ProjectId[] toProjectIds(final List projects) { return projects.stream() - .map(Project::getProjectId) - .toArray(Long[]::new); + .map(Project::getId) + .toArray(ProjectId[]::new); } } diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/ProjectQueryMapper.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/ProjectQueryMapper.java new file mode 100644 index 00000000..86a3c56d --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/ProjectQueryMapper.java @@ -0,0 +1,37 @@ +package es.princip.getp.persistence.adapter.project; + +import es.princip.getp.application.project.commission.dto.response.ProjectClientResponse; +import es.princip.getp.application.project.commission.dto.response.ProjectDetailResponse; +import es.princip.getp.persistence.adapter.common.mapper.DurationPersistenceMapper; +import es.princip.getp.persistence.adapter.project.commission.ProjectJpaEntity; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper( + componentModel = "spring", + uses = { + DurationPersistenceMapper.class + } +) +public interface ProjectQueryMapper { + + @Mapping(source = "project.id", target = "projectId") + @Mapping(source = "project.title", target = "title") + @Mapping(source = "project.payment", target = "payment") + @Mapping(source = "project.recruitmentCount", target = "recruitmentCount") + @Mapping(source = "project.applicationDuration", target = "applicationDuration") + @Mapping(source = "project.estimatedDuration", target = "estimatedDuration") + @Mapping(source = "project.description", target = "description") + @Mapping(source = "project.meetingType", target = "meetingType") + @Mapping(source = "project.category", target = "category") + @Mapping(source = "project.status", target = "status") + @Mapping(source = "project.attachmentFiles", target = "attachmentFiles") + @Mapping(source = "project.hashtags", target = "hashtags") + ProjectDetailResponse mapToDetailResponse( + ProjectJpaEntity project, + Long applicantsCount, + Long likesCount, + Boolean liked, + ProjectClientResponse client + ); +} diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/CountProjectApplicationAdapter.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/CountProjectApplicationAdapter.java new file mode 100644 index 00000000..d0e9f928 --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/CountProjectApplicationAdapter.java @@ -0,0 +1,92 @@ +package es.princip.getp.persistence.adapter.project.apply; + +import com.querydsl.core.types.dsl.BooleanExpression; +import es.princip.getp.application.project.apply.port.out.CountProjectApplicationPort; +import es.princip.getp.domain.project.apply.model.ProjectApplicationStatus; +import es.princip.getp.domain.project.commission.model.ProjectId; +import es.princip.getp.persistence.adapter.project.apply.model.QProjectApplicationJpaEntity; +import es.princip.getp.persistence.adapter.project.apply.model.QTeammateJpaEntity; +import es.princip.getp.persistence.support.QueryDslSupport; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.Arrays; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import static es.princip.getp.domain.project.apply.model.ProjectApplicationStatus.PENDING_TEAM_APPROVAL; + +@Repository +@RequiredArgsConstructor +class CountProjectApplicationAdapter extends QueryDslSupport implements CountProjectApplicationPort { + + private static final QProjectApplicationJpaEntity application + = QProjectApplicationJpaEntity.projectApplicationJpaEntity; + private static final QTeammateJpaEntity teammate = QTeammateJpaEntity.teammateJpaEntity; + + private static final ProjectApplicationStatus[] statusesWithoutPendingTeamApproval + = Arrays.stream(ProjectApplicationStatus.values()) + .filter(status -> !status.equals(PENDING_TEAM_APPROVAL)) + .toArray(ProjectApplicationStatus[]::new); + + private static BooleanExpression applicationStatusNePendingTeamApproval() { + return application.status.in(statusesWithoutPendingTeamApproval); + } + + private static BooleanExpression teammateApplicationStatusNePendingTeamApproval() { + return teammate.application.status.in(statusesWithoutPendingTeamApproval); + } + + @Override + public Map countBy(final ProjectId... projectId) { + final Long[] ids = Arrays.stream(projectId) + .map(ProjectId::getValue) + .toArray(Long[]::new); + + final Map counts = queryFactory.select(application.projectId, application.count()) + .from(application) + .where(application.projectId.in(ids) + .and(applicationStatusNePendingTeamApproval())) + .groupBy(application.projectId) + .fetch() + .stream() + .collect(Collectors.toMap( + tuple -> new ProjectId(tuple.get(application.projectId)), + tuple -> Optional.ofNullable(tuple.get(application.count())) + .orElse(0L) + )); + + queryFactory.select(teammate.application.projectId, teammate.count()) + .from(teammate) + .where(teammate.application.projectId.in(ids) + .and(teammateApplicationStatusNePendingTeamApproval())) + .groupBy(teammate.application.projectId) + .fetch() + .forEach(tuple -> { + final ProjectId key = new ProjectId(tuple.get(teammate.application.projectId)); + final Long value = Optional.ofNullable(tuple.get(teammate.count())) + .orElse(0L); + counts.put(key, counts.getOrDefault(key, 0L) + value); + }); + + return counts; + } + + @Override + public Long countBy(final ProjectId projectId) { + Long count = queryFactory.select(application.count()) + .from(application) + .where(application.projectId.eq(projectId.getValue()) + .and(applicationStatusNePendingTeamApproval())) + .fetchOne(); + + count += queryFactory.select(teammate.count()) + .from(teammate) + .where(teammate.application.projectId.eq(projectId.getValue()) + .and(teammateApplicationStatusNePendingTeamApproval())) + .fetchOne(); + + return count; + } +} \ No newline at end of file diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/FindProjectApplicantAdapter.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/FindProjectApplicantAdapter.java new file mode 100644 index 00000000..1c6491c9 --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/FindProjectApplicantAdapter.java @@ -0,0 +1,123 @@ +package es.princip.getp.persistence.adapter.project.apply; + +import com.querydsl.core.Tuple; +import com.querydsl.core.group.GroupBy; +import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.BooleanExpression; +import es.princip.getp.application.project.apply.dto.response.ProjectApplicantResponse; +import es.princip.getp.application.project.apply.dto.response.ProjectApplicantTeammateResponse; +import es.princip.getp.application.project.apply.port.out.FindProjectApplicantPort; +import es.princip.getp.application.support.Cursor; +import es.princip.getp.application.support.CursorPageable; +import es.princip.getp.domain.people.model.Education; +import es.princip.getp.domain.project.commission.model.ProjectId; +import es.princip.getp.persistence.adapter.member.QMemberJpaEntity; +import es.princip.getp.persistence.adapter.people.model.QPeopleJpaEntity; +import es.princip.getp.persistence.adapter.project.apply.model.QProjectApplicationJpaEntity; +import es.princip.getp.persistence.adapter.project.apply.model.QTeammateJpaEntity; +import es.princip.getp.persistence.support.QueryDslSupport; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Slice; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static com.querydsl.core.types.Order.DESC; +import static es.princip.getp.domain.project.apply.model.ProjectApplicationStatus.PENDING_TEAM_APPROVAL; + +@Repository +@RequiredArgsConstructor +class FindProjectApplicantAdapter extends QueryDslSupport implements FindProjectApplicantPort { + + private static final QPeopleJpaEntity people = QPeopleJpaEntity.peopleJpaEntity; + private static final QMemberJpaEntity member = QMemberJpaEntity.memberJpaEntity; + private static final QProjectApplicationJpaEntity application = QProjectApplicationJpaEntity.projectApplicationJpaEntity; + private static final QTeammateJpaEntity teammate = QTeammateJpaEntity.teammateJpaEntity; + + private BooleanExpression getCursorBooleanExpression(final CursorPageable pageable) { + if (!pageable.hasCursor()) { + return null; + } + final var idCursor = pageable.getCursor().getId(); + return application.id.lt(idCursor); + } + + private List fetchApplicants( + final CursorPageable pageable, + final ProjectId projectId + ) { + return queryFactory.select( + application.id, + application.applicantId, + people.profile.school, + people.profile.major, + member.nickname, + member.profileImage, + application.status + ) + .from(application) + .join(people).on(application.applicantId.eq(people.id)) + .join(member).on(people.memberId.eq(member.id)) + .where(application.projectId.eq(projectId.getValue()) + .and(application.status.ne(PENDING_TEAM_APPROVAL)) + .and(getCursorBooleanExpression(pageable))) + .orderBy(new OrderSpecifier<>(DESC, application.id)) + .limit(pageable.getPageSize() + 1) + .fetch(); + } + + private Map> fetchTeammates(final Long[] ids) { + return queryFactory.select( + teammate.application.id, + teammate.peopleId, + member.nickname, + member.profileImage + ) + .from(teammate) + .join(people).on(teammate.peopleId.eq(people.id)) + .join(member).on(people.memberId.eq(member.id)) + .where(teammate.application.id.in(ids)) + .transform(GroupBy.groupBy(teammate.application.id).as(GroupBy.list( + Projections.constructor( + ProjectApplicantTeammateResponse.class, + teammate.peopleId, + member.nickname, + member.profileImage + ) + ))); + } + + private List fetchContent( + final CursorPageable pageable, + final ProjectId projectId + ) { + final var applicants = fetchApplicants(pageable, projectId); + final var ids = applicants.stream() + .map(tuple -> tuple.get(application.id)) + .toArray(Long[]::new); + final var teammates = fetchTeammates(ids); + return applicants.stream() + .map(tuple -> new ProjectApplicantResponse( + tuple.get(application.applicantId), + tuple.get(member.nickname), + tuple.get(member.profileImage), + Education.of(tuple.get(people.profile.school), tuple.get(people.profile.major)), + tuple.get(application.status), + Optional.ofNullable(teammates.get(tuple.get(application.id))) + .orElse(List.of()) + )) + .toList(); + } + + @Override + public Slice findApplicantsBy( + final CursorPageable pageable, + final ProjectId projectId + ) { + final List content = fetchContent(pageable, projectId); + return paginate(pageable, content); + } +} \ No newline at end of file diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/FindTeammateAdapter.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/FindTeammateAdapter.java new file mode 100644 index 00000000..00e4b0c0 --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/FindTeammateAdapter.java @@ -0,0 +1,101 @@ +package es.princip.getp.persistence.adapter.project.apply; + +import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.JPAExpressions; +import com.querydsl.jpa.impl.JPAQuery; +import es.princip.getp.application.project.apply.dto.response.SearchTeammateResponse; +import es.princip.getp.application.project.apply.port.out.FindTeammatePort; +import es.princip.getp.application.support.Cursor; +import es.princip.getp.application.support.CursorPageable; +import es.princip.getp.domain.project.commission.model.ProjectId; +import es.princip.getp.persistence.adapter.member.QMemberJpaEntity; +import es.princip.getp.persistence.adapter.people.model.QPeopleJpaEntity; +import es.princip.getp.persistence.adapter.project.apply.model.QProjectApplicationJpaEntity; +import es.princip.getp.persistence.adapter.project.apply.model.QTeammateJpaEntity; +import es.princip.getp.persistence.support.QueryDslSupport; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.domain.Slice; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Repository; + +import java.util.List; + +import static es.princip.getp.persistence.adapter.people.PeoplePersistenceUtil.getOrderSpecifier; + +@Repository +@RequiredArgsConstructor +class FindTeammateAdapter extends QueryDslSupport implements FindTeammatePort { + + private static final QPeopleJpaEntity people = QPeopleJpaEntity.peopleJpaEntity; + private static final QMemberJpaEntity member = QMemberJpaEntity.memberJpaEntity; + private static final QProjectApplicationJpaEntity application = QProjectApplicationJpaEntity.projectApplicationJpaEntity; + private static final QTeammateJpaEntity teammate = QTeammateJpaEntity.teammateJpaEntity; + + private JPAQuery buildQuery( + final ProjectId projectId, + final JPAQuery select, + final String nickname + ) { + final Long pid = projectId.getValue(); + final JPAQuery query = select.from(people) + .join(member).on(people.memberId.eq(member.id)) + .where(people.profile.isNotNull() + .and(people.id.notIn(JPAExpressions.select(teammate.peopleId) + .from(teammate) + .where(teammate.application.projectId.eq(pid)) + )) + .and(people.id.notIn(JPAExpressions.select(application.applicantId) + .from(application) + .where(application.projectId.eq(pid) + )) + )); + if (!StringUtils.isAllBlank(nickname)) { + query.where(member.nickname.startsWith(nickname)); + } + return query; + } + + private BooleanExpression getCursorBooleanExpression(final CursorPageable pageable) { + if (!pageable.hasCursor()) { + return null; + } + final Long idCursor = pageable.getCursor().getId(); + return people.id.lt(idCursor); + } + + private List fetchContent( + final ProjectId projectId, + final CursorPageable pageable, + final String nickname + ) { + final JPAQuery selectFrom = queryFactory.select( + Projections.constructor( + SearchTeammateResponse.class, + people.id, + member.nickname, + member.profileImage + ) + ); + final Sort.Order order = pageable.getSort() + .stream() + .findFirst() + .orElse(null); + return buildQuery(projectId, selectFrom, nickname) + .where(getCursorBooleanExpression(pageable)) + .orderBy(getOrderSpecifier(order)) + .limit(pageable.getPageSize() + 1) + .fetch(); + } + + @Override + public Slice findBy( + final ProjectId projectId, + final CursorPageable pageable, + final String nickname + ) { + final List content = fetchContent(projectId, pageable, nickname); + return paginate(pageable, content); + } +} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationJpaRepository.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationJpaRepository.java similarity index 79% rename from src/main/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationJpaRepository.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationJpaRepository.java index 7a6799c4..bdc1668a 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationJpaRepository.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationJpaRepository.java @@ -1,5 +1,6 @@ package es.princip.getp.persistence.adapter.project.apply; +import es.princip.getp.persistence.adapter.project.apply.model.ProjectApplicationJpaEntity; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationPersistenceAdapter.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationPersistenceAdapter.java new file mode 100644 index 00000000..60e14bd4 --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationPersistenceAdapter.java @@ -0,0 +1,67 @@ +package es.princip.getp.persistence.adapter.project.apply; + +import es.princip.getp.application.project.apply.exception.NotFoundProjectApplicationException; +import es.princip.getp.application.project.apply.port.out.CheckProjectApplicationPort; +import es.princip.getp.application.project.apply.port.out.LoadProjectApplicantPort; +import es.princip.getp.application.project.apply.port.out.SaveProjectApplicationPort; +import es.princip.getp.application.project.apply.port.out.UpdateProjectApplicantPort; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.domain.project.apply.model.ProjectApplication; +import es.princip.getp.domain.project.apply.model.ProjectApplicationId; +import es.princip.getp.domain.project.commission.model.ProjectId; +import es.princip.getp.persistence.adapter.project.apply.model.ProjectApplicationJpaEntity; +import es.princip.getp.persistence.adapter.project.apply.model.QTeammateJpaEntity; +import es.princip.getp.persistence.support.QueryDslSupport; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +class ProjectApplicationPersistenceAdapter extends QueryDslSupport implements + SaveProjectApplicationPort, + LoadProjectApplicantPort, + UpdateProjectApplicantPort, + CheckProjectApplicationPort { + + private static final QTeammateJpaEntity teammate = QTeammateJpaEntity.teammateJpaEntity; + + private final ProjectApplicationPersistenceMapper mapper; + private final ProjectApplicationJpaRepository repository; + + @Override + public ProjectApplicationId save(final ProjectApplication application) { + final ProjectApplicationJpaEntity jpaEntity = mapper.mapToJpa(application); + return new ProjectApplicationId(repository.save(jpaEntity).getId()); + } + + @Override + public boolean existsBy(final PeopleId applicantId, final ProjectId projectId) { + return repository.existsByApplicantIdAndProjectId(applicantId.getValue(), projectId.getValue()) || + teammateExistsBy(applicantId, projectId); + } + + private boolean teammateExistsBy(final PeopleId applicantId, final ProjectId projectId) { + return queryFactory.selectOne().from(teammate) + .where(teammate.application.projectId.eq(projectId.getValue()).and( + teammate.peopleId.eq(applicantId.getValue()) + )) + .fetchFirst() != null; + } + + @Override + public ProjectApplication loadBy(final ProjectApplicationId applicationId) { + final ProjectApplicationJpaEntity jpaEntity = repository.findById(applicationId.getValue()) + .orElseThrow(NotFoundProjectApplicationException::new); + return mapper.mapToDomain(jpaEntity); + } + + @Override + public void update(final ProjectApplication application) { + final Long applicationId = application.getId().getValue(); + if (!repository.existsById(applicationId)) { + throw new NotFoundProjectApplicationException(); + } + final ProjectApplicationJpaEntity jpaEntity = mapper.mapToJpa(application); + repository.save(jpaEntity); + } +} diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationPersistenceMapper.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationPersistenceMapper.java new file mode 100644 index 00000000..c46d8b78 --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationPersistenceMapper.java @@ -0,0 +1,74 @@ +package es.princip.getp.persistence.adapter.project.apply; + +import es.princip.getp.domain.project.apply.model.IndividualProjectApplication; +import es.princip.getp.domain.project.apply.model.ProjectApplication; +import es.princip.getp.domain.project.apply.model.TeamProjectApplication; +import es.princip.getp.domain.project.apply.model.Teammate; +import es.princip.getp.persistence.adapter.common.mapper.AttachmentFilePersistenceMapper; +import es.princip.getp.persistence.adapter.people.mapper.PeoplePersistenceMapper; +import es.princip.getp.persistence.adapter.project.apply.model.IndividualProjectApplicationJpaEntity; +import es.princip.getp.persistence.adapter.project.apply.model.ProjectApplicationJpaEntity; +import es.princip.getp.persistence.adapter.project.apply.model.TeamProjectApplicationJpaEntity; +import es.princip.getp.persistence.adapter.project.apply.model.TeammateJpaEntity; +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper( + componentModel = "spring", + uses = { + PeoplePersistenceMapper.class, + AttachmentFilePersistenceMapper.class + } +) +abstract class ProjectApplicationPersistenceMapper { + + ProjectApplication mapToDomain(ProjectApplicationJpaEntity application) { + if (application instanceof IndividualProjectApplicationJpaEntity individual) { + return mapToDomain(individual); + } else if (application instanceof TeamProjectApplicationJpaEntity team) { + return mapToDomain(team); + } + throw new IllegalArgumentException("올바르지 않은 프로젝트 지원 유형: " + application.getClass()); + } + + @Mapping(source = "id", target = "id.value") + @Mapping(source = "applicantId", target = "applicantId.value") + @Mapping(source = "projectId", target = "projectId.value") + protected abstract TeamProjectApplication mapToDomain(TeamProjectApplicationJpaEntity application); + + @Mapping(source = "id", target = "id.value") + @Mapping(source = "applicantId", target = "applicantId.value") + @Mapping(source = "projectId", target = "projectId.value") + protected abstract IndividualProjectApplication mapToDomain(IndividualProjectApplicationJpaEntity application); + + @Mapping(source = "id", target = "id.value") + @Mapping(source = "peopleId", target = "peopleId.value") + protected abstract Teammate mapToDomain(TeammateJpaEntity application); + + ProjectApplicationJpaEntity mapToJpa(ProjectApplication application) { + if (application instanceof IndividualProjectApplication individual) { + return mapToJpa(individual); + } else if (application instanceof TeamProjectApplication team) { + final TeamProjectApplicationJpaEntity entity = mapToJpa(team); + team.getTeammates().stream() + .map(teammate -> mapToJpa(teammate, entity)) + .forEach(entity::addTeammate); + return entity; + } + throw new IllegalArgumentException("올바르지 않은 프로젝트 지원 유형: " + application.getClass()); + } + + @InheritInverseConfiguration + protected abstract TeamProjectApplicationJpaEntity mapToJpa(TeamProjectApplication domain); + + @InheritInverseConfiguration + protected abstract IndividualProjectApplicationJpaEntity mapToJpa(IndividualProjectApplication domain); + + @Mapping(target = "id", source = "teammate.id.value") + @Mapping(target = "peopleId", source = "teammate.peopleId.value") + @Mapping(target = "status", source = "teammate.status") + @Mapping(target = "createdAt", source = "teammate.createdAt") + @Mapping(target = "updatedAt", source = "teammate.updatedAt") + protected abstract TeammateJpaEntity mapToJpa(Teammate teammate, TeamProjectApplicationJpaEntity application); +} diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/SerializeApplicantCursorAdapter.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/SerializeApplicantCursorAdapter.java new file mode 100644 index 00000000..7814abe7 --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/SerializeApplicantCursorAdapter.java @@ -0,0 +1,45 @@ +package es.princip.getp.persistence.adapter.project.apply; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import es.princip.getp.application.project.apply.dto.response.ProjectApplicantResponse; +import es.princip.getp.application.project.apply.port.out.SerializeApplicantCursorPort; +import es.princip.getp.application.support.Cursor; +import es.princip.getp.persistence.support.SerializeCursorException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Slice; +import org.springframework.stereotype.Component; + +import java.util.Base64; + +@Slf4j +@Component +@RequiredArgsConstructor +class SerializeApplicantCursorAdapter implements SerializeApplicantCursorPort { + + private final ObjectMapper objectMapper; + + @Override + public String serializeCursor(final Slice slice) { + if (!slice.hasNext()) { + return null; + } + final Long id = slice.getContent() + .stream() + .reduce((first, second) -> second) + .map(ProjectApplicantResponse::peopleId) + .orElse(null); + if (id == null) { + return null; + } + final Cursor cursor = new Cursor(id); + try { + final String cursorJson = objectMapper.writeValueAsString(cursor); + return Base64.getEncoder().encodeToString(cursorJson.getBytes()); + } catch (final JsonProcessingException exception) { + log.debug("Failed to serialize cursor", exception); + throw new SerializeCursorException(); + } + } +} \ No newline at end of file diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/SerializeTeammateCursorAdapter.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/SerializeTeammateCursorAdapter.java new file mode 100644 index 00000000..05c5cb40 --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/SerializeTeammateCursorAdapter.java @@ -0,0 +1,45 @@ +package es.princip.getp.persistence.adapter.project.apply; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import es.princip.getp.application.project.apply.dto.response.SearchTeammateResponse; +import es.princip.getp.application.project.apply.port.out.SerializeTeammateCursorPort; +import es.princip.getp.application.support.Cursor; +import es.princip.getp.persistence.support.SerializeCursorException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Slice; +import org.springframework.stereotype.Component; + +import java.util.Base64; + +@Slf4j +@Component +@RequiredArgsConstructor +class SerializeTeammateCursorAdapter implements SerializeTeammateCursorPort { + + private final ObjectMapper objectMapper; + + @Override + public String serializeCursor(final Slice slice) { + if (!slice.hasNext()) { + return null; + } + final Long id = slice.getContent() + .stream() + .reduce((first, second) -> second) + .map(SearchTeammateResponse::peopleId) + .orElse(null); + if (id == null) { + return null; + } + final Cursor cursor = new Cursor(id); + try { + final String cursorJson = objectMapper.writeValueAsString(cursor); + return Base64.getEncoder().encodeToString(cursorJson.getBytes()); + } catch (final JsonProcessingException exception) { + log.debug("Failed to serialize cursor", exception); + throw new SerializeCursorException(); + } + } +} \ No newline at end of file diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/model/IndividualProjectApplicationJpaEntity.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/model/IndividualProjectApplicationJpaEntity.java new file mode 100644 index 00000000..fe2430f4 --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/model/IndividualProjectApplicationJpaEntity.java @@ -0,0 +1,46 @@ +package es.princip.getp.persistence.adapter.project.apply.model; + +import es.princip.getp.domain.project.apply.model.IndividualProjectApplication; +import es.princip.getp.domain.project.apply.model.ProjectApplicationStatus; +import es.princip.getp.persistence.adapter.common.DurationJpaVO; +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; + +@Entity +@Table(name = "individual_project_application") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@DiscriminatorValue(IndividualProjectApplication.TYPE) +public class IndividualProjectApplicationJpaEntity extends ProjectApplicationJpaEntity { + + @Builder + public IndividualProjectApplicationJpaEntity( + final Long id, + final Long applicantId, + final Long projectId, + final DurationJpaVO expectedDuration, + final ProjectApplicationStatus status, + final String description, + final List attachmentFiles, + final LocalDateTime createdAt, + final LocalDateTime updatedAt + ) { + super( + id, + applicantId, + projectId, + expectedDuration, + status, + description, + attachmentFiles, + createdAt, + updatedAt + ); + } +} diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/model/ProjectApplicationJpaEntity.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/model/ProjectApplicationJpaEntity.java new file mode 100644 index 00000000..4b10e74e --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/model/ProjectApplicationJpaEntity.java @@ -0,0 +1,81 @@ +package es.princip.getp.persistence.adapter.project.apply.model; + +import es.princip.getp.domain.project.apply.model.ProjectApplicationStatus; +import es.princip.getp.persistence.adapter.BaseTimeJpaEntity; +import es.princip.getp.persistence.adapter.common.DurationJpaVO; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Getter +@Entity +@Table(name = "project_application") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Inheritance(strategy = InheritanceType.JOINED) +@DiscriminatorColumn(discriminatorType = DiscriminatorType.STRING) +public abstract class ProjectApplicationJpaEntity extends BaseTimeJpaEntity { + + @Id + @Column(name = "project_application_id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "people_id") + private Long applicantId; + + @Column(name = "project_id") + private Long projectId; + + @Embedded + @AttributeOverrides( + { + @AttributeOverride(name = "startDate", column = @Column(name = "expected_start_date")), + @AttributeOverride(name = "endDate", column = @Column(name = "expected_end_date")) + } + ) + private DurationJpaVO expectedDuration; + + @Column(name = "status") + @Enumerated(EnumType.STRING) + private ProjectApplicationStatus status; + + @Column(name = "description") + private String description; + + @ElementCollection + @CollectionTable( + name = "project_application_attachment_file", + joinColumns = @JoinColumn( + name = "project_application_id", + foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT) + ) + ) + private List attachmentFiles = new ArrayList<>(); + + protected ProjectApplicationJpaEntity( + final Long id, + final Long applicantId, + final Long projectId, + final DurationJpaVO expectedDuration, + final ProjectApplicationStatus status, + final String description, + final List attachmentFiles, + final LocalDateTime createdAt, + final LocalDateTime updatedAt + ) { + this.id = id; + this.applicantId = applicantId; + this.projectId = projectId; + this.expectedDuration = expectedDuration; + this.status = status; + this.description = description; + this.attachmentFiles = attachmentFiles; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + } +} diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/model/TeamProjectApplicationJpaEntity.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/model/TeamProjectApplicationJpaEntity.java new file mode 100644 index 00000000..23a656fc --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/model/TeamProjectApplicationJpaEntity.java @@ -0,0 +1,59 @@ +package es.princip.getp.persistence.adapter.project.apply.model; + +import es.princip.getp.domain.project.apply.model.ProjectApplicationStatus; +import es.princip.getp.domain.project.apply.model.TeamProjectApplication; +import es.princip.getp.persistence.adapter.common.DurationJpaVO; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Getter +@Entity +@Table(name = "team_project_application") +@DiscriminatorValue(TeamProjectApplication.TYPE) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class TeamProjectApplicationJpaEntity extends ProjectApplicationJpaEntity { + + @OneToMany( + mappedBy = "application", + cascade = CascadeType.ALL, + orphanRemoval = true, + fetch = FetchType.EAGER + ) + private List teammates = new ArrayList<>(); + + @Builder + public TeamProjectApplicationJpaEntity( + final Long id, + final Long applicantId, + final Long projectId, + final DurationJpaVO expectedDuration, + final ProjectApplicationStatus status, + final String description, + final List attachmentFiles, + final LocalDateTime createdAt, + final LocalDateTime updatedAt + ) { + super( + id, + applicantId, + projectId, + expectedDuration, + status, + description, + attachmentFiles, + createdAt, + updatedAt + ); + } + + public void addTeammate(TeammateJpaEntity teammate) { + teammates.add(teammate); + } +} diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/model/TeammateJpaEntity.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/model/TeammateJpaEntity.java new file mode 100644 index 00000000..a93d4867 --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/apply/model/TeammateJpaEntity.java @@ -0,0 +1,52 @@ +package es.princip.getp.persistence.adapter.project.apply.model; + +import es.princip.getp.domain.project.apply.model.TeammateStatus; +import es.princip.getp.persistence.adapter.BaseTimeJpaEntity; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +import static jakarta.persistence.ConstraintMode.NO_CONSTRAINT; + +@Getter +@Entity +@Table(name = "team_project_application_teammate") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class TeammateJpaEntity extends BaseTimeJpaEntity { + + @Id + @Column(name = "teammate_id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "people_id") + private Long peopleId; + + @Enumerated(EnumType.STRING) + @Column(name = "status") + private TeammateStatus status; + + @ManyToOne + @JoinColumn(name = "project_application_id", foreignKey = @ForeignKey(NO_CONSTRAINT)) + private TeamProjectApplicationJpaEntity application; + + @Builder + public TeammateJpaEntity( + final Long id, + final Long peopleId, + final TeammateStatus status, + final TeamProjectApplicationJpaEntity application, + final LocalDateTime createdAt, + final LocalDateTime updatedAt + ) { + super(createdAt, updatedAt); + this.id = id; + this.peopleId = peopleId; + this.status = status; + this.application = application; + } +} diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/commission/FindProjectAdapter.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/commission/FindProjectAdapter.java new file mode 100644 index 00000000..3b59baeb --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/commission/FindProjectAdapter.java @@ -0,0 +1,172 @@ +package es.princip.getp.persistence.adapter.project.commission; + +import com.querydsl.core.types.Order; +import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQuery; +import es.princip.getp.application.project.commission.dto.response.ProjectCardResponse; +import es.princip.getp.application.project.commission.dto.response.ProjectClientResponse; +import es.princip.getp.application.project.commission.dto.response.ProjectDetailResponse; +import es.princip.getp.application.client.port.out.ClientQuery; +import es.princip.getp.application.like.project.port.out.CheckProjectLikePort; +import es.princip.getp.application.like.project.port.out.CountProjectLikePort; +import es.princip.getp.application.project.apply.port.out.CountProjectApplicationPort; +import es.princip.getp.application.project.commission.dto.command.ProjectSearchFilter; +import es.princip.getp.application.project.commission.dto.command.ProjectSearchOrder; +import es.princip.getp.application.project.commission.exception.NotFoundProjectException; +import es.princip.getp.application.project.commission.port.out.FindProjectPort; +import es.princip.getp.domain.client.model.ClientId; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.project.commission.model.Project; +import es.princip.getp.domain.project.commission.model.ProjectId; +import es.princip.getp.domain.project.commission.model.ProjectStatus; +import es.princip.getp.persistence.adapter.client.QClientJpaEntity; +import es.princip.getp.persistence.adapter.like.project.QProjectLikeJpaEntity; +import es.princip.getp.persistence.adapter.member.QMemberJpaEntity; +import es.princip.getp.persistence.adapter.people.model.QPeopleJpaEntity; +import es.princip.getp.persistence.adapter.project.ProjectPersistenceMapper; +import es.princip.getp.persistence.adapter.project.ProjectQueryMapper; +import es.princip.getp.persistence.adapter.project.apply.model.QProjectApplicationJpaEntity; +import es.princip.getp.persistence.support.QueryDslSupport; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static es.princip.getp.persistence.adapter.project.ProjectPersistenceUtil.toProjectIds; + +@Repository +@RequiredArgsConstructor +class FindProjectAdapter extends QueryDslSupport implements FindProjectPort { + + private static final QProjectApplicationJpaEntity application = QProjectApplicationJpaEntity.projectApplicationJpaEntity; + private static final QProjectLikeJpaEntity like = QProjectLikeJpaEntity.projectLikeJpaEntity; + private static final QClientJpaEntity client = QClientJpaEntity.clientJpaEntity; + private static final QMemberJpaEntity member = QMemberJpaEntity.memberJpaEntity; + private static final QPeopleJpaEntity people = QPeopleJpaEntity.peopleJpaEntity; + private static final QProjectJpaEntity project = QProjectJpaEntity.projectJpaEntity; + + private final ClientQuery clientQuery; + private final CountProjectLikePort countProjectLikePort; + private final CountProjectApplicationPort countProjectApplicationPort; + private final CheckProjectLikePort checkProjectLikePort; + private final ProjectPersistenceMapper persistenceMapper; + private final ProjectQueryMapper queryMapper; + + private BooleanExpression memberIdEq(final MemberId memberId) { + return Optional.ofNullable(memberId) + .map(MemberId::getValue) + .map(member.id::eq) + .orElse(null); + } + + private JPAQuery buildQuery( + final JPAQuery selectFrom, + final ProjectSearchFilter filter, + final MemberId memberId + ) { + if (filter.isApplied()) { + selectFrom.join(application).on(project.id.eq(application.projectId)) + .join(people).on(application.applicantId.eq(people.id)) + .join(member).on(people.memberId.eq(member.id)) + .where(memberIdEq(memberId)); + } + if (filter.isLiked()) { + selectFrom.join(like).on(project.id.eq(like.projectId)) + .join(member).on(like.memberId.eq(member.id)) + .where(memberIdEq(memberId)); + } + if (filter.isCommissioned()) { + selectFrom.join(client).on(project.clientId.eq(client.id)) + .join(member).on(client.memberId.eq(member.id)) + .where(memberIdEq(memberId)); + } + if (filter.isClosed()) { + selectFrom.where(project.status.eq(ProjectStatus.CANCELLED)); + } + return selectFrom; + } + + private List assemble( + final List projects, + final Map projectApplicationCounts + ) { + return projects.stream() + .map(project -> ProjectCardResponse.of( + project, + projectApplicationCounts.getOrDefault(project.getId(), 0L) + )) + .toList(); + } + + private static OrderSpecifier getOrderSpecifier(final Sort.Order order, final ProjectSearchOrder searchOrder) { + final Order converted = convertTo(order); + return switch (searchOrder) { + case CREATED_AT -> new OrderSpecifier<>(converted, project.createdAt); + case PAYMENT -> new OrderSpecifier<>(converted, project.payment); + case APPLICATION_DURATION -> new OrderSpecifier<>(converted, project.applicationDuration.endDate); + case PROJECT_ID -> new OrderSpecifier<>(converted, project.id); + }; + } + + private static OrderSpecifier[] getOrderSpecifiers(final Sort sort) { + return sort.stream() + .map(order -> getOrderSpecifier(order, ProjectSearchOrder.get(order.getProperty()))) + .toArray(OrderSpecifier[]::new); + } + + @Override + public Page findBy( + final Pageable pageable, + final ProjectSearchFilter filter, + final MemberId memberId + ) { + final JPAQuery contentQuery = buildQuery( + queryFactory.selectFrom(project), + filter, + memberId + ); + contentQuery.orderBy(getOrderSpecifiers(pageable.getSort())); + final List projects = contentQuery + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch() + .stream() + .map(persistenceMapper::mapToDomain) + .toList(); + final ProjectId[] projectIds = toProjectIds(projects); + final Map projectApplicationCounts = countProjectApplicationPort.countBy(projectIds); + final List content = assemble(projects, projectApplicationCounts); + return paginate( + pageable, + content, + countQuery -> buildQuery(countQuery.select(project.count()).from(project), filter, memberId) + ); + } + + private ProjectJpaEntity fetchProject(final ProjectId projectId) { + return Optional.ofNullable( + queryFactory.selectFrom(project) + .where(project.id.eq(projectId.getValue())) + .fetchOne() + ) + .orElseThrow(NotFoundProjectException::new); + } + + @Override + public ProjectDetailResponse findBy(final Member member, final ProjectId projectId) { + final ProjectJpaEntity result = fetchProject(projectId); + final Long applicantsCount = countProjectApplicationPort.countBy(projectId); + final Long likesCount = countProjectLikePort.countBy(projectId); + final ProjectClientResponse projectClientResponse = clientQuery.findProjectClientBy(new ClientId(result.getClientId())); + final Boolean liked = checkProjectLikePort.existsBy(member, projectId); + + return queryMapper.mapToDetailResponse(result, applicantsCount, likesCount, liked, projectClientResponse); + } +} diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/commission/ProjectJpaEntity.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/commission/ProjectJpaEntity.java similarity index 96% rename from src/main/java/es/princip/getp/persistence/adapter/project/commission/ProjectJpaEntity.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/commission/ProjectJpaEntity.java index 03103e53..348bebc7 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/project/commission/ProjectJpaEntity.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/commission/ProjectJpaEntity.java @@ -22,7 +22,7 @@ public class ProjectJpaEntity extends BaseTimeJpaEntity { @Id @Column(name = "project_id") @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long projectId; + private Long id; @Column(name = "title") private String title; @@ -30,6 +30,9 @@ public class ProjectJpaEntity extends BaseTimeJpaEntity { @Column(name = "payment") private Long payment; + @Column(name = "recruitment_count") + private Long recruitmentCount; + @Embedded @AttributeOverrides( { diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/commission/ProjectJpaRepository.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/commission/ProjectJpaRepository.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/project/commission/ProjectJpaRepository.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/commission/ProjectJpaRepository.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/commission/ProjectPersistenceAdapter.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/commission/ProjectPersistenceAdapter.java similarity index 67% rename from src/main/java/es/princip/getp/persistence/adapter/project/commission/ProjectPersistenceAdapter.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/commission/ProjectPersistenceAdapter.java index 5a7dd2ba..68256212 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/project/commission/ProjectPersistenceAdapter.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/commission/ProjectPersistenceAdapter.java @@ -1,39 +1,38 @@ package es.princip.getp.persistence.adapter.project.commission; +import es.princip.getp.application.project.commission.exception.NotFoundProjectException; import es.princip.getp.application.project.commission.port.out.LoadProjectPort; import es.princip.getp.application.project.commission.port.out.SaveProjectPort; import es.princip.getp.application.project.commission.port.out.UpdateProjectPort; import es.princip.getp.domain.project.commission.model.Project; +import es.princip.getp.domain.project.commission.model.ProjectId; import es.princip.getp.persistence.adapter.project.ProjectPersistenceMapper; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; @Repository @RequiredArgsConstructor -class ProjectPersistenceAdapter implements - SaveProjectPort, - LoadProjectPort, - UpdateProjectPort { +class ProjectPersistenceAdapter implements SaveProjectPort, LoadProjectPort, UpdateProjectPort { private final ProjectPersistenceMapper mapper; private final ProjectJpaRepository repository; @Override - public Long save(final Project project) { + public ProjectId save(final Project project) { final ProjectJpaEntity jpaEntity = mapper.mapToJpa(project); - return repository.save(jpaEntity).getProjectId(); + return new ProjectId(repository.save(jpaEntity).getId()); } @Override - public Project loadBy(final Long meetingId) { - final ProjectJpaEntity jpaEntity = repository.findById(meetingId) + public Project loadBy(final ProjectId projectId) { + final ProjectJpaEntity jpaEntity = repository.findById(projectId.getValue()) .orElseThrow(NotFoundProjectException::new); return mapper.mapToDomain(jpaEntity); } @Override public void update(final Project project) { - final Long projectId = project.getProjectId(); + final Long projectId = project.getId().getValue(); if (!repository.existsById(projectId)) { throw new NotFoundProjectException(); } diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingJpaRepository.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingJpaRepository.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingJpaRepository.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingJpaRepository.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingPersistenceAdapter.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingPersistenceAdapter.java similarity index 76% rename from src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingPersistenceAdapter.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingPersistenceAdapter.java index 194025ee..e0c60cf3 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingPersistenceAdapter.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingPersistenceAdapter.java @@ -1,6 +1,6 @@ package es.princip.getp.persistence.adapter.project.meeting; -import es.princip.getp.application.project.meeting.port.out.CheckProjectMeetingPort; +import es.princip.getp.application.project.meeting.exception.NotFoundProjectMeetingException; import es.princip.getp.application.project.meeting.port.out.LoadProjectMeetingPort; import es.princip.getp.application.project.meeting.port.out.SaveProjectMeetingPort; import es.princip.getp.application.project.meeting.port.out.UpdateProjectMeetingPort; @@ -12,13 +12,12 @@ @Repository @RequiredArgsConstructor class ProjectMeetingPersistenceAdapter implements - SaveProjectMeetingPort, - LoadProjectMeetingPort, - UpdateProjectMeetingPort, - CheckProjectMeetingPort { + SaveProjectMeetingPort, + LoadProjectMeetingPort, + UpdateProjectMeetingPort { private final ProjectMeetingPersistenceMapper mapper; - private final ProjectMeetingRepository repository; + private final ProjectMeetingJpaRepository repository; @Override public Long save(final ProjectMeeting projectMeeting) { @@ -42,9 +41,4 @@ public void update(final ProjectMeeting projectMeeting) { final ProjectMeetingJpaEntity jpaEntity = mapper.mapToJpa(projectMeeting); repository.save(jpaEntity); } - - @Override - public boolean existsApplicantByProjectIdAndMemberId(final Long memberId, final Long projectId) { - return repository.existsApplicantByProjectIdAndMemberId(projectId, memberId); - } } diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingPersistenceMapper.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingPersistenceMapper.java similarity index 73% rename from src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingPersistenceMapper.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingPersistenceMapper.java index 48976c5c..2d73723d 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingPersistenceMapper.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingPersistenceMapper.java @@ -9,10 +9,12 @@ @Mapper(componentModel = "spring", uses = {PhoneNumberPersistenceMapper.class}) public interface ProjectMeetingPersistenceMapper { - @Mapping(source = "peopleId", target = "applicantId") + @Mapping(source = "projectId", target = "projectId.value") + @Mapping(source = "peopleId", target = "applicantId.value") ProjectMeeting mapToDomain(ProjectMeetingJpaEntity projectMeetingJpaEntity); - @Mapping(target = "peopleId", source = "applicantId") + @Mapping(target = "projectId", source = "projectId.value") + @Mapping(target = "peopleId", source = "applicantId.value") @Mapping(target = "phoneNumber", source = "phoneNumber.value") ProjectMeetingJpaEntity mapToJpa(ProjectMeeting projectMeeting); } diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/meeting/model/MeetingScheduleJpaVO.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/meeting/model/MeetingScheduleJpaVO.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/project/meeting/model/MeetingScheduleJpaVO.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/meeting/model/MeetingScheduleJpaVO.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/meeting/model/ProjectMeetingJpaEntity.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/meeting/model/ProjectMeetingJpaEntity.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/project/meeting/model/ProjectMeetingJpaEntity.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/project/meeting/model/ProjectMeetingJpaEntity.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermJpaEntity.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermJpaEntity.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermJpaEntity.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermJpaEntity.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermJpaRepository.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermJpaRepository.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermJpaRepository.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermJpaRepository.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermPersistenceAdapter.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermPersistenceAdapter.java similarity index 95% rename from src/main/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermPersistenceAdapter.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermPersistenceAdapter.java index 028482df..2c8617cb 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermPersistenceAdapter.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermPersistenceAdapter.java @@ -1,5 +1,6 @@ package es.princip.getp.persistence.adapter.serviceTerm; +import es.princip.getp.application.serviceTerm.exception.NotFoundServiceTermException; import es.princip.getp.application.serviceTerm.port.out.CheckServiceTermPort; import es.princip.getp.application.serviceTerm.port.out.LoadServiceTermPort; import es.princip.getp.application.serviceTerm.port.out.SaveServiceTermPort; diff --git a/src/main/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermPersistenceMapper.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermPersistenceMapper.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermPersistenceMapper.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermPersistenceMapper.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/storage/FileLogJpaEntity.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/storage/FileLogJpaEntity.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/storage/FileLogJpaEntity.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/storage/FileLogJpaEntity.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/storage/FileLogJpaRepository.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/storage/FileLogJpaRepository.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/storage/FileLogJpaRepository.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/storage/FileLogJpaRepository.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/storage/FileLogPersistenceAdapter.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/storage/FileLogPersistenceAdapter.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/adapter/storage/FileLogPersistenceAdapter.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/storage/FileLogPersistenceAdapter.java diff --git a/src/main/java/es/princip/getp/persistence/adapter/storage/FileLogPersistenceMapper.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/storage/FileLogPersistenceMapper.java similarity index 67% rename from src/main/java/es/princip/getp/persistence/adapter/storage/FileLogPersistenceMapper.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/storage/FileLogPersistenceMapper.java index 20ff4d67..67f5ba43 100644 --- a/src/main/java/es/princip/getp/persistence/adapter/storage/FileLogPersistenceMapper.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/adapter/storage/FileLogPersistenceMapper.java @@ -2,11 +2,14 @@ import es.princip.getp.application.storage.FileLog; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; @Mapper(componentModel = "spring") interface FileLogPersistenceMapper { + @Mapping(source = "memberId", target = "memberId.value") FileLog mapToDomain(FileLogJpaEntity fileLogJpaEntity); + @Mapping(target = "memberId", source = "memberId.value") FileLogJpaEntity mapToJpa(FileLog fileLog); } diff --git a/src/main/java/es/princip/getp/persistence/config/JpaAuditingConfig.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/config/JpaAuditingConfig.java similarity index 100% rename from src/main/java/es/princip/getp/persistence/config/JpaAuditingConfig.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/config/JpaAuditingConfig.java diff --git a/src/main/java/es/princip/getp/persistence/support/QueryDslSupport.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/support/QueryDslSupport.java similarity index 58% rename from src/main/java/es/princip/getp/persistence/support/QueryDslSupport.java rename to get-p-persistence/src/main/java/es/princip/getp/persistence/support/QueryDslSupport.java index fbc778d2..f570579c 100644 --- a/src/main/java/es/princip/getp/persistence/support/QueryDslSupport.java +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/support/QueryDslSupport.java @@ -1,20 +1,26 @@ package es.princip.getp.persistence.support; +import com.querydsl.core.types.Order; import com.querydsl.jpa.JPQLTemplates; import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; +import es.princip.getp.application.support.Cursor; +import es.princip.getp.application.support.CursorPageable; import jakarta.annotation.PostConstruct; import jakarta.persistence.EntityManager; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.*; import org.springframework.data.support.PageableExecutionUtils; import org.springframework.stereotype.Repository; import org.springframework.util.Assert; +import java.util.ArrayList; import java.util.List; import java.util.function.Function; +import static com.querydsl.core.types.Order.ASC; +import static com.querydsl.core.types.Order.DESC; + /** * QueryDsl 5.x 버전에 맞춘 QueryDsl 지원 라이브러리 * @@ -41,12 +47,34 @@ public void validate() { Assert.notNull(queryFactory, "QueryFactory must not be null!"); } - protected Page applyPagination( + protected Page paginate( final Pageable pageable, final List content, final Function> countQuery ) { - JPAQuery countResult = countQuery.apply(queryFactory); + final JPAQuery countResult = countQuery.apply(queryFactory); return PageableExecutionUtils.getPage(content, pageable, countResult::fetchOne); } + + protected boolean removeIfContentHasNext(final List content, final int size) { + if (content.size() > size) { + content.remove(size); + return true; + } + return false; + } + + protected Slice paginate( + final CursorPageable pageable, + final List content + ) { + final List mutable = new ArrayList<>(content); + final int size = pageable.getPageSize(); + boolean hasNext = removeIfContentHasNext(mutable, size); + return new SliceImpl<>(mutable, PageRequest.ofSize(size), hasNext); + } + + protected static Order convertTo(final Sort.Order order) { + return order == null ? null : order.isAscending() ? ASC : DESC; + } } \ No newline at end of file diff --git a/get-p-persistence/src/main/java/es/princip/getp/persistence/support/SerializeCursorException.java b/get-p-persistence/src/main/java/es/princip/getp/persistence/support/SerializeCursorException.java new file mode 100644 index 00000000..0fba537c --- /dev/null +++ b/get-p-persistence/src/main/java/es/princip/getp/persistence/support/SerializeCursorException.java @@ -0,0 +1,14 @@ +package es.princip.getp.persistence.support; + +import es.princip.getp.domain.support.ErrorDescription; +import es.princip.getp.domain.support.ErrorDescriptionException; + +public class SerializeCursorException extends ErrorDescriptionException { + + private static final String code = "SERIALIZE_CURSOR_ERROR"; + private static final String message = "커서를 직렬화하는데 오류가 발생했습니다."; + + public SerializeCursorException() { + super(ErrorDescription.of(code, message)); + } +} \ No newline at end of file diff --git a/src/main/resources/db/data/e2e-test.sql b/get-p-persistence/src/main/resources/db/data/e2e-test.sql similarity index 86% rename from src/main/resources/db/data/e2e-test.sql rename to get-p-persistence/src/main/resources/db/data/e2e-test.sql index c6a8eda3..bdde51f0 100644 --- a/src/main/resources/db/data/e2e-test.sql +++ b/get-p-persistence/src/main/resources/db/data/e2e-test.sql @@ -118,37 +118,36 @@ INSERT INTO people ( email, introduction, major, - people_type, school, created_at, updated_at ) VALUES - (1, 1, '서울특별시 강남구', 'people1@gmail.com', '저는 웹 개발에 열정이 많은 개발자입니다.', '컴퓨터공학', 'INDIVIDUAL', '서울대학교', NOW(), NOW()), - (2, 2, '서울특별시 송파구', 'people2@gmail.com', 'AI와 머신러닝 프로젝트에 많은 경험이 있습니다.', '컴퓨터과학', 'INDIVIDUAL', '연세대학교', NOW(), NOW()), - (3, 3, '경기도 성남시', 'people3@gmail.com', '교육용 소프트웨어 개발에서 10년 이상의 경력을 가지고 있습니다.', '소프트웨어공학', 'INDIVIDUAL', '고려대학교', NOW(), NOW()), - (4, 4, '부산광역시 해운대구', 'people4@gmail.com', '대규모 웹 애플리케이션 개발에 참여한 경험이 있습니다.', '컴퓨터공학', 'INDIVIDUAL', '성균관대학교', NOW(), NOW()), - (5, 5, '대구광역시 중구', 'people5@gmail.com', '클라우드 컴퓨팅과 데이터베이스 관리에 관심이 많습니다.', '컴퓨터과학', 'INDIVIDUAL', 'KAIST', NOW(), NOW()), - (6, 6, '인천광역시 남동구', 'people6@gmail.com', '사용자 인터페이스 디자인과 프론트엔드 개발을 선호합니다.', '컴퓨터공학', 'INDIVIDUAL', '홍익대학교', NOW(), NOW()), - (7, 7, '서울특별시 관악구', 'people7@gmail.com', '의료 소프트웨어 개발 및 관리에 전문성을 가지고 있습니다.', '컴퓨터과학', 'INDIVIDUAL', '서울대학교 의과대학', NOW(), NOW()), - (8, 8, '경기도 용인시', 'people8@gmail.com', '법률 관련 소프트웨어 솔루션을 개발한 경험이 있습니다.', '컴퓨터공학', 'INDIVIDUAL', '서울대학교 법과대학', NOW(), NOW()), - (9, 9, '충청북도 청주시', 'people9@gmail.com', '임베디드 시스템과 IoT 개발에 많은 프로젝트를 수행했습니다.', '전자공학', 'INDIVIDUAL', 'POSTECH', NOW(), NOW()), - (10, 10, '서울특별시 마포구', 'people10@gmail.com', '데이터 분석 및 마케팅 자동화 도구 개발에 강점을 가지고 있습니다.', '컴퓨터공학', 'INDIVIDUAL', '이화여자대학교', NOW(), NOW()), - (11, 11, '서울특별시 종로구', 'people11@gmail.com', '게임 개발과 그래픽 디자인에 열정을 가지고 있습니다.', '게임공학', 'INDIVIDUAL', '한국예술종합학교', NOW(), NOW()), - (12, 12, '경기도 고양시', 'people12@gmail.com', '최신 IT 트렌드와 혁신적인 소프트웨어 솔루션에 관심이 많습니다.', '소프트웨어공학', 'INDIVIDUAL', '경희대학교', NOW(), NOW()), - (13, 13, '서울특별시 동작구', 'people13@gmail.com', '교육용 웹 애플리케이션 기획 및 개발에 전문성을 가지고 있습니다.', '교육공학', 'INDIVIDUAL', '한양대학교', NOW(), NOW()), - (14, 14, '경기도 의정부시', 'people14@gmail.com', '효율적인 소프트웨어 아키텍처 설계에 집중하고 있습니다.', '컴퓨터공학', 'INDIVIDUAL', '중앙대학교', NOW(), NOW()), - (15, 15, '전라북도 전주시', 'people15@gmail.com', '과학 데이터 분석 도구 개발에 강점을 가지고 있습니다.', '컴퓨터공학', 'INDIVIDUAL', '서울과학기술대학교', NOW(), NOW()), - (16, 16, '서울특별시 서초구', 'people16@gmail.com', '사용자 경험을 중시하는 웹 디자인과 개발을 합니다.', '산업디자인', 'INDIVIDUAL', '국민대학교', NOW(), NOW()), - (17, 17, '부산광역시 부산진구', 'people17@gmail.com', '의료 관련 데이터 분석 및 소프트웨어 개발에 전문성을 가지고 있습니다.', '컴퓨터공학', 'INDIVIDUAL', '부산대학교 의과대학', NOW(), NOW()), - (18, 18, '서울특별시 강서구', 'people18@gmail.com', '법률 관련 소프트웨어 개발 및 데이터 분석에 강점을 가지고 있습니다.', '법학', 'INDIVIDUAL', '연세대학교 법과대학', NOW(), NOW()), - (19, 19, '대전광역시 유성구', 'people19@gmail.com', '기계 공학과 소프트웨어 개발 문제 해결에 강점을 가지고 있습니다.', '기계공학', 'INDIVIDUAL', '한림대학교', NOW(), NOW()), - (20, 20, '서울특별시 노원구', 'people20@gmail.com', '브랜드 구축과 마케팅 자동화 도구 개발에 경험이 많습니다.', '컴퓨터과학', 'INDIVIDUAL', '상명대학교', NOW(), NOW()), - (21, 21, '서울특별시 성동구', 'people21@gmail.com', 'AI를 활용한 예술 작품 제작 및 관련 소프트웨어 개발에 참여하고 있습니다.', '컴퓨터공학', 'INDIVIDUAL', '서울예술대학교', NOW(), NOW()), - (22, 22, '경기도 안양시', 'people22@gmail.com', '네트워크 관리 및 소프트웨어 솔루션 개발에 능숙합니다.', '네트워크공학', 'INDIVIDUAL', '국방과학연구소', NOW(), NOW()), - (23, 23, '서울특별시 강북구', 'people23@gmail.com', '학생들을 위한 학습 관리 시스템 개발에 열정을 가지고 있습니다.', '학습심리학', 'INDIVIDUAL', '세종대학교', NOW(), NOW()), - (24, 24, '경기도 성남시', 'people24@gmail.com', '글로벌 소프트웨어 개발 프로젝트를 관리합니다.', '국제경영', 'INDIVIDUAL', '인하대학교', NOW(), NOW()), - (25, 25, '전라남도 광주시', 'people25@gmail.com', '혁신적인 과학 기술 기반 소프트웨어 솔루션을 개발하고 있습니다.', '화학', 'INDIVIDUAL', '충남대학교', NOW(), NOW()); + (1, 1, '서울특별시 강남구', 'people1@gmail.com', '저는 웹 개발에 열정이 많은 개발자입니다.', '컴퓨터공학', '서울대학교', NOW(), NOW()), + (2, 2, '서울특별시 송파구', 'people2@gmail.com', 'AI와 머신러닝 프로젝트에 많은 경험이 있습니다.', '컴퓨터과학', '연세대학교', NOW(), NOW()), + (3, 3, '경기도 성남시', 'people3@gmail.com', '교육용 소프트웨어 개발에서 10년 이상의 경력을 가지고 있습니다.', '소프트웨어공학', '고려대학교', NOW(), NOW()), + (4, 4, '부산광역시 해운대구', 'people4@gmail.com', '대규모 웹 애플리케이션 개발에 참여한 경험이 있습니다.', '컴퓨터공학', '성균관대학교', NOW(), NOW()), + (5, 5, '대구광역시 중구', 'people5@gmail.com', '클라우드 컴퓨팅과 데이터베이스 관리에 관심이 많습니다.', '컴퓨터과학', 'KAIST', NOW(), NOW()), + (6, 6, '인천광역시 남동구', 'people6@gmail.com', '사용자 인터페이스 디자인과 프론트엔드 개발을 선호합니다.', '컴퓨터공학', '홍익대학교', NOW(), NOW()), + (7, 7, '서울특별시 관악구', 'people7@gmail.com', '의료 소프트웨어 개발 및 관리에 전문성을 가지고 있습니다.', '컴퓨터과학', '서울대학교 의과대학', NOW(), NOW()), + (8, 8, '경기도 용인시', 'people8@gmail.com', '법률 관련 소프트웨어 솔루션을 개발한 경험이 있습니다.', '컴퓨터공학', '서울대학교 법과대학', NOW(), NOW()), + (9, 9, '충청북도 청주시', 'people9@gmail.com', '임베디드 시스템과 IoT 개발에 많은 프로젝트를 수행했습니다.', '전자공학', 'POSTECH', NOW(), NOW()), + (10, 10, '서울특별시 마포구', 'people10@gmail.com', '데이터 분석 및 마케팅 자동화 도구 개발에 강점을 가지고 있습니다.', '컴퓨터공학', '이화여자대학교', NOW(), NOW()), + (11, 11, '서울특별시 종로구', 'people11@gmail.com', '게임 개발과 그래픽 디자인에 열정을 가지고 있습니다.', '게임공학', '한국예술종합학교', NOW(), NOW()), + (12, 12, '경기도 고양시', 'people12@gmail.com', '최신 IT 트렌드와 혁신적인 소프트웨어 솔루션에 관심이 많습니다.', '소프트웨어공학', '경희대학교', NOW(), NOW()), + (13, 13, '서울특별시 동작구', 'people13@gmail.com', '교육용 웹 애플리케이션 기획 및 개발에 전문성을 가지고 있습니다.', '교육공학', '한양대학교', NOW(), NOW()), + (14, 14, '경기도 의정부시', 'people14@gmail.com', '효율적인 소프트웨어 아키텍처 설계에 집중하고 있습니다.', '컴퓨터공학', '중앙대학교', NOW(), NOW()), + (15, 15, '전라북도 전주시', 'people15@gmail.com', '과학 데이터 분석 도구 개발에 강점을 가지고 있습니다.', '컴퓨터공학', '서울과학기술대학교', NOW(), NOW()), + (16, 16, '서울특별시 서초구', 'people16@gmail.com', '사용자 경험을 중시하는 웹 디자인과 개발을 합니다.', '산업디자인', '국민대학교', NOW(), NOW()), + (17, 17, '부산광역시 부산진구', 'people17@gmail.com', '의료 관련 데이터 분석 및 소프트웨어 개발에 전문성을 가지고 있습니다.', '컴퓨터공학', '부산대학교 의과대학', NOW(), NOW()), + (18, 18, '서울특별시 강서구', 'people18@gmail.com', '법률 관련 소프트웨어 개발 및 데이터 분석에 강점을 가지고 있습니다.', '법학', '연세대학교 법과대학', NOW(), NOW()), + (19, 19, '대전광역시 유성구', 'people19@gmail.com', '기계 공학과 소프트웨어 개발 문제 해결에 강점을 가지고 있습니다.', '기계공학', '한림대학교', NOW(), NOW()), + (20, 20, '서울특별시 노원구', 'people20@gmail.com', '브랜드 구축과 마케팅 자동화 도구 개발에 경험이 많습니다.', '컴퓨터과학', '상명대학교', NOW(), NOW()), + (21, 21, '서울특별시 성동구', 'people21@gmail.com', 'AI를 활용한 예술 작품 제작 및 관련 소프트웨어 개발에 참여하고 있습니다.', '컴퓨터공학', '서울예술대학교', NOW(), NOW()), + (22, 22, '경기도 안양시', 'people22@gmail.com', '네트워크 관리 및 소프트웨어 솔루션 개발에 능숙합니다.', '네트워크공학', '국방과학연구소', NOW(), NOW()), + (23, 23, '서울특별시 강북구', 'people23@gmail.com', '학생들을 위한 학습 관리 시스템 개발에 열정을 가지고 있습니다.', '학습심리학', '세종대학교', NOW(), NOW()), + (24, 24, '경기도 성남시', 'people24@gmail.com', '글로벌 소프트웨어 개발 프로젝트를 관리합니다.', '국제경영', '인하대학교', NOW(), NOW()), + (25, 25, '전라남도 광주시', 'people25@gmail.com', '혁신적인 과학 기술 기반 소프트웨어 솔루션을 개발하고 있습니다.', '화학', '충남대학교', NOW(), NOW()); INSERT INTO people_profile_hashtag (people_id, hashtags) VALUES @@ -234,7 +233,7 @@ VALUES (24, '글로벌 소프트웨어 프로젝트 관리', 'https://storage.principes.xyz/24/files/portfolio24.pdf'), (25, '과학 기술 기반 혁신 솔루션 개발', 'https://storage.principes.xyz/25/files/portfolio25.pdf'); -INSERT INTO people_like (client_id, people_id, created_at) +INSERT INTO people_like (member_id, people_id, created_at) VALUES (1, 1, NOW()), (1, 2, NOW()), (1, 5, NOW()), (1, 8, NOW()), (2, 3, NOW()), (2, 6, NOW()), (2, 9, NOW()), (2, 12, NOW()), @@ -300,47 +299,48 @@ VALUES (25, 50, '김은호', '567-890-123458', '우리은행', 'client25@gmail.com', '부산광역시 기장군 456-78', '45678', NOW(), NOW()); INSERT INTO project ( - project_id, - application_start_date, - application_end_date, - estimated_start_date, - estimated_end_date, - payment, - category, - description, - meeting_type, - status, - title, - client_id, - created_at, - updated_at + project_id, + application_start_date, + application_end_date, + estimated_start_date, + estimated_end_date, + payment, + category, + description, + meeting_type, + status, + title, + client_id, + created_at, + updated_at, + recruitment_count ) VALUES - (1, '2024-09-01', '2024-09-15', '2024-09-20', '2024-11-01', 500000, 'BACKEND', '새 시스템을 위한 백엔드 개발', 'REMOTE', 'APPLYING', '시스템 백엔드 리뉴얼', 1, NOW(), NOW()), - (2, '2024-09-15', '2024-10-01', '2024-10-05', '2024-12-01', 600000, 'FRONTEND', '프론트엔드 디자인 및 구현', 'IN_PERSON', 'APPLYING', '웹사이트 리디자인', 2, NOW(), NOW()), - (3, '2024-10-01', '2024-10-15', '2024-10-20', '2024-12-15', 700000, 'PROGRAM', '새 애플리케이션을 위한 프로그래밍', 'REMOTE', 'PREPARING', '새 애플리케이션 개발', 3, NOW(), NOW()), - (4, '2024-09-20', '2024-10-10', '2024-10-15', '2024-12-31', 800000, 'WEB', '웹 애플리케이션 개발 프로젝트', 'IN_PERSON', 'PREPARING', '전자상거래 플랫폼', 4, NOW(), NOW()), - (5, '2024-09-05', '2024-09-20', '2024-09-25', '2024-11-20', 550000, 'BACKEND', '모바일 앱을 위한 API 개발', 'REMOTE', 'APPLYING', '모바일 API 통합', 5, NOW(), NOW()), - (6, '2024-08-30', '2024-09-15', '2024-09-20', '2024-11-30', 620000, 'FRONTEND', 'UI/UX 개선 작업', 'IN_PERSON', 'APPLYING', 'UI/UX 향상', 6, NOW(), NOW()), - (7, '2024-09-10', '2024-09-30', '2024-10-01', '2024-12-15', 500000, 'PROGRAM', '내부 도구를 위한 소프트웨어 프로그래밍', 'REMOTE', 'PREPARING', '내부 도구 업그레이드', 7, NOW(), NOW()), - (8, '2024-09-15', '2024-10-05', '2024-10-10', '2024-12-10', 750000, 'WEB', '완전한 웹 개발', 'IN_PERSON', 'APPLYING', '완전한 웹 솔루션', 8, NOW(), NOW()), - (9, '2024-09-01', '2024-09-20', '2024-09-25', '2024-11-15', 530000, 'BACKEND', '백엔드 API 구축', 'REMOTE', 'APPLYING', '백엔드 API 구축', 9, NOW(), NOW()), - (10, '2024-09-20', '2024-10-01', '2024-10-05', '2024-12-10', 600000, 'FRONTEND', '사용자 인터페이스 디자인 개선', 'IN_PERSON', 'PREPARING', 'UI 디자인 개선', 10, NOW(), NOW()), - (11, '2024-09-05', '2024-09-25', '2024-09-30', '2024-11-30', 620000, 'PROGRAM', '새 소프트웨어 모듈 개발', 'REMOTE', 'APPLYING', '소프트웨어 모듈 개발', 11, NOW(), NOW()), - (12, '2024-10-01', '2024-10-20', '2024-10-25', '2024-12-15', 700000, 'WEB', '웹사이트 기능 추가', 'IN_PERSON', 'PREPARING', '웹사이트 기능 추가', 12, NOW(), NOW()), - (13, '2024-08-25', '2024-09-15', '2024-09-20', '2024-11-10', 550000, 'BACKEND', '서버 관리 시스템 구축', 'REMOTE', 'APPLYING', '서버 관리 시스템', 13, NOW(), NOW()), - (14, '2024-09-05', '2024-09-25', '2024-09-30', '2024-11-30', 580000, 'FRONTEND', '웹 프론트엔드 기능 개선', 'IN_PERSON', 'APPLYING', '프론트엔드 개선', 14, NOW(), NOW()), - (15, '2024-09-15', '2024-10-10', '2024-10-15', '2024-12-20', 630000, 'PROGRAM', '애플리케이션 데이터베이스 설계', 'REMOTE', 'PREPARING', '데이터베이스 설계', 15, NOW(), NOW()), - (16, '2024-08-15', '2024-09-15', '2024-09-20', '2024-11-15', 610000, 'WEB', '웹 애플리케이션 보안 강화', 'IN_PERSON', 'APPLYING', '웹 보안 강화', 16, NOW(), NOW()), - (17, '2024-09-05', '2024-09-25', '2024-09-30', '2024-12-15', 650000, 'BACKEND', '서버 성능 개선 프로젝트', 'REMOTE', 'PREPARING', '서버 성능 개선', 17, NOW(), NOW()), - (18, '2024-08-25', '2024-09-10', '2024-09-15', '2024-11-30', 560000, 'FRONTEND', '웹페이지 반응형 디자인', 'IN_PERSON', 'APPLYING', '반응형 디자인', 18, NOW(), NOW()), - (19, '2024-09-10', '2024-10-01', '2024-10-05', '2024-12-25', 620000, 'PROGRAM', '소프트웨어 테스트 및 배포', 'REMOTE', 'PREPARING', '소프트웨어 테스트', 19, NOW(), NOW()), - (20, '2024-09-01', '2024-09-20', '2024-09-25', '2024-11-20', 540000, 'WEB', '웹사이트 콘텐츠 관리 시스템 구축', 'IN_PERSON', 'APPLYING', 'CMS 구축', 20, NOW(), NOW()), - (21, '2024-09-10', '2024-09-30', '2024-10-01', '2024-12-10', 670000, 'BACKEND', '클라우드 기반 백엔드 시스템 구축', 'REMOTE', 'PREPARING', '클라우드 백엔드', 21, NOW(), NOW()), - (22, '2024-08-30', '2024-09-20', '2024-09-25', '2024-11-30', 600000, 'FRONTEND', '웹사이트 사용자 경험 개선', 'IN_PERSON', 'APPLYING', 'UX 개선', 22, NOW(), NOW()), - (23, '2024-09-15', '2024-10-05', '2024-10-10', '2024-12-05', 640000, 'PROGRAM', '애플리케이션 기능 추가', 'REMOTE', 'PREPARING', '기능 추가', 23, NOW(), NOW()), - (24, '2024-09-10', '2024-10-01', '2024-10-05', '2024-11-25', 560000, 'WEB', '웹사이트 유지보수', 'IN_PERSON', 'APPLYING', '웹 유지보수', 24, NOW(), NOW()), - (25, '2024-09-20', '2024-10-15', '2024-10-20', '2025-02-01', 690000, 'BACKEND', 'API 성능 개선', 'REMOTE', 'PREPARING', 'API 성능', 25, NOW(), NOW()); + (1, '2024-09-01', '2024-09-15', '2024-09-20', '2024-11-01', 500000, 'BACKEND', '새 시스템을 위한 백엔드 개발', 'REMOTE', 'APPLICATION_OPENED', '시스템 백엔드 리뉴얼', 1, NOW(), NOW(), 5), + (2, '2024-09-15', '2024-10-01', '2024-10-05', '2024-12-01', 600000, 'FRONTEND', '프론트엔드 디자인 및 구현', 'IN_PERSON', 'APPLICATION_OPENED', '웹사이트 리디자인', 2, NOW(), NOW(), 5), + (3, '2024-10-01', '2024-10-15', '2024-10-20', '2024-12-15', 700000, 'PROGRAM', '새 애플리케이션을 위한 프로그래밍', 'REMOTE', 'PREPARING', '새 애플리케이션 개발', 3, NOW(), NOW(), 5), + (4, '2024-09-20', '2024-10-10', '2024-10-15', '2024-12-31', 800000, 'WEB', '웹 애플리케이션 개발 프로젝트', 'IN_PERSON', 'PREPARING', '전자상거래 플랫폼', 4, NOW(), NOW(), 5), + (5, '2024-09-05', '2024-09-20', '2024-09-25', '2024-11-20', 550000, 'BACKEND', '모바일 앱을 위한 API 개발', 'REMOTE', 'APPLICATION_OPENED', '모바일 API 통합', 5, NOW(), NOW(), 5), + (6, '2024-08-30', '2024-09-15', '2024-09-20', '2024-11-30', 620000, 'FRONTEND', 'UI/UX 개선 작업', 'IN_PERSON', 'APPLICATION_OPENED', 'UI/UX 향상', 6, NOW(), NOW(), 5), + (7, '2024-09-10', '2024-09-30', '2024-10-01', '2024-12-15', 500000, 'PROGRAM', '내부 도구를 위한 소프트웨어 프로그래밍', 'REMOTE', 'PREPARING', '내부 도구 업그레이드', 7, NOW(), NOW(), 5), + (8, '2024-09-15', '2024-10-05', '2024-10-10', '2024-12-10', 750000, 'WEB', '완전한 웹 개발', 'IN_PERSON', 'APPLICATION_OPENED', '완전한 웹 솔루션', 8, NOW(), NOW(), 5), + (9, '2024-09-01', '2024-09-20', '2024-09-25', '2024-11-15', 530000, 'BACKEND', '백엔드 API 구축', 'REMOTE', 'APPLICATION_OPENED', '백엔드 API 구축', 9, NOW(), NOW(), 5), + (10, '2024-09-20', '2024-10-01', '2024-10-05', '2024-12-10', 600000, 'FRONTEND', '사용자 인터페이스 디자인 개선', 'IN_PERSON', 'PREPARING', 'UI 디자인 개선', 10, NOW(), NOW(), 5), + (11, '2024-09-05', '2024-09-25', '2024-09-30', '2024-11-30', 620000, 'PROGRAM', '새 소프트웨어 모듈 개발', 'REMOTE', 'APPLICATION_OPENED', '소프트웨어 모듈 개발', 11, NOW(), NOW(), 5), + (12, '2024-10-01', '2024-10-20', '2024-10-25', '2024-12-15', 700000, 'WEB', '웹사이트 기능 추가', 'IN_PERSON', 'PREPARING', '웹사이트 기능 추가', 12, NOW(), NOW(), 5), + (13, '2024-08-25', '2024-09-15', '2024-09-20', '2024-11-10', 550000, 'BACKEND', '서버 관리 시스템 구축', 'REMOTE', 'APPLICATION_OPENED', '서버 관리 시스템', 13, NOW(), NOW(), 5), + (14, '2024-09-05', '2024-09-25', '2024-09-30', '2024-11-30', 580000, 'FRONTEND', '웹 프론트엔드 기능 개선', 'IN_PERSON', 'APPLICATION_OPENED', '프론트엔드 개선', 14, NOW(), NOW(), 5), + (15, '2024-09-15', '2024-10-10', '2024-10-15', '2024-12-20', 630000, 'PROGRAM', '애플리케이션 데이터베이스 설계', 'REMOTE', 'PREPARING', '데이터베이스 설계', 15, NOW(), NOW(), 5), + (16, '2024-08-15', '2024-09-15', '2024-09-20', '2024-11-15', 610000, 'WEB', '웹 애플리케이션 보안 강화', 'IN_PERSON', 'APPLICATION_OPENED', '웹 보안 강화', 16, NOW(), NOW(), 5), + (17, '2024-09-05', '2024-09-25', '2024-09-30', '2024-12-15', 650000, 'BACKEND', '서버 성능 개선 프로젝트', 'REMOTE', 'PREPARING', '서버 성능 개선', 17, NOW(), NOW(), 5), + (18, '2024-08-25', '2024-09-10', '2024-09-15', '2024-11-30', 560000, 'FRONTEND', '웹페이지 반응형 디자인', 'IN_PERSON', 'APPLICATION_OPENED', '반응형 디자인', 18, NOW(), NOW(), 5), + (19, '2024-09-10', '2024-10-01', '2024-10-05', '2024-12-25', 620000, 'PROGRAM', '소프트웨어 테스트 및 배포', 'REMOTE', 'PREPARING', '소프트웨어 테스트', 19, NOW(), NOW(), 5), + (20, '2024-09-01', '2024-09-20', '2024-09-25', '2024-11-20', 540000, 'WEB', '웹사이트 콘텐츠 관리 시스템 구축', 'IN_PERSON', 'APPLICATION_OPENED', 'CMS 구축', 20, NOW(), NOW(), 5), + (21, '2024-09-10', '2024-09-30', '2024-10-01', '2024-12-10', 670000, 'BACKEND', '클라우드 기반 백엔드 시스템 구축', 'REMOTE', 'PREPARING', '클라우드 백엔드', 21, NOW(), NOW(), 5), + (22, '2024-08-30', '2024-09-20', '2024-09-25', '2024-11-30', 600000, 'FRONTEND', '웹사이트 사용자 경험 개선', 'IN_PERSON', 'APPLICATION_OPENED', 'UX 개선', 22, NOW(), NOW(), 5), + (23, '2024-09-15', '2024-10-05', '2024-10-10', '2024-12-05', 640000, 'PROGRAM', '애플리케이션 기능 추가', 'REMOTE', 'PREPARING', '기능 추가', 23, NOW(), NOW(), 5), + (24, '2024-09-10', '2024-10-01', '2024-10-05', '2024-11-25', 560000, 'WEB', '웹사이트 유지보수', 'IN_PERSON', 'APPLICATION_OPENED', '웹 유지보수', 24, NOW(), NOW(), 5), + (25, '2024-09-20', '2024-10-15', '2024-10-20', '2025-02-01', 690000, 'BACKEND', 'API 성능 개선', 'REMOTE', 'PREPARING', 'API 성능', 25, NOW(), NOW(), 5); INSERT INTO project_hashtag (project_id, hashtags) VALUES @@ -423,7 +423,7 @@ VALUES (25, 'https://storage.principes.xyz/25/files/project25_summary.pdf'), (25, 'https://storage.principes.xyz/25/files/project25_notes.pdf'); -INSERT INTO project_like (people_id, project_id, created_at) +INSERT INTO project_like (member_id, project_id, created_at) VALUES (1, 1, NOW()), (1, 2, NOW()), (1, 5, NOW()), (1, 8, NOW()), (2, 3, NOW()), (2, 6, NOW()), (2, 9, NOW()), (2, 12, NOW()), @@ -447,4 +447,52 @@ VALUES (20, 21, NOW()), (20, 22, NOW()), (20, 25, NOW()), (21, 22, NOW()), (21, 23, NOW()), (22, 24, NOW()), - (23, 25, NOW()); \ No newline at end of file + (23, 25, NOW()); + +INSERT INTO project_application ( + project_application_id, + expected_start_date, + expected_end_date, + description, + status, + people_id, + project_id, + created_at, + updated_at, + dtype +) +VALUES + (1, '2024-01-01', '2024-01-05', '개발 지원서 작성', 'COMPLETED', 1, 1, now(), now(), 'INDIVIDUAL'), + (2, '2024-02-01', '2024-02-05', '웹사이트 구축', 'COMPLETED', 2, 1, now(), now(), 'INDIVIDUAL'), + (3, '2024-03-01', '2024-03-05', '데이터 분석 프로젝트', 'COMPLETED', 3, 1, now(), now(), 'INDIVIDUAL'), + (4, '2024-04-01', '2024-04-05', '모바일 앱 개발', 'COMPLETED', 4, 1, now(), now(), 'INDIVIDUAL'), + (5, '2024-05-01', '2024-05-05', 'AI 모델 구축', 'COMPLETED', 5, 1, now(), now(), 'INDIVIDUAL'), + (6, '2024-06-01', '2024-06-05', '대규모 시스템 개발', 'COMPLETED', 6, 1, now(), now(), 'TEAM'), + (7, '2024-07-01', '2024-07-05', '클라우드 인프라 구축', 'COMPLETED', 7, 1, now(), now(), 'TEAM'), + (8, '2024-08-01', '2024-08-05', 'AI 프로젝트 공동 진행', 'COMPLETED', 8, 1, now(), now(), 'TEAM'), + (9, '2024-09-01', '2024-09-05', 'IoT 플랫폼 개발', 'COMPLETED', 9, 1, now(), now(), 'TEAM'), + (10, '2024-10-01', '2024-10-05', '데이터 엔지니어링 프로젝트', 'COMPLETED', 10, 1, now(), now(), 'TEAM'); + +INSERT INTO individual_project_application (project_application_id) VALUES (1), (2), (3), (4), (5); + +INSERT INTO team_project_application (project_application_id) VALUES (6), (7), (8), (9), (10); + +INSERT INTO team_project_application_teammate ( + teammate_id, + people_id, + status, + project_application_id, + created_at, + updated_at +) +VALUES + (1, 11, 'APPROVED', 6, now(), now()), + (2, 12, 'APPROVED', 6, now(), now()), + (3, 13, 'APPROVED', 7, now(), now()), + (4, 14, 'APPROVED', 7, now(), now()), + (5, 15, 'APPROVED', 8, now(), now()), + (6, 16, 'APPROVED', 8, now(), now()), + (7, 17, 'APPROVED', 9, now(), now()), + (8, 18, 'APPROVED', 9, now(), now()), + (9, 19, 'APPROVED', 10, now(), now()), + (10, 20, 'APPROVED', 10, now(), now()); diff --git a/src/main/resources/db/migration/V1__init.sql b/get-p-persistence/src/main/resources/db/migration/V1__init.sql similarity index 100% rename from src/main/resources/db/migration/V1__init.sql rename to get-p-persistence/src/main/resources/db/migration/V1__init.sql diff --git a/get-p-persistence/src/main/resources/db/migration/V2__alter_like_member_id.sql b/get-p-persistence/src/main/resources/db/migration/V2__alter_like_member_id.sql new file mode 100644 index 00000000..493066cf --- /dev/null +++ b/get-p-persistence/src/main/resources/db/migration/V2__alter_like_member_id.sql @@ -0,0 +1,2 @@ +alter table people_like rename column client_id to member_id; +alter table project_like rename column people_id to member_id; \ No newline at end of file diff --git a/get-p-persistence/src/main/resources/db/migration/V3__add_project_recruitment_count.sql b/get-p-persistence/src/main/resources/db/migration/V3__add_project_recruitment_count.sql new file mode 100644 index 00000000..604be201 --- /dev/null +++ b/get-p-persistence/src/main/resources/db/migration/V3__add_project_recruitment_count.sql @@ -0,0 +1 @@ +alter table project add column recruitment_count bigint; \ No newline at end of file diff --git a/get-p-persistence/src/main/resources/db/migration/V4__divide_project_application.sql b/get-p-persistence/src/main/resources/db/migration/V4__divide_project_application.sql new file mode 100644 index 00000000..fb31d3e5 --- /dev/null +++ b/get-p-persistence/src/main/resources/db/migration/V4__divide_project_application.sql @@ -0,0 +1,29 @@ +alter table people drop column people_type; + +alter table project_application add column dtype varchar(255) not null; +alter table project_application change column status status enum( + 'PENDING_TEAM_APPROVAL', + 'COMPLETED', + 'WAITING_MEETING', + 'MEETING_COMPLETED', + 'ACCEPTED', + 'CLOSED') + null; + +create table individual_project_application +( + project_application_id bigint auto_increment, + primary key (project_application_id) +); + +create table team_project_application +( + project_application_id bigint auto_increment, + primary key (project_application_id) +); + +create table team_project_application_team +( + project_application_id bigint not null, + teams bigint null +); diff --git a/get-p-persistence/src/main/resources/db/migration/V5__create_teammate.sql b/get-p-persistence/src/main/resources/db/migration/V5__create_teammate.sql new file mode 100644 index 00000000..695e4e0e --- /dev/null +++ b/get-p-persistence/src/main/resources/db/migration/V5__create_teammate.sql @@ -0,0 +1,10 @@ +create table team_project_application_teammate +( + teammate_id bigint auto_increment not null, + people_id bigint not null, + status enum ('PENDING', 'APPROVED', 'REJECTED', 'EXPIRED') null, + project_application_id bigint not null, + created_at timestamp null, + updated_at timestamp null, + primary key (teammate_id) +); diff --git a/get-p-persistence/src/main/resources/db/migration/V6__add_project_category_etc.sql b/get-p-persistence/src/main/resources/db/migration/V6__add_project_category_etc.sql new file mode 100644 index 00000000..53df0b15 --- /dev/null +++ b/get-p-persistence/src/main/resources/db/migration/V6__add_project_category_etc.sql @@ -0,0 +1 @@ +alter table project modify category enum('BACKEND', 'FRONTEND', 'PROGRAM', 'WEB', 'ETC'); diff --git a/get-p-persistence/src/main/resources/db/migration/V7__add_project_status.sql b/get-p-persistence/src/main/resources/db/migration/V7__add_project_status.sql new file mode 100644 index 00000000..f226cce9 --- /dev/null +++ b/get-p-persistence/src/main/resources/db/migration/V7__add_project_status.sql @@ -0,0 +1,15 @@ +alter table project modify column status varchar(255); + +update project set status = 'APPLICATION_OPENED' where status = 'APPLYING'; + +alter table project modify column status enum( + 'PREPARING', + 'APPLICATION_OPENED', + 'APPLICATION_CLOSED', + 'MEETING', + 'CONFIRMED', + 'PROGRESSING', + 'COMPLETED', + 'CANCELLED' +); + diff --git a/get-p-persistence/src/main/resources/db/migration/V8__drop_team_project_application_team.sql b/get-p-persistence/src/main/resources/db/migration/V8__drop_team_project_application_team.sql new file mode 100644 index 00000000..b78f62c0 --- /dev/null +++ b/get-p-persistence/src/main/resources/db/migration/V8__drop_team_project_application_team.sql @@ -0,0 +1 @@ +drop table team_project_application_team; \ No newline at end of file diff --git a/get-p-persistence/src/main/resources/db/migration/V9__create_index_project_application.sql b/get-p-persistence/src/main/resources/db/migration/V9__create_index_project_application.sql new file mode 100644 index 00000000..5929841e --- /dev/null +++ b/get-p-persistence/src/main/resources/db/migration/V9__create_index_project_application.sql @@ -0,0 +1,5 @@ +create index ix_project_application_project_id_status +on project_application(project_id, status); + +create index ix_team_project_application_teammate_project_application_id +on team_project_application_teammate(project_application_id); \ No newline at end of file diff --git a/get-p-persistence/src/main/resources/persistence-config.yml b/get-p-persistence/src/main/resources/persistence-config.yml new file mode 100644 index 00000000..45161029 --- /dev/null +++ b/get-p-persistence/src/main/resources/persistence-config.yml @@ -0,0 +1,71 @@ +spring: + datasource: + url: ${DB_URL} + username: ${DB_USERNAME} + password: ${DB_PASSWORD} + + data: + redis: + host: ${REDIS_HOST} + port: ${REDIS_PORT} + password: ${REDIS_PASSWORD} + + flyway: + enabled: true + baseline-on-migrate: true + +--- +spring: + config: + activate: + on-profile: local + + jpa: + hibernate: + ddl-auto: validate + properties: + hibernate: + show_sql: true + format_sql: true + highlight_sql: true + use_sql_comments: true + jdbc: + time_zone: Asia/Seoul + default_batch_fetch_size: 20 + dialect: org.hibernate.dialect.MySQLDialect + +logging: + level: + org: + type: + descriptor: + sql: + BasicBinder: TRACE + +--- +spring: + config: + activate: + on-profile: dev + + jpa: + hibernate: + ddl-auto: validate + properties: + hibernate: + show_sql: true + format_sql: true + highlight_sql: true + use_sql_comments: true + jdbc: + time_zone: Asia/Seoul + default_batch_fetch_size: 20 + dialect: org.hibernate.dialect.MySQLDialect + +logging: + level: + org: + type: + descriptor: + sql: + BasicBinder: TRACE \ No newline at end of file diff --git a/get-p-persistence/src/test/java/es/princip/getp/PersistenceTestApplication.java b/get-p-persistence/src/test/java/es/princip/getp/PersistenceTestApplication.java new file mode 100644 index 00000000..afd585ab --- /dev/null +++ b/get-p-persistence/src/test/java/es/princip/getp/PersistenceTestApplication.java @@ -0,0 +1,11 @@ +package es.princip.getp; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class PersistenceTestApplication { + public static void main(String[] args) { + SpringApplication.run(PersistenceTestApplication.class, args); + } +} diff --git a/src/test/java/es/princip/getp/persistence/adapter/client/ClientDataLoader.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/client/ClientDataLoader.java similarity index 62% rename from src/test/java/es/princip/getp/persistence/adapter/client/ClientDataLoader.java rename to get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/client/ClientDataLoader.java index 1e06621e..2038d99c 100644 --- a/src/test/java/es/princip/getp/persistence/adapter/client/ClientDataLoader.java +++ b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/client/ClientDataLoader.java @@ -1,8 +1,6 @@ package es.princip.getp.persistence.adapter.client; -import es.princip.getp.domain.member.model.MemberType; import es.princip.getp.persistence.support.DataLoader; -import es.princip.getp.persistence.adapter.member.MemberJpaEntity; import jakarta.persistence.EntityManager; import lombok.RequiredArgsConstructor; @@ -11,7 +9,6 @@ import static es.princip.getp.fixture.client.AddressFixture.*; import static es.princip.getp.fixture.client.BankAccountFixture.*; -import static es.princip.getp.fixture.member.PasswordFixture.PASSWORD; @RequiredArgsConstructor public class ClientDataLoader implements DataLoader { @@ -20,13 +17,6 @@ public class ClientDataLoader implements DataLoader { @Override public void load(final int size) { - final List memberList = LongStream.rangeClosed(1, size) - .mapToObj(i -> MemberJpaEntity.builder() - .email("test" + i + "@example.com") - .password(PASSWORD) - .memberType(MemberType.ROLE_CLIENT) - .build()) - .toList(); final List clientList = LongStream.rangeClosed(1, size) .mapToObj(i -> ClientJpaEntity.builder() .memberId(i) @@ -35,17 +25,12 @@ public void load(final int size) { .bankAccount(new BankAccountJpaVO(BANK, ACCOUNT_NUMBER, ACCOUNT_HOLDER)) .build()) .toList(); - - memberList.forEach(entityManager::persist); clientList.forEach(entityManager::persist); } @Override public void teardown() { entityManager.createQuery("DELETE FROM ClientJpaEntity").executeUpdate(); - entityManager.createQuery("DELETE FROM MemberJpaEntity").executeUpdate(); - entityManager.createNativeQuery("ALTER TABLE member AUTO_INCREMENT = 1") - .executeUpdate(); entityManager.createNativeQuery("ALTER TABLE client AUTO_INCREMENT = 1") .executeUpdate(); } diff --git a/src/test/java/es/princip/getp/persistence/adapter/like/people/CountPeopleLikeAdapterTest.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/like/people/CountPeopleLikeAdapterTest.java similarity index 77% rename from src/test/java/es/princip/getp/persistence/adapter/like/people/CountPeopleLikeAdapterTest.java rename to get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/like/people/CountPeopleLikeAdapterTest.java index 8efd9c8f..8eb7ce7a 100644 --- a/src/test/java/es/princip/getp/persistence/adapter/like/people/CountPeopleLikeAdapterTest.java +++ b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/like/people/CountPeopleLikeAdapterTest.java @@ -1,6 +1,7 @@ package es.princip.getp.persistence.adapter.like.people; import es.princip.getp.application.like.people.port.out.CountPeopleLikePort; +import es.princip.getp.domain.people.model.PeopleId; import es.princip.getp.persistence.support.DataLoader; import es.princip.getp.persistence.support.PersistenceAdapterTest; import jakarta.persistence.EntityManager; @@ -22,7 +23,6 @@ public class CountPeopleLikeAdapterTest extends PersistenceAdapterTest { @PersistenceContext private EntityManager entityManager; @Autowired private CountPeopleLikePort countPeopleLikePort; - @Autowired private PeopleLikePersistenceMapper peopleLikeMapper; private static final int TEST_SIZE = 5; private List dataLoaders; @@ -30,7 +30,7 @@ public class CountPeopleLikeAdapterTest extends PersistenceAdapterTest { @BeforeEach void setUp() { dataLoaders = List.of( - new PeopleLikeDataLoader(entityManager, peopleLikeMapper) + new PeopleLikeDataLoader(entityManager) ); dataLoaders.forEach(dataLoader -> dataLoader.load(TEST_SIZE)); } @@ -42,15 +42,17 @@ void teardown() { @Test void 피플이_받은_좋아요_수를_조회한다() { - assertThat(countPeopleLikePort.countBy(1L)).isEqualTo(TEST_SIZE); + final PeopleId peopleId = new PeopleId(1L); + assertThat(countPeopleLikePort.countBy(peopleId)).isEqualTo(TEST_SIZE); } @Test void 여러명의_피플이_받은_좋아요_수를_조회한다() { - final Long[] peopleIds = LongStream.rangeClosed(1, TEST_SIZE) + final PeopleId[] peopleIds = LongStream.rangeClosed(1, TEST_SIZE) .boxed() - .toArray(Long[]::new); - final Map likesCounts = countPeopleLikePort.countBy(peopleIds); + .map(PeopleId::new) + .toArray(PeopleId[]::new); + final Map likesCounts = countPeopleLikePort.countBy(peopleIds); assertThat(likesCounts).hasSize(peopleIds.length) .containsOnlyKeys(peopleIds) diff --git a/src/test/java/es/princip/getp/persistence/adapter/like/people/PeopleLikeDataLoader.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/like/people/PeopleLikeDataLoader.java similarity index 66% rename from src/test/java/es/princip/getp/persistence/adapter/like/people/PeopleLikeDataLoader.java rename to get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/like/people/PeopleLikeDataLoader.java index b4cd7645..f9c111a4 100644 --- a/src/test/java/es/princip/getp/persistence/adapter/like/people/PeopleLikeDataLoader.java +++ b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/like/people/PeopleLikeDataLoader.java @@ -1,6 +1,5 @@ package es.princip.getp.persistence.adapter.like.people; -import es.princip.getp.fixture.like.PeopleLikeFixture; import es.princip.getp.persistence.support.DataLoader; import jakarta.persistence.EntityManager; import lombok.RequiredArgsConstructor; @@ -13,20 +12,13 @@ public class PeopleLikeDataLoader implements DataLoader { private final EntityManager entityManager; - private final PeopleLikePersistenceMapper mapper; @Override public void load(final int size) { final List likeList = new ArrayList<>(); - LongStream.range(0, size).forEach(clientId -> - LongStream.rangeClosed(1, size).forEach(peopleId -> - likeList.add( - mapper.mapToJpa( - PeopleLikeFixture.peopleLike(1L, peopleId) - ) - ) - ) - ); + LongStream.rangeClosed(1, size) + .forEach(memberId -> LongStream.rangeClosed(1, size) + .forEach(peopleId -> likeList.add(peopleLike(memberId, peopleId)))); likeList.forEach(entityManager::persist); } @@ -36,4 +28,11 @@ public void teardown() { entityManager.createNativeQuery("ALTER TABLE people_like AUTO_INCREMENT = 1") .executeUpdate(); } + + private PeopleLikeJpaEntity peopleLike(final Long memberId, final Long peopleId) { + return PeopleLikeJpaEntity.builder() + .memberId(memberId) + .peopleId(peopleId) + .build(); + } } diff --git a/src/test/java/es/princip/getp/persistence/adapter/like/project/CountProjectLikeAdapterTest.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/like/project/CountProjectLikeAdapterTest.java similarity index 86% rename from src/test/java/es/princip/getp/persistence/adapter/like/project/CountProjectLikeAdapterTest.java rename to get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/like/project/CountProjectLikeAdapterTest.java index e375ce8e..eff26f67 100644 --- a/src/test/java/es/princip/getp/persistence/adapter/like/project/CountProjectLikeAdapterTest.java +++ b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/like/project/CountProjectLikeAdapterTest.java @@ -1,6 +1,7 @@ package es.princip.getp.persistence.adapter.like.project; import es.princip.getp.application.like.project.port.out.CountProjectLikePort; +import es.princip.getp.domain.project.commission.model.ProjectId; import es.princip.getp.persistence.support.DataLoader; import es.princip.getp.persistence.support.PersistenceAdapterTest; import jakarta.persistence.EntityManager; @@ -20,7 +21,6 @@ public class CountProjectLikeAdapterTest extends PersistenceAdapterTest { @PersistenceContext private EntityManager entityManager; @Autowired private CountProjectLikePort countProjectLikePort; - @Autowired private ProjectLikePersistenceMapper projectLikeMapper; private static final int TEST_SIZE = 5; private List dataLoaders; @@ -28,7 +28,7 @@ public class CountProjectLikeAdapterTest extends PersistenceAdapterTest { @BeforeEach void setUp() { dataLoaders = List.of( - new ProjectLikeDataLoader(entityManager, projectLikeMapper) + new ProjectLikeDataLoader(entityManager) ); dataLoaders.forEach(dataLoader -> dataLoader.load(TEST_SIZE)); } @@ -40,7 +40,7 @@ void teardown() { @Test void 프로젝트가_받은_좋아요_수를_조회한다() { - final Long likesCount = countProjectLikePort.countBy(1L); + final Long likesCount = countProjectLikePort.countBy(new ProjectId(1L)); assertThat(likesCount).isEqualTo(TEST_SIZE); } diff --git a/src/test/java/es/princip/getp/persistence/adapter/like/project/ProjectLikeDataLoader.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/like/project/ProjectLikeDataLoader.java similarity index 66% rename from src/test/java/es/princip/getp/persistence/adapter/like/project/ProjectLikeDataLoader.java rename to get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/like/project/ProjectLikeDataLoader.java index c539a72a..117a884a 100644 --- a/src/test/java/es/princip/getp/persistence/adapter/like/project/ProjectLikeDataLoader.java +++ b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/like/project/ProjectLikeDataLoader.java @@ -1,6 +1,7 @@ package es.princip.getp.persistence.adapter.like.project; -import es.princip.getp.fixture.like.ProjectLikeFixture; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.project.commission.model.ProjectId; import es.princip.getp.persistence.support.DataLoader; import jakarta.persistence.EntityManager; import lombok.RequiredArgsConstructor; @@ -14,17 +15,17 @@ public class ProjectLikeDataLoader implements DataLoader { private final EntityManager entityManager; - private final ProjectLikePersistenceMapper mapper; @Override @Transactional public void load(final int size) { final List projectLikeList = new ArrayList<>(); - LongStream.rangeClosed(1, size).forEach(peopleId -> + LongStream.rangeClosed(1, size).forEach(memberId -> LongStream.rangeClosed(1, size).forEach(projectId -> projectLikeList.add( - mapper.mapToJpa( - ProjectLikeFixture.projectLike(peopleId, projectId) + projectLike( + new MemberId(memberId), + new ProjectId(projectId) ) ) ) @@ -38,4 +39,11 @@ public void teardown() { entityManager.createNativeQuery("ALTER TABLE project_like AUTO_INCREMENT = 1") .executeUpdate(); } + + private ProjectLikeJpaEntity projectLike(final MemberId memberId, final ProjectId projectId) { + return ProjectLikeJpaEntity.builder() + .memberId(memberId.getValue()) + .projectId(projectId.getValue()) + .build(); + } } diff --git a/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/member/MemberDataLoader.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/member/MemberDataLoader.java new file mode 100644 index 00000000..50a91d83 --- /dev/null +++ b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/member/MemberDataLoader.java @@ -0,0 +1,34 @@ +package es.princip.getp.persistence.adapter.member; + +import es.princip.getp.persistence.support.DataLoader; +import jakarta.persistence.EntityManager; +import lombok.RequiredArgsConstructor; + +import java.util.List; +import java.util.stream.LongStream; + +import static es.princip.getp.fixture.member.PasswordFixture.PASSWORD; + +@RequiredArgsConstructor +public class MemberDataLoader implements DataLoader { + + private final EntityManager entityManager; + + @Override + public void load(final int size) { + final List memberList = LongStream.range(1, 1 + size) + .mapToObj(i -> MemberJpaEntity.builder() + .email("test" + i + "@example.com") + .nickname("test" + i) + .password(PASSWORD) + .build()) + .toList(); + memberList.forEach(entityManager::persist); + } + + @Override + public void teardown() { + entityManager.createQuery("DELETE FROM MemberJpaEntity").executeUpdate(); + entityManager.createNativeQuery("ALTER TABLE member AUTO_INCREMENT = 1").executeUpdate(); + } +} diff --git a/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/people/FindMyPeopleAdapterTest.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/people/FindMyPeopleAdapterTest.java new file mode 100644 index 00000000..8fed0f6f --- /dev/null +++ b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/people/FindMyPeopleAdapterTest.java @@ -0,0 +1,58 @@ +package es.princip.getp.persistence.adapter.people; + +import es.princip.getp.application.people.dto.response.people.MyPeopleResponse; +import es.princip.getp.application.people.dto.response.peopleProfile.PeopleProfileDetailResponse; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.persistence.adapter.member.MemberDataLoader; +import es.princip.getp.persistence.support.DataLoader; +import es.princip.getp.persistence.support.PersistenceAdapterTest; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class FindMyPeopleAdapterTest extends PersistenceAdapterTest { + + private static final int TEST_SIZE = 1; + + @PersistenceContext private EntityManager entityManager; + @Autowired private FindMyPeopleAdapter adapter; + + private List dataLoaders; + + @BeforeEach + void setUp() { + dataLoaders = List.of( + new MemberDataLoader(entityManager), + new PeopleDataLoader(entityManager) + ); + dataLoaders.forEach(dataLoader -> dataLoader.load(TEST_SIZE)); + } + + @AfterEach + void teardown() { + dataLoaders.forEach(DataLoader::teardown); + } + + @Test + void 멤버_ID로_피플_정보를_조회한다() { + final MemberId memberId = new MemberId(1L); + final MyPeopleResponse response = adapter.findBy(memberId); + + assertThat(response).isNotNull(); + } + + @Test + void 멤버_ID로_피플_상세_프로필을_조회한다() { + final MemberId memberId = new MemberId(1L); + final PeopleProfileDetailResponse response = adapter.findDetailProfileBy(memberId); + + assertThat(response).isNotNull(); + } +} \ No newline at end of file diff --git a/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/people/FindPeopleAdapterTest.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/people/FindPeopleAdapterTest.java new file mode 100644 index 00000000..696062fc --- /dev/null +++ b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/people/FindPeopleAdapterTest.java @@ -0,0 +1,102 @@ +package es.princip.getp.persistence.adapter.people; + +import es.princip.getp.application.people.dto.response.people.CardPeopleResponse; +import es.princip.getp.application.people.dto.response.people.PeopleDetailResponse; +import es.princip.getp.application.people.dto.command.PeopleSearchFilter; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.people.model.PeopleId; +import es.princip.getp.persistence.adapter.like.people.PeopleLikeDataLoader; +import es.princip.getp.persistence.adapter.member.MemberDataLoader; +import es.princip.getp.persistence.support.DataLoader; +import es.princip.getp.persistence.support.PersistenceAdapterTest; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +import java.util.List; + +import static es.princip.getp.domain.member.model.MemberType.ROLE_PEOPLE; +import static es.princip.getp.fixture.member.MemberFixture.member; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.spy; + +class FindPeopleAdapterTest extends PersistenceAdapterTest { + + private static final int TEST_SIZE = 20; + private static final int PAGE_SIZE = 10; + + @PersistenceContext private EntityManager entityManager; + @Autowired private FindPeopleAdapter adapter; + + private List dataLoaders; + + @BeforeEach + void setUp() { + dataLoaders = List.of( + new MemberDataLoader(entityManager), + new PeopleLikeDataLoader(entityManager), + new PeopleDataLoader(entityManager) + ); + dataLoaders.forEach(dataLoader -> dataLoader.load(TEST_SIZE)); + } + + @AfterEach + void teardown() { + dataLoaders.forEach(DataLoader::teardown); + } + + @Test + void 피플_목록을_조회한다() { + final Pageable pageable = PageRequest.of(0, PAGE_SIZE); + final PeopleSearchFilter filter = new PeopleSearchFilter(null, null); + final MemberId memberId = new MemberId(1L); + final Page response = adapter.findCardBy(pageable, filter, memberId); + + assertThat(response.getNumberOfElements()).isEqualTo(PAGE_SIZE); + assertThat(response.getTotalElements()).isEqualTo(TEST_SIZE); + } + + @Test + void 좋아요한_피플_목록을_조회한다() { + final Pageable pageable = PageRequest.of(0, PAGE_SIZE); + final PeopleSearchFilter filter = new PeopleSearchFilter(null, "true"); + final MemberId memberId = new MemberId(1L); + final Page response = adapter.findCardBy(pageable, filter, memberId); + + assertThat(response.getContent()).isNotEmpty(); + assertThat(response.getNumberOfElements()).isEqualTo(PAGE_SIZE); + assertThat(response.getTotalElements()).isEqualTo(TEST_SIZE); + } + + @Test + void 닉네임으로_피플을_검색한다() { + final Pageable pageable = PageRequest.of(0, PAGE_SIZE); + final PeopleSearchFilter filter = new PeopleSearchFilter("test1", null); + final MemberId memberId = new MemberId(1L); + final Page response = adapter.findCardBy(pageable, filter, memberId); + + assertThat(response.getContent()).isNotEmpty(); + assertThat(response.getNumberOfElements()).isEqualTo(PAGE_SIZE); + assertThat(response.getTotalElements()).isEqualTo(11L); + } + + @Test + void 피플_ID로_피플_상세_정보를_조회한다() { + final MemberId memberId = new MemberId(1L); + final Member member = spy(member(ROLE_PEOPLE)); + final PeopleId peopleId = new PeopleId(1L); + + given(member.getId()).willReturn(memberId); + final PeopleDetailResponse response = adapter.findDetailBy(member, peopleId); + + assertThat(response).isNotNull(); + } +} \ No newline at end of file diff --git a/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/people/PeopleDataLoader.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/people/PeopleDataLoader.java new file mode 100644 index 00000000..3053a909 --- /dev/null +++ b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/people/PeopleDataLoader.java @@ -0,0 +1,51 @@ +package es.princip.getp.persistence.adapter.people; + +import es.princip.getp.persistence.adapter.people.model.PeopleJpaEntity; +import es.princip.getp.persistence.adapter.people.model.PeopleProfileJpaVO; +import es.princip.getp.persistence.support.DataLoader; +import jakarta.persistence.EntityManager; +import lombok.RequiredArgsConstructor; + +import java.util.List; +import java.util.stream.LongStream; + +import static es.princip.getp.fixture.people.ActivityAreaFixture.ACTIVITY_AREA; +import static es.princip.getp.fixture.people.EducationFixture.MAJOR; +import static es.princip.getp.fixture.people.EducationFixture.SCHOOL; +import static es.princip.getp.fixture.people.IntroductionFixture.INTRODUCTION; + +@RequiredArgsConstructor +public class PeopleDataLoader implements DataLoader { + + private final EntityManager entityManager; + + @Override + public void load(final int size) { + final List peopleList = LongStream.rangeClosed(1, size) + .mapToObj(this::people) + .toList() + .stream() + .toList(); + peopleList.forEach(entityManager::persist); + } + + @Override + public void teardown() { + entityManager.createQuery("DELETE FROM PeopleJpaEntity").executeUpdate(); + entityManager.createNativeQuery("ALTER TABLE people AUTO_INCREMENT = 1").executeUpdate(); + } + + private PeopleJpaEntity people(final Long memberId) { + final PeopleProfileJpaVO profile = PeopleProfileJpaVO.builder() + .introduction(INTRODUCTION) + .activityArea(ACTIVITY_AREA) + .school(SCHOOL) + .major(MAJOR) + .build(); + return PeopleJpaEntity.builder() + .memberId(memberId) + .email("people" + memberId + "@test.com") + .profile(profile) + .build(); + } +} diff --git a/src/test/java/es/princip/getp/persistence/adapter/people/PeoplePersistenceFixture.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/people/PeoplePersistenceFixture.java similarity index 90% rename from src/test/java/es/princip/getp/persistence/adapter/people/PeoplePersistenceFixture.java rename to get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/people/PeoplePersistenceFixture.java index 19f3007e..9c4a0dcb 100644 --- a/src/test/java/es/princip/getp/persistence/adapter/people/PeoplePersistenceFixture.java +++ b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/people/PeoplePersistenceFixture.java @@ -2,7 +2,6 @@ import es.princip.getp.domain.common.model.Hashtag; import es.princip.getp.domain.common.model.TechStack; -import es.princip.getp.domain.people.model.PeopleType; import es.princip.getp.persistence.adapter.people.model.PeopleJpaEntity; import es.princip.getp.persistence.adapter.people.model.PeopleProfileJpaVO; import es.princip.getp.persistence.adapter.people.model.PortfolioJpaVO; @@ -53,21 +52,19 @@ private static PeopleProfileJpaVO peopleProfileJpaVO() { .build(); } - public static PeopleJpaEntity peopleJpaEntity(Long memberId, PeopleType peopleType) { + public static PeopleJpaEntity peopleJpaEntity(Long memberId) { return PeopleJpaEntity.builder() .id(memberId) .email(EMAIL) - .peopleType(peopleType) .memberId(memberId) .profile(peopleProfileJpaVO()) .build(); } - public static PeopleJpaEntity peopleJpaEntityWithoutProfile(Long memberId, PeopleType peopleType) { + public static PeopleJpaEntity peopleJpaEntityWithoutProfile(Long memberId) { return PeopleJpaEntity.builder() .id(memberId) .email(EMAIL) - .peopleType(peopleType) .memberId(memberId) .build(); } diff --git a/src/test/java/es/princip/getp/persistence/adapter/people/mapper/PeoplePersistenceMapperTest.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/people/mapper/PeoplePersistenceMapperTest.java similarity index 81% rename from src/test/java/es/princip/getp/persistence/adapter/people/mapper/PeoplePersistenceMapperTest.java rename to get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/people/mapper/PeoplePersistenceMapperTest.java index fed19597..15d7b4b2 100644 --- a/src/test/java/es/princip/getp/persistence/adapter/people/mapper/PeoplePersistenceMapperTest.java +++ b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/people/mapper/PeoplePersistenceMapperTest.java @@ -1,9 +1,9 @@ package es.princip.getp.persistence.adapter.people.mapper; +import es.princip.getp.domain.member.model.MemberId; import es.princip.getp.domain.people.model.People; -import es.princip.getp.domain.people.model.PeopleType; -import es.princip.getp.persistence.support.PersistenceMapperTest; import es.princip.getp.persistence.adapter.people.model.PeopleJpaEntity; +import es.princip.getp.persistence.support.PersistenceMapperTest; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -17,10 +17,12 @@ class PeoplePersistenceMapperTest extends PersistenceMapperTest { @Autowired PeoplePersistenceMapper mapper; + private final MemberId memberId = new MemberId(1L); + @Test void 도메인_모델로_매핑한다() { - final PeopleJpaEntity peopleJpaEntity = peopleJpaEntity(1L, PeopleType.INDIVIDUAL); - final People expected = people(1L, PeopleType.INDIVIDUAL); + final PeopleJpaEntity peopleJpaEntity = peopleJpaEntity(memberId.getValue()); + final People expected = people(memberId); final People people = mapper.mapToDomain(peopleJpaEntity); @@ -31,8 +33,8 @@ class PeoplePersistenceMapperTest extends PersistenceMapperTest { @Test void 도메인_모델로_매핑_시_피플_프로필을_등록하지_않으면_NULL로_매핑한다() { - final PeopleJpaEntity peopleJpaEntity = peopleJpaEntityWithoutProfile(1L, PeopleType.INDIVIDUAL); - final People expected = peopleWithoutProfile(1L, PeopleType.INDIVIDUAL); + final PeopleJpaEntity peopleJpaEntity = peopleJpaEntityWithoutProfile(memberId.getValue()); + final People expected = peopleWithoutProfile(memberId); final People people = mapper.mapToDomain(peopleJpaEntity); @@ -44,8 +46,8 @@ class PeoplePersistenceMapperTest extends PersistenceMapperTest { @Test void JPA_모델로_매핑한다() { - final People people = people(1L, PeopleType.INDIVIDUAL); - final PeopleJpaEntity expected = peopleJpaEntity(1L, PeopleType.INDIVIDUAL); + final People people = people(memberId); + final PeopleJpaEntity expected = peopleJpaEntity(memberId.getValue()); final PeopleJpaEntity peopleJpaEntity = mapper.mapToJpa(people); diff --git a/src/test/java/es/princip/getp/persistence/adapter/project/apply/FindProjectApplicationAdapterTest.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/project/apply/CountProjectApplicationAdapterTest.java similarity index 58% rename from src/test/java/es/princip/getp/persistence/adapter/project/apply/FindProjectApplicationAdapterTest.java rename to get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/project/apply/CountProjectApplicationAdapterTest.java index 61980e03..590db770 100644 --- a/src/test/java/es/princip/getp/persistence/adapter/project/apply/FindProjectApplicationAdapterTest.java +++ b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/project/apply/CountProjectApplicationAdapterTest.java @@ -1,5 +1,6 @@ package es.princip.getp.persistence.adapter.project.apply; +import es.princip.getp.domain.project.commission.model.ProjectId; import es.princip.getp.persistence.support.DataLoader; import es.princip.getp.persistence.support.PersistenceAdapterTest; import jakarta.persistence.EntityManager; @@ -15,20 +16,19 @@ import static org.assertj.core.api.Assertions.assertThat; -class FindProjectApplicationAdapterTest extends PersistenceAdapterTest { +class CountProjectApplicationAdapterTest extends PersistenceAdapterTest { private static final int TEST_SIZE = 10; @PersistenceContext private EntityManager entityManager; - @Autowired private ProjectApplicationPersistenceMapper applicationMapper; - @Autowired private FindProjectApplicationAdapter adapter; + @Autowired private CountProjectApplicationAdapter adapter; private List dataLoaders; @BeforeEach void setUp() { dataLoaders = List.of( - new ProjectApplicationDataLoader(applicationMapper, entityManager) + new ProjectApplicationDataLoader(entityManager) ); dataLoaders.forEach(dataLoader -> dataLoader.load(TEST_SIZE)); } @@ -40,12 +40,21 @@ void teardown() { @Test void 각_프로젝트마다_지원자_수를_구한다() { - final Long[] projectIds = LongStream.rangeClosed(1, TEST_SIZE) + final ProjectId[] projectIds = LongStream.rangeClosed(1, TEST_SIZE) .boxed() - .toArray(Long[]::new); - final Map result = adapter.countByProjectIds(projectIds); + .map(ProjectId::new) + .toArray(ProjectId[]::new); + final Map result = adapter.countBy(projectIds); assertThat(result).hasSize(TEST_SIZE); - assertThat(result.values()).allMatch(value -> value == TEST_SIZE); + assertThat(result.values()).allMatch(value -> value > 0L); + } + + @Test + void 프로젝트의_지원자_수를_구한다() { + final ProjectId projectId = new ProjectId(TEST_SIZE / 2 + 1L); + final Long result = adapter.countBy(projectId); + + assertThat(result).isEqualTo(2L); } } \ No newline at end of file diff --git a/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/project/apply/CursorPageRequest.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/project/apply/CursorPageRequest.java new file mode 100644 index 00000000..eac95829 --- /dev/null +++ b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/project/apply/CursorPageRequest.java @@ -0,0 +1,37 @@ +package es.princip.getp.persistence.adapter.project.apply; + +import es.princip.getp.application.support.Cursor; +import es.princip.getp.application.support.CursorPageable; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; + +public class CursorPageRequest implements CursorPageable { + + private final Pageable pageable; + private final T cursor; + + public CursorPageRequest(final Pageable pageable, final T cursor) { + this.pageable = pageable; + this.cursor = cursor; + } + + @Override + public int getPageSize() { + return pageable.getPageSize(); + } + + @Override + public T getCursor() { + return cursor; + } + + @Override + public boolean hasCursor() { + return cursor != null; + } + + @Override + public Sort getSort() { + return pageable.getSort(); + } +} diff --git a/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/project/apply/FindTeammateAdapterTest.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/project/apply/FindTeammateAdapterTest.java new file mode 100644 index 00000000..4c7f0e62 --- /dev/null +++ b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/project/apply/FindTeammateAdapterTest.java @@ -0,0 +1,76 @@ +package es.princip.getp.persistence.adapter.project.apply; + +import es.princip.getp.application.project.apply.dto.response.SearchTeammateResponse; +import es.princip.getp.application.support.Cursor; +import es.princip.getp.application.support.CursorPageable; +import es.princip.getp.domain.project.commission.model.ProjectId; +import es.princip.getp.persistence.adapter.member.MemberDataLoader; +import es.princip.getp.persistence.adapter.people.PeopleDataLoader; +import es.princip.getp.persistence.support.DataLoader; +import es.princip.getp.persistence.support.PersistenceAdapterTest; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; + +import java.util.List; + +import static es.princip.getp.persistence.adapter.project.apply.ProjectApplicationDataLoader.teamProjectApplication; +import static org.assertj.core.api.Assertions.assertThat; + +class FindTeammateAdapterTest extends PersistenceAdapterTest { + + private static final int TEST_SIZE = 10; + private static final int PAGE_SIZE = 5; + + @PersistenceContext private EntityManager entityManager; + @Autowired private FindTeammateAdapter adapter; + + private List dataLoaders; + + @BeforeEach + void setUp() { + dataLoaders = List.of( + new MemberDataLoader(entityManager), + new PeopleDataLoader(entityManager) + ); + dataLoaders.forEach(dataLoader -> dataLoader.load(TEST_SIZE)); + } + + @AfterEach + void teardown() { + dataLoaders.forEach(DataLoader::teardown); + } + + @Test + void 닉네임으로_팀원을_검색한다() { + final ProjectId projectId = new ProjectId(1L); + final Pageable pageable = PageRequest.of(0, PAGE_SIZE); + final CursorPageable cursorPageable = new CursorPageRequest<>(pageable, null); + + final Slice response = adapter.findBy(projectId, cursorPageable, "test"); + + assertThat(response.getContent()).isNotEmpty(); + assertThat(response.getNumberOfElements()).isEqualTo(PAGE_SIZE); + assertThat(response.hasNext()).isTrue(); + } + + @Test + void 이미_해당_프로젝트에_지원한_피플은_검색되지_않는다() { + final ProjectId projectId = new ProjectId(1L); + final Pageable pageable = PageRequest.of(0, PAGE_SIZE); + final CursorPageable cursorPageable = new CursorPageRequest<>(pageable, null); + entityManager.persist(teamProjectApplication(1L, 1L, PAGE_SIZE)); + + final Slice response = adapter.findBy(projectId, cursorPageable, "test"); + + assertThat(response.getContent()).isNotEmpty(); + assertThat(response.getNumberOfElements()).isEqualTo(PAGE_SIZE); + assertThat(response.hasNext()).isFalse(); + } +} \ No newline at end of file diff --git a/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationDataLoader.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationDataLoader.java new file mode 100644 index 00000000..01ddf1a8 --- /dev/null +++ b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationDataLoader.java @@ -0,0 +1,96 @@ +package es.princip.getp.persistence.adapter.project.apply; + +import es.princip.getp.domain.project.apply.model.TeammateStatus; +import es.princip.getp.persistence.adapter.common.DurationJpaVO; +import es.princip.getp.persistence.adapter.project.apply.model.IndividualProjectApplicationJpaEntity; +import es.princip.getp.persistence.adapter.project.apply.model.ProjectApplicationJpaEntity; +import es.princip.getp.persistence.adapter.project.apply.model.TeamProjectApplicationJpaEntity; +import es.princip.getp.persistence.adapter.project.apply.model.TeammateJpaEntity; +import es.princip.getp.persistence.support.DataLoader; +import jakarta.persistence.EntityManager; +import lombok.RequiredArgsConstructor; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.LongStream; + +import static es.princip.getp.domain.project.apply.model.ProjectApplicationStatus.COMPLETED; +import static es.princip.getp.fixture.project.ProjectApplicationFixture.DESCRIPTION; + +@RequiredArgsConstructor +public class ProjectApplicationDataLoader implements DataLoader { + + private final EntityManager entityManager; + + @Override + public void load(final int size) { + final List projectApplicationList = new ArrayList<>(); + LongStream.rangeClosed(1, size / 2).forEach(id -> + projectApplicationList.add(individualProjectApplication(id, id)) + ); + LongStream.rangeClosed(size / 2 + 1, size).forEach(id -> + projectApplicationList.add(teamProjectApplication(id, id, 2)) // TODO: 현재 같은 피플이 중복 지원 중 + ); + projectApplicationList.forEach(entityManager::persist); + } + + @Override + public void teardown() { + entityManager.createQuery("DELETE FROM ProjectApplicationJpaEntity").executeUpdate(); + entityManager.createNativeQuery("ALTER TABLE individual_project_application AUTO_INCREMENT = 1") + .executeUpdate(); + entityManager.createNativeQuery("ALTER TABLE team_project_application AUTO_INCREMENT = 1") + .executeUpdate(); + } + + static ProjectApplicationJpaEntity individualProjectApplication( + final Long applicantId, + final Long projectId + ) { + return IndividualProjectApplicationJpaEntity.builder() + .applicantId(applicantId) + .projectId(projectId) + .expectedDuration(new DurationJpaVO( + LocalDate.of(2024, 7, 1), + LocalDate.of(2024, 7, 31) + )) + .status(COMPLETED) + .description(DESCRIPTION) + .attachmentFiles(List.of("https://example.com/attachment1")) + .build(); + } + + static ProjectApplicationJpaEntity teamProjectApplication( + final Long applicantId, + final Long projectId, + final int teamSize + ) { + final TeamProjectApplicationJpaEntity application = TeamProjectApplicationJpaEntity.builder() + .applicantId(applicantId) + .projectId(projectId) + .expectedDuration(new DurationJpaVO( + LocalDate.of(2024, 7, 1), + LocalDate.of(2024, 7, 31) + )) + .status(COMPLETED) + .description(DESCRIPTION) + .attachmentFiles(List.of("https://example.com/attachment1")) + .build(); + LongStream.rangeClosed(applicantId + 1, applicantId + teamSize - 1).forEach(peopleId -> + application.addTeammate(teammate(peopleId, application)) + ); + return application; + } + + private static TeammateJpaEntity teammate( + final Long peopleId, + final TeamProjectApplicationJpaEntity application + ) { + return TeammateJpaEntity.builder() + .peopleId(peopleId) + .status(TeammateStatus.APPROVED) + .application(application) + .build(); + } +} diff --git a/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/project/apply/SerializeTeammateCursorAdapterTest.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/project/apply/SerializeTeammateCursorAdapterTest.java new file mode 100644 index 00000000..1fbedbc8 --- /dev/null +++ b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/project/apply/SerializeTeammateCursorAdapterTest.java @@ -0,0 +1,49 @@ +package es.princip.getp.persistence.adapter.project.apply; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import es.princip.getp.application.project.apply.dto.response.SearchTeammateResponse; +import es.princip.getp.application.support.Cursor; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.domain.SliceImpl; + +import java.util.List; +import java.util.stream.LongStream; + +import static org.assertj.core.api.Assertions.assertThat; + +class SerializeTeammateCursorAdapterTest { + + private final ObjectMapper objectMapper = new ObjectMapper(); + private final SerializeTeammateCursorAdapter adapter = new SerializeTeammateCursorAdapter(objectMapper); + + private final int size = 5; + private final Pageable pageable = PageRequest.of(0, size); + private final List content = LongStream.rangeClosed(1, size) + .mapToObj(i -> new SearchTeammateResponse(i, "test" + i, null)) + .toList(); + + @Test + void 피플_ID로_다음_페이지에_대한_커서를_생성한다() throws JsonProcessingException { + final Slice response = new SliceImpl<>(content, pageable, true); + final String expected = objectMapper.writeValueAsString(new Cursor((long) size)); + + final String cursor = adapter.serializeCursor(response); + + assertThat(cursor).asBase64Decoded() + .asString() + .isEqualTo(expected); + } + + @Test + void 다음_페이지가_없으면_null을_반환한다() { + final Slice response = new SliceImpl<>(content, pageable, false); + + final String cursor = adapter.serializeCursor(response); + + assertThat(cursor).isNull(); + } +} \ No newline at end of file diff --git a/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/project/commission/FindProjectAdapterTest.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/project/commission/FindProjectAdapterTest.java new file mode 100644 index 00000000..4797a7e8 --- /dev/null +++ b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/project/commission/FindProjectAdapterTest.java @@ -0,0 +1,119 @@ +package es.princip.getp.persistence.adapter.project.commission; + +import es.princip.getp.application.project.commission.dto.response.ProjectCardResponse; +import es.princip.getp.application.project.commission.dto.response.ProjectDetailResponse; +import es.princip.getp.application.project.commission.dto.command.ProjectSearchFilter; +import es.princip.getp.domain.member.model.Member; +import es.princip.getp.domain.member.model.MemberId; +import es.princip.getp.domain.member.model.MemberType; +import es.princip.getp.domain.project.commission.model.ProjectId; +import es.princip.getp.fixture.member.MemberFixture; +import es.princip.getp.persistence.adapter.client.ClientDataLoader; +import es.princip.getp.persistence.adapter.member.MemberDataLoader; +import es.princip.getp.persistence.adapter.people.PeopleDataLoader; +import es.princip.getp.persistence.adapter.project.apply.ProjectApplicationDataLoader; +import es.princip.getp.persistence.support.DataLoader; +import es.princip.getp.persistence.support.PersistenceAdapterTest; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +@Slf4j +class FindProjectAdapterTest extends PersistenceAdapterTest { + + private static final int TEST_SIZE = 20; + + @PersistenceContext private EntityManager entityManager; + @Autowired private FindProjectAdapter adapter; + + private List dataLoaders; + + @BeforeEach + void setUp() { + dataLoaders = List.of( + new MemberDataLoader(entityManager), + new PeopleDataLoader(entityManager), + new ClientDataLoader(entityManager), + new ProjectDataLoader(entityManager), + new ProjectApplicationDataLoader(entityManager) + ); + dataLoaders.forEach(dataLoader -> dataLoader.load(TEST_SIZE)); + } + + @AfterEach + void teardown() { + dataLoaders.forEach(DataLoader::teardown); + } + + @Test + void 프로젝트_목록을_조회한다() { + final int pageSize = 10; + final Pageable pageable = PageRequest.of(0, pageSize); + final ProjectSearchFilter filter = new ProjectSearchFilter( + "null", + "null", + "null", + "null" + ); + final Page response = adapter.findBy(pageable, filter, null); + + assertThat(response.getContent()).isNotEmpty(); + } + + @Test + void 의뢰한_프로젝트_목록을_조회한다() { + final int pageSize = 10; + final Pageable pageable = PageRequest.of(0, pageSize); + final ProjectSearchFilter filter = new ProjectSearchFilter( + "null", + "true", + "null", + "null" + ); + final MemberId member = new MemberId(1L); + final Page response = adapter.findBy(pageable, filter, member); + + assertThat(response.getContent()).isNotEmpty(); + } + + @Test + void 지원한_프로젝트_목록을_조회한다() { + final int pageSize = 10; + final Pageable pageable = PageRequest.of(0, pageSize); + final ProjectSearchFilter filter = new ProjectSearchFilter( + "null", + "null", + "true", + "null" + ); + final MemberId member = new MemberId(1L); + final Page response = adapter.findBy(pageable, filter, member); + + assertThat(response.getContent()).isNotEmpty(); + } + + @Test + void 프로젝트_상세_정보를_조회한다() { + final MemberId memberId = new MemberId(1L); + final Member member = spy(MemberFixture.member(MemberType.ROLE_PEOPLE)); + final ProjectId projectId = new ProjectId(1L); + + when(member.getId()).thenReturn(memberId); + final ProjectDetailResponse response = adapter.findBy(member, projectId); + + assertThat(response).isNotNull(); + } +} \ No newline at end of file diff --git a/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/project/commission/ProjectDataLoader.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/project/commission/ProjectDataLoader.java new file mode 100644 index 00000000..293ba024 --- /dev/null +++ b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/project/commission/ProjectDataLoader.java @@ -0,0 +1,62 @@ +package es.princip.getp.persistence.adapter.project.commission; + +import es.princip.getp.domain.project.commission.model.MeetingType; +import es.princip.getp.domain.project.commission.model.ProjectCategory; +import es.princip.getp.domain.project.commission.model.ProjectStatus; +import es.princip.getp.persistence.adapter.common.DurationJpaVO; +import es.princip.getp.persistence.support.DataLoader; +import jakarta.persistence.EntityManager; +import lombok.RequiredArgsConstructor; + +import java.util.List; +import java.util.stream.IntStream; +import java.util.stream.LongStream; + +import static es.princip.getp.fixture.project.ProjectFixture.*; + +@RequiredArgsConstructor +public class ProjectDataLoader implements DataLoader { + + private final EntityManager entityManager; + + @Override + public void load(final int size) { + final List statuses = List.of(ProjectStatus.values()); + final int statusCount = size / statuses.size(); + final List projectList = IntStream.range(0, statuses.size()) + .mapToObj(statusIdx -> LongStream.range(0, statusCount) + .mapToObj(clientId -> project( + statusIdx * statusCount + clientId + 1, + statuses.get(statusIdx) + ))) + .flatMap(s -> s) + .toList(); + projectList.forEach(entityManager::persist); + } + + @Override + public void teardown() { + entityManager.createQuery("DELETE FROM ProjectJpaEntity").executeUpdate(); + entityManager.createNativeQuery("ALTER TABLE project AUTO_INCREMENT = 1") + .executeUpdate(); + } + + private ProjectJpaEntity project(final Long clientId, final ProjectStatus status) { + return ProjectJpaEntity.builder() + .title(TITLE) + .payment(PAYMENT) + .recruitmentCount(RECRUITMENT_COUNT) + .applicationDuration(new DurationJpaVO( + APPLICATION_START_DATE, + APPLICATION_END_DATE)) + .estimatedDuration(new DurationJpaVO( + ESTIMATED_START_DATE, + ESTIMATED_END_DATE)) + .description(DESCRIPTION) + .meetingType(MeetingType.IN_PERSON) + .category(ProjectCategory.BACKEND) + .status(status) + .clientId(clientId) + .build(); + } +} diff --git a/src/test/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermDataLoader.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermDataLoader.java similarity index 100% rename from src/test/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermDataLoader.java rename to get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermDataLoader.java diff --git a/src/test/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermPersistenceAdapterTest.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermPersistenceAdapterTest.java similarity index 95% rename from src/test/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermPersistenceAdapterTest.java rename to get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermPersistenceAdapterTest.java index 9df85db2..c191284f 100644 --- a/src/test/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermPersistenceAdapterTest.java +++ b/get-p-persistence/src/test/java/es/princip/getp/persistence/adapter/serviceTerm/ServiceTermPersistenceAdapterTest.java @@ -1,5 +1,6 @@ package es.princip.getp.persistence.adapter.serviceTerm; +import es.princip.getp.application.serviceTerm.exception.NotFoundServiceTermException; import es.princip.getp.domain.serviceTerm.model.ServiceTermTag; import es.princip.getp.persistence.support.PersistenceAdapterTest; import jakarta.persistence.EntityManager; diff --git a/get-p-persistence/src/test/java/es/princip/getp/persistence/config/ObjectMapperConfig.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/config/ObjectMapperConfig.java new file mode 100644 index 00000000..31acb4b2 --- /dev/null +++ b/get-p-persistence/src/test/java/es/princip/getp/persistence/config/ObjectMapperConfig.java @@ -0,0 +1,15 @@ +package es.princip.getp.persistence.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; + +@TestConfiguration +public class ObjectMapperConfig { + + @Bean + public ObjectMapper objectMapper() { + return new ObjectMapper().registerModule(new JavaTimeModule()); + } +} diff --git a/src/test/java/es/princip/getp/persistence/config/PersistenceMapperTestConfig.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/config/PersistenceMapperTestConfig.java similarity index 100% rename from src/test/java/es/princip/getp/persistence/config/PersistenceMapperTestConfig.java rename to get-p-persistence/src/test/java/es/princip/getp/persistence/config/PersistenceMapperTestConfig.java diff --git a/src/test/java/es/princip/getp/persistence/support/DataLoader.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/support/DataLoader.java similarity index 100% rename from src/test/java/es/princip/getp/persistence/support/DataLoader.java rename to get-p-persistence/src/test/java/es/princip/getp/persistence/support/DataLoader.java diff --git a/src/test/java/es/princip/getp/persistence/support/PersistenceAdapterTest.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/support/PersistenceAdapterTest.java similarity index 60% rename from src/test/java/es/princip/getp/persistence/support/PersistenceAdapterTest.java rename to get-p-persistence/src/test/java/es/princip/getp/persistence/support/PersistenceAdapterTest.java index 3fe0624d..d5769603 100644 --- a/src/test/java/es/princip/getp/persistence/support/PersistenceAdapterTest.java +++ b/get-p-persistence/src/test/java/es/princip/getp/persistence/support/PersistenceAdapterTest.java @@ -1,5 +1,6 @@ package es.princip.getp.persistence.support; +import es.princip.getp.persistence.config.ObjectMapperConfig; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.parallel.Execution; @@ -7,17 +8,23 @@ import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.ComponentScan; -import org.springframework.test.context.ActiveProfiles; +import org.springframework.context.annotation.FilterType; +import org.springframework.context.annotation.Import; @Slf4j @DataJpaTest -@ActiveProfiles("test") +@Import(ObjectMapperConfig.class) @Execution(ExecutionMode.SAME_THREAD) @TestInstance(TestInstance.Lifecycle.PER_CLASS) -@ComponentScan(basePackages = { - "es.princip.getp.domain.like.query.dao", - "es.princip.getp.persistence.adapter" -}) // TODO: DAO를 persistence adapter로 변경할 것. 애플리케이션 컨텍스트 캐싱을 위해 임시로 추가 +@ComponentScan( + basePackages = { + "es.princip.getp.persistence.adapter" + }, + excludeFilters = @ComponentScan.Filter( + type = FilterType.REGEX, + pattern = "es.princip.getp.persistence.adapter.auth.*" + ) +) @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) public abstract class PersistenceAdapterTest { } diff --git a/src/test/java/es/princip/getp/persistence/support/PersistenceMapperTest.java b/get-p-persistence/src/test/java/es/princip/getp/persistence/support/PersistenceMapperTest.java similarity index 100% rename from src/test/java/es/princip/getp/persistence/support/PersistenceMapperTest.java rename to get-p-persistence/src/test/java/es/princip/getp/persistence/support/PersistenceMapperTest.java diff --git a/get-p-persistence/src/test/resources/application.yml b/get-p-persistence/src/test/resources/application.yml new file mode 100644 index 00000000..ca442424 --- /dev/null +++ b/get-p-persistence/src/test/resources/application.yml @@ -0,0 +1,31 @@ +spring: + datasource: + url: ${DB_TEST_URL} + driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver + + flyway: + enabled: true + baseline-on-migrate: true + + jpa: + hibernate: + ddl-auto: validate + properties: + hibernate: + show_sql: true + format_sql: true + highlight_sql: true + use_sql_comments: true + jdbc: + time_zone: Asia/Seoul + batch_size: 100 + default_batch_fetch_size: 20 + dialect: org.hibernate.dialect.MySQLDialect + +logging: + level: + org: + type: + descriptor: + sql: + BasicBinder: TRACE \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index a44e72bd..5dbd9429 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,8 @@ rootProject.name = 'get-p-server' + +include 'get-p-api' +include 'get-p-domain' +include 'get-p-application' +include 'get-p-persistence' +include 'get-p-infrastructure' +include 'get-p-batch' \ No newline at end of file diff --git a/src/docs/asciidoc/auth/login.adoc b/src/docs/asciidoc/auth/login.adoc deleted file mode 100644 index 11fa29c4..00000000 --- a/src/docs/asciidoc/auth/login.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/login/login[snippets="http-request,request-fields,http-response,response-fields-data"] \ No newline at end of file diff --git a/src/docs/asciidoc/auth/reissue-access-token.adoc b/src/docs/asciidoc/auth/reissue-access-token.adoc deleted file mode 100644 index 7b828e0c..00000000 --- a/src/docs/asciidoc/auth/reissue-access-token.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/reissue-access-token/reissue-access-token[snippets="http-request,request-headers,http-response,response-fields-data"] \ No newline at end of file diff --git a/src/docs/asciidoc/auth/send-email-verification-code-for-sign-up.adoc b/src/docs/asciidoc/auth/send-email-verification-code-for-sign-up.adoc deleted file mode 100644 index 2e297715..00000000 --- a/src/docs/asciidoc/auth/send-email-verification-code-for-sign-up.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::send-email-verification-code-for-sign-up/send-email-verification-code-for-sign-up[snippets="http-request,http-response"] \ No newline at end of file diff --git a/src/docs/asciidoc/auth/signup.adoc b/src/docs/asciidoc/auth/signup.adoc deleted file mode 100644 index e80bc4ed..00000000 --- a/src/docs/asciidoc/auth/signup.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/sign-up/sign-up[snippets="http-request,request-fields,http-response"] \ No newline at end of file diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc deleted file mode 100644 index ac61cd4f..00000000 --- a/src/docs/asciidoc/index.adoc +++ /dev/null @@ -1,186 +0,0 @@ -= GET-P API 문서 -:doctype: book -:icons: font -:source-highlighter: highlightjs -:toc: left -:toclevels: 4 -:sectnums: -:sectlinks: -:sectanchors: -:operation-response-fields-data-title: Response fields -:operation-error-code-fields-title: Error codes - -== 인증 -=== 회원 가입 - -사용자는 회원 가입을 할 수 있습니다. - -include::{docdir}/auth/signup.adoc[] - -=== 로그인 - -사용자는 로그인을 할 수 있습니다. - -include::{docdir}/auth/login.adoc[] - -=== 회원 가입 시 이메일 인증 코드 전송 - -사용자는 회원 가입 시 이메일을 인증해야 합니다. - -include::{docdir}/auth/send-email-verification-code-for-sign-up.adoc[] - -=== Access Token 및 Refresh Token 재발급 - -Access Token 및 Refresh Token 재발급 시 기존의 토큰들은 만료 처리가 됩니다. 새로운 토큰을 사용해주세요. - -include::{docdir}/auth/reissue-access-token.adoc[] - -== 사용자 - -=== [회원] 내 회원 정보 조회 - -회원은 자신의 회원 정보를 조회할 수 있습니다. - -include::{docdir}/member/get-my-member.adoc[] - -=== [회원] 내 프로필 사진 등록 - -회원은 자신의 프로필 사진을 등록할 수 있습니다. - -include::{docdir}/member/upload-profile-image.adoc[] - -== 의뢰자 - -=== [의뢰자] 내 의뢰자 정보 등록 - -의뢰자는 자신의 의뢰자 정보를 등록할 수 있습니다. - -include::{docdir}/my-client/register-my-client.adoc[] - -=== [의뢰자] 내 의뢰자 정보 조회 - -의뢰자는 자신의 의뢰자 정보를 조회할 수 있습니다. - -include::{docdir}/my-client/get-my-client.adoc[] - -=== [의뢰자] 내 의뢰자 정보 수정 - -의뢰자는 자신의 의뢰자 정보를 수정할 수 있습니다. - -include::{docdir}/my-client/edit-my-client.adoc[] - -== 피플 - -=== 피플 상세 조회 - -사용자는 피플의 상세 정보를 조회할 수 있습니다. - -include::{docdir}/people/get-people.adoc[] - -=== 피플 목록 조회 - -사용자는 피플 목록을 조회할 수 있습니다. - -include::{docdir}/people/get-card-people-page.adoc[] - -=== [피플] 내 피플 정보 등록 - -피플은 자신의 피플 정보를 등록할 수 있습니다. - -include::{docdir}/my-people/create-my-people.adoc[] - -=== [피플] 내 피플 정보 조회 - -피플은 자신의 피플 정보를 조회할 수 있습니다. - -include::{docdir}/my-people/get-my-people.adoc[] - -=== [피플] 내 피플 정보 수정 - -피플은 자신의 피플 정보를 수정할 수 있습니다. - -include::{docdir}/my-people/update-my-people.adoc[] - -=== [피플] 내 피플 프로필 등록 - -피플은 자신의 프로필을 등록할 수 있습니다. - -include::{docdir}/my-people-profile/write-my-people-profile.adoc[] - -=== [피플] 내 피플 프로필 조회 - -피플은 자신의 프로필을 조회할 수 있습니다. - -include::{docdir}/my-people-profile/get-my-people-profile.adoc[] - -=== [피플] 내 피플 프로필 수정 - -피플은 자신의 프로필을 수정할 수 있습니다. - -include::{docdir}/my-people-profile/edit-my-people-profile.adoc[] - -== 프로젝트 - -=== 프로젝트 상세 조회 - -사용자는 프로젝트의 상세 정보를 조회할 수 있습니다. - -include::{docdir}/project/get-project-by-project-Id.adoc[] - -=== 프로젝트 목록 조회 - -사용자는 프로젝트 목록을 조회할 수 있습니다. - -include::{docdir}/project/get-projects.adoc[] - -=== [피플] 프로젝트 좋아요 - -피플은 마음에 드는 프로젝트에 좋아요를 누를 수 있습니다. - -include::{docdir}/project/like-project.adoc[] - -=== [피플] 프로젝트 좋아요 취소 - -피플은 마음에 드는 프로젝트에 눌렀던 좋아요를 취소할 수 있습니다. - -include::{docdir}/project/unlike-project.adoc[] - -=== [의뢰자] 프로젝트 의뢰 - -의뢰자는 프로젝트를 의뢰할 수 있습니다. - -include::{docdir}/project/commission-project.adoc[] - -=== [피플] 프로젝트 지원 - -피플은 프로젝트에 지원할 수 있습니다. - -include::{docdir}/project/apply-for-project.adoc[] - -== 프로젝트 관리 - -=== [피플] 지원한 프로젝트 목록 조회 - -피플은 자신이 지원한 프로젝트 목록을 조회할 수 있습니다. - -include::{docdir}/project/get-my-applied-projects.adoc[] - -=== [의뢰자] 의뢰한 프로젝트 목록 조회 - -의뢰자는 자신이 의뢰한 프로젝트 목록을 조회할 수 있습니다. - -include::{docdir}/project/get-my-commissioned-projects.adoc[] - -=== [의뢰자] 프로젝트 미팅 신청 - -의뢰자는 프로젝트 지원자에게 미팅을 신청할 수 있습니다. - -include::{docdir}/project/schedule-meeting.adoc[] - -== 스토리지 - -=== [회원] 파일 업로드 - -회원은 파일을 업로드할 수 있습니다. 업로드할 수 있는 파일 확장자는 `pdf`, `zip`, `ppt`, `docx`, `hwp`, `jpg`, `png` 입니다. - -include::{docdir}/storage/upload-file.adoc[] \ No newline at end of file diff --git a/src/docs/asciidoc/member/get-my-member.adoc b/src/docs/asciidoc/member/get-my-member.adoc deleted file mode 100644 index fe2b637c..00000000 --- a/src/docs/asciidoc/member/get-my-member.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/get-my-member/get-my-member[snippets="http-request,request-headers,http-response,response-fields-data"] \ No newline at end of file diff --git a/src/docs/asciidoc/member/upload-profile-image.adoc b/src/docs/asciidoc/member/upload-profile-image.adoc deleted file mode 100644 index d6b691a8..00000000 --- a/src/docs/asciidoc/member/upload-profile-image.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/upload-profile-image/upload-profile-image/[snippets="httpie-request,request-headers,request-parts,http-response,response-fields-data"] \ No newline at end of file diff --git a/src/docs/asciidoc/my-client/edit-my-client.adoc b/src/docs/asciidoc/my-client/edit-my-client.adoc deleted file mode 100644 index f70a4eed..00000000 --- a/src/docs/asciidoc/my-client/edit-my-client.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/edit-my-client/edit-my-client[snippets="http-request,request-headers,request-fields,http-response"] \ No newline at end of file diff --git a/src/docs/asciidoc/my-client/get-my-client.adoc b/src/docs/asciidoc/my-client/get-my-client.adoc deleted file mode 100644 index a2583e8e..00000000 --- a/src/docs/asciidoc/my-client/get-my-client.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/get-my-client/get-my-client[snippets="http-request,request-headers,http-response,response-fields-data"] \ No newline at end of file diff --git a/src/docs/asciidoc/my-client/register-my-client.adoc b/src/docs/asciidoc/my-client/register-my-client.adoc deleted file mode 100644 index 33e833ad..00000000 --- a/src/docs/asciidoc/my-client/register-my-client.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/register-my-client/register-my-client[snippets="http-request,request-headers,request-fields,http-response,response-fields-data"] \ No newline at end of file diff --git a/src/docs/asciidoc/my-people-profile/edit-my-people-profile.adoc b/src/docs/asciidoc/my-people-profile/edit-my-people-profile.adoc deleted file mode 100644 index 0d9ae0a6..00000000 --- a/src/docs/asciidoc/my-people-profile/edit-my-people-profile.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/edit-my-people-profile/edit-my-people-profile[snippets="http-request,request-headers,request-fields,http-response"] \ No newline at end of file diff --git a/src/docs/asciidoc/my-people-profile/get-my-people-profile.adoc b/src/docs/asciidoc/my-people-profile/get-my-people-profile.adoc deleted file mode 100644 index d48a56f4..00000000 --- a/src/docs/asciidoc/my-people-profile/get-my-people-profile.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/get-my-people-profile/get-my-people-profile[snippets="http-request,request-headers,http-response,response-fields-data"] \ No newline at end of file diff --git a/src/docs/asciidoc/my-people-profile/write-my-people-profile.adoc b/src/docs/asciidoc/my-people-profile/write-my-people-profile.adoc deleted file mode 100644 index 617ac120..00000000 --- a/src/docs/asciidoc/my-people-profile/write-my-people-profile.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/write-my-people-profile/write-my-people-profile[snippets="http-request,request-headers,request-fields,http-response"] \ No newline at end of file diff --git a/src/docs/asciidoc/my-people/create-my-people.adoc b/src/docs/asciidoc/my-people/create-my-people.adoc deleted file mode 100644 index 2b64d7de..00000000 --- a/src/docs/asciidoc/my-people/create-my-people.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/create-my-people/create-my-people[snippets="http-request,request-headers,request-fields,http-response"] \ No newline at end of file diff --git a/src/docs/asciidoc/my-people/get-my-people.adoc b/src/docs/asciidoc/my-people/get-my-people.adoc deleted file mode 100644 index 96df4dd7..00000000 --- a/src/docs/asciidoc/my-people/get-my-people.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/get-my-people/get-my-people[snippets="http-request,request-headers,http-response,response-fields-data"] \ No newline at end of file diff --git a/src/docs/asciidoc/my-people/update-my-people.adoc b/src/docs/asciidoc/my-people/update-my-people.adoc deleted file mode 100644 index 8cb38190..00000000 --- a/src/docs/asciidoc/my-people/update-my-people.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/update-my-people/update-my-people[snippets="http-request,request-headers,request-fields,http-response"] \ No newline at end of file diff --git a/src/docs/asciidoc/people/get-card-people-page.adoc b/src/docs/asciidoc/people/get-card-people-page.adoc deleted file mode 100644 index f2464d15..00000000 --- a/src/docs/asciidoc/people/get-card-people-page.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/get-card-people-page/get-card-people-page[snippets="http-request,query-parameters,http-response,response-fields-data"] \ No newline at end of file diff --git a/src/docs/asciidoc/people/get-my-people.adoc b/src/docs/asciidoc/people/get-my-people.adoc deleted file mode 100644 index 96df4dd7..00000000 --- a/src/docs/asciidoc/people/get-my-people.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/get-my-people/get-my-people[snippets="http-request,request-headers,http-response,response-fields-data"] \ No newline at end of file diff --git a/src/docs/asciidoc/people/get-people.adoc b/src/docs/asciidoc/people/get-people.adoc deleted file mode 100644 index 26a0f9db..00000000 --- a/src/docs/asciidoc/people/get-people.adoc +++ /dev/null @@ -1,17 +0,0 @@ -==== HTTP request -include::{snippets}/get-people/get-people_-when-user-logined/http-request.adoc[] - -==== Request headers -include::{snippets}/get-people/get-people_-when-user-logined/request-headers.adoc[] - -==== Path parameters -include::{snippets}/get-people/get-people_-when-user-logined/path-parameters.adoc[] - -==== HTTP response when user logined -include::{snippets}/get-people/get-people_-when-user-logined/http-response.adoc[] - -==== HTTP response when user not logined -include::{snippets}/get-people/get-people_-when-user-not-logined/http-response.adoc[] - -==== Response fields -include::{snippets}/get-people/get-people_-when-user-not-logined/response-fields-data.adoc[] \ No newline at end of file diff --git a/src/docs/asciidoc/project/apply-for-project.adoc b/src/docs/asciidoc/project/apply-for-project.adoc deleted file mode 100644 index 970068ce..00000000 --- a/src/docs/asciidoc/project/apply-for-project.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/apply-for-project/apply-for-project[snippets="http-request,request-headers,path-parameters,request-fields,http-response,response-fields-data"] \ No newline at end of file diff --git a/src/docs/asciidoc/project/commission-project.adoc b/src/docs/asciidoc/project/commission-project.adoc deleted file mode 100644 index 5a54b605..00000000 --- a/src/docs/asciidoc/project/commission-project.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/commission-project/commission-project[snippets="http-request,request-headers,request-fields,http-response,response-fields-data"] \ No newline at end of file diff --git a/src/docs/asciidoc/project/get-my-applied-projects.adoc b/src/docs/asciidoc/project/get-my-applied-projects.adoc deleted file mode 100644 index c7ec821f..00000000 --- a/src/docs/asciidoc/project/get-my-applied-projects.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/get-my-applied-projects/get-my-applied-projects[snippets="http-request,request-headers,query-parameters,http-response,response-fields-data"] \ No newline at end of file diff --git a/src/docs/asciidoc/project/get-my-commissioned-projects.adoc b/src/docs/asciidoc/project/get-my-commissioned-projects.adoc deleted file mode 100644 index 4737508b..00000000 --- a/src/docs/asciidoc/project/get-my-commissioned-projects.adoc +++ /dev/null @@ -1,28 +0,0 @@ -==== HTTP request -include::{snippets}/get-my-commissioned-projects/get-my-commissioned-projects/http-request.adoc[] - -==== Request headers -include::{snippets}/get-my-commissioned-projects/get-my-commissioned-projects/request-headers.adoc[] - -==== Query parameters - -include::{snippets}/get-my-commissioned-projects/get-my-commissioned-projects/query-parameters.adoc[] - -==== Sort parameters -[cols=2*] -|=== -|최신순 정렬 -|`sort=createdAt,desc` - -|높은 가격 순 정렬 -|`sort=payment,desc` - -|마감 임박 순 정렬 -|`sort=applicationDuration,desc` -|=== - -==== HTTP response -include::{snippets}/get-my-commissioned-projects/get-my-commissioned-projects/http-response.adoc[] - -==== Response fields -include::{snippets}/get-my-commissioned-projects/get-my-commissioned-projects/response-fields-data.adoc[] \ No newline at end of file diff --git a/src/docs/asciidoc/project/get-project-by-project-Id.adoc b/src/docs/asciidoc/project/get-project-by-project-Id.adoc deleted file mode 100644 index 460818ee..00000000 --- a/src/docs/asciidoc/project/get-project-by-project-Id.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/get-project-by-project-id/get-project-by-project-id[snippets="http-request,request-headers,path-parameters,http-response,response-fields-data"] \ No newline at end of file diff --git a/src/docs/asciidoc/project/get-projects.adoc b/src/docs/asciidoc/project/get-projects.adoc deleted file mode 100644 index 9107821c..00000000 --- a/src/docs/asciidoc/project/get-projects.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/get-projects/get-projects[snippets="http-request,request-headers,http-response,response-fields-data"] \ No newline at end of file diff --git a/src/docs/asciidoc/project/like-project.adoc b/src/docs/asciidoc/project/like-project.adoc deleted file mode 100644 index 4d278e06..00000000 --- a/src/docs/asciidoc/project/like-project.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/like-project/like-project[snippets="http-request,request-headers,http-response"] \ No newline at end of file diff --git a/src/docs/asciidoc/project/schedule-meeting.adoc b/src/docs/asciidoc/project/schedule-meeting.adoc deleted file mode 100644 index 7887fd78..00000000 --- a/src/docs/asciidoc/project/schedule-meeting.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/schedule-meeting/schedule-meeting[snippets="http-request,request-headers,request-fields,http-response,response-fields-data"] \ No newline at end of file diff --git a/src/docs/asciidoc/project/unlike-project.adoc b/src/docs/asciidoc/project/unlike-project.adoc deleted file mode 100644 index f7e92f85..00000000 --- a/src/docs/asciidoc/project/unlike-project.adoc +++ /dev/null @@ -1,2 +0,0 @@ - -operation::/unlike-project/unlike-project[snippets="http-request,request-headers,http-response"] \ No newline at end of file diff --git a/src/docs/asciidoc/storage/upload-file.adoc b/src/docs/asciidoc/storage/upload-file.adoc deleted file mode 100644 index f680540d..00000000 --- a/src/docs/asciidoc/storage/upload-file.adoc +++ /dev/null @@ -1 +0,0 @@ -operation::/upload-file/upload-file/[snippets="httpie-request,request-headers,request-parts,http-response,response-fields-data"] \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/client/command/dto/response/RegisterMyClientResponse.java b/src/main/java/es/princip/getp/api/controller/client/command/dto/response/RegisterMyClientResponse.java deleted file mode 100644 index 29b5afbf..00000000 --- a/src/main/java/es/princip/getp/api/controller/client/command/dto/response/RegisterMyClientResponse.java +++ /dev/null @@ -1,6 +0,0 @@ -package es.princip.getp.api.controller.client.command.dto.response; - -public record RegisterMyClientResponse( - Long clientId -) { -} diff --git a/src/main/java/es/princip/getp/api/controller/common/dto/HashtagsResponse.java b/src/main/java/es/princip/getp/api/controller/common/dto/HashtagsResponse.java deleted file mode 100644 index fe9b3279..00000000 --- a/src/main/java/es/princip/getp/api/controller/common/dto/HashtagsResponse.java +++ /dev/null @@ -1,41 +0,0 @@ -package es.princip.getp.api.controller.common.dto; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import es.princip.getp.domain.common.model.Hashtag; -import lombok.ToString; - -import java.io.IOException; -import java.util.List; - -@ToString -@JsonSerialize(using = HashtagsResponse.HashtagsResponseSerializer.class) -public class HashtagsResponse { - - private final List hashtags; - - private HashtagsResponse(final List hashtags) { - this.hashtags = hashtags; - } - - public static HashtagsResponse from(final List hashtags) { - return new HashtagsResponse(hashtags); - } - - static class HashtagsResponseSerializer extends JsonSerializer { - - @Override - public void serialize( - final HashtagsResponse response, - final JsonGenerator jsonGenerator, - final SerializerProvider serializerProvider - ) throws IOException { - List urls = response.hashtags.stream() - .map(Hashtag::getValue) - .toList(); - jsonGenerator.writeObject(urls); - } - } -} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/member/command/dto/response/ProfileImageResponse.java b/src/main/java/es/princip/getp/api/controller/member/command/dto/response/ProfileImageResponse.java deleted file mode 100644 index 2ff502d9..00000000 --- a/src/main/java/es/princip/getp/api/controller/member/command/dto/response/ProfileImageResponse.java +++ /dev/null @@ -1,4 +0,0 @@ -package es.princip.getp.api.controller.member.command.dto.response; - -public record ProfileImageResponse(String profileImageUri) { -} diff --git a/src/main/java/es/princip/getp/api/controller/people/command/dto/response/RegisterPeopleResponse.java b/src/main/java/es/princip/getp/api/controller/people/command/dto/response/RegisterPeopleResponse.java deleted file mode 100644 index 2a7616c9..00000000 --- a/src/main/java/es/princip/getp/api/controller/people/command/dto/response/RegisterPeopleResponse.java +++ /dev/null @@ -1,4 +0,0 @@ -package es.princip.getp.api.controller.people.command.dto.response; - -public record RegisterPeopleResponse(Long peopleId) { -} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/people/query/PeopleQueryController.java b/src/main/java/es/princip/getp/api/controller/people/query/PeopleQueryController.java deleted file mode 100644 index a4457526..00000000 --- a/src/main/java/es/princip/getp/api/controller/people/query/PeopleQueryController.java +++ /dev/null @@ -1,57 +0,0 @@ -package es.princip.getp.api.controller.people.query; - -import es.princip.getp.api.support.ControllerSupport; -import es.princip.getp.api.support.dto.ApiResponse; -import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; -import es.princip.getp.api.support.dto.PageResponse; -import es.princip.getp.api.controller.people.query.dto.people.CardPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.people.DetailPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.people.PublicDetailPeopleResponse; -import es.princip.getp.application.people.port.in.GetPeopleQuery; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.web.PageableDefault; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/people") -@RequiredArgsConstructor -public class PeopleQueryController extends ControllerSupport { - - private final GetPeopleQuery getPeopleQuery; - - /** - * 피플 상세 조회 - * - * @param peopleId 피플 ID - * @return 피플 ID에 해당되는 피플 상세 정보 - */ - @GetMapping("/{peopleId}") - public ResponseEntity> getPeople(@PathVariable final Long peopleId) { - if (isAuthenticated()) { - final DetailPeopleResponse response = getPeopleQuery.getDetailById(peopleId); - return ApiResponse.success(HttpStatus.OK, response); - } - final PublicDetailPeopleResponse response = getPeopleQuery.getPublicDetailById(peopleId); - return ApiResponse.success(HttpStatus.OK, response); - } - - /** - * 피플 목록 조회 - * - * @param pageable 정렬 기준 - * @return 정렬 기준에 해당되는 피플 정보 목록 - */ - @GetMapping - public ResponseEntity>> getCardPeoplePage( - @PageableDefault(sort = "peopleId", direction = Sort.Direction.DESC) final Pageable pageable) { - final PageResponse response = PageResponse.from(getPeopleQuery.getPagedCards(pageable)); - return ApiResponse.success(HttpStatus.OK, response); - } -} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/people/query/dto/people/DetailPeopleResponse.java b/src/main/java/es/princip/getp/api/controller/people/query/dto/people/DetailPeopleResponse.java deleted file mode 100644 index e3f1f05c..00000000 --- a/src/main/java/es/princip/getp/api/controller/people/query/dto/people/DetailPeopleResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package es.princip.getp.api.controller.people.query.dto.people; - -import es.princip.getp.api.controller.people.query.dto.peopleProfile.DetailPeopleProfileResponse; -import es.princip.getp.domain.people.model.PeopleType; - -public record DetailPeopleResponse( - Long peopleId, - String nickname, - String profileImageUri, - PeopleType peopleType, - long completedProjectsCount, - long likesCount, - DetailPeopleProfileResponse profile -) { -} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/people/query/dto/people/PublicDetailPeopleResponse.java b/src/main/java/es/princip/getp/api/controller/people/query/dto/people/PublicDetailPeopleResponse.java deleted file mode 100644 index 54b9239d..00000000 --- a/src/main/java/es/princip/getp/api/controller/people/query/dto/people/PublicDetailPeopleResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package es.princip.getp.api.controller.people.query.dto.people; - -import es.princip.getp.api.controller.people.query.dto.peopleProfile.PublicDetailPeopleProfileResponse; -import es.princip.getp.domain.people.model.PeopleType; - -public record PublicDetailPeopleResponse( - Long peopleId, - String nickname, - String profileImageUri, - PeopleType peopleType, - long completedProjectsCount, - long likesCount, - PublicDetailPeopleProfileResponse profile -) { -} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/people/query/dto/peopleProfile/DetailPeopleProfileResponse.java b/src/main/java/es/princip/getp/api/controller/people/query/dto/peopleProfile/DetailPeopleProfileResponse.java deleted file mode 100644 index a910be34..00000000 --- a/src/main/java/es/princip/getp/api/controller/people/query/dto/peopleProfile/DetailPeopleProfileResponse.java +++ /dev/null @@ -1,16 +0,0 @@ -package es.princip.getp.api.controller.people.query.dto.peopleProfile; - -import es.princip.getp.api.controller.common.dto.HashtagsResponse; -import es.princip.getp.domain.people.model.Education; - -import java.util.List; - -public record DetailPeopleProfileResponse( - String introduction, - String activityArea, - Education education, - List techStacks, - HashtagsResponse hashtags, - List portfolios -) { -} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/people/query/dto/peopleProfile/PublicDetailPeopleProfileResponse.java b/src/main/java/es/princip/getp/api/controller/people/query/dto/peopleProfile/PublicDetailPeopleProfileResponse.java deleted file mode 100644 index e546e601..00000000 --- a/src/main/java/es/princip/getp/api/controller/people/query/dto/peopleProfile/PublicDetailPeopleProfileResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package es.princip.getp.api.controller.people.query.dto.peopleProfile; - -import es.princip.getp.api.controller.common.dto.HashtagsResponse; -import es.princip.getp.domain.people.model.PeopleProfile; - -public record PublicDetailPeopleProfileResponse( - HashtagsResponse hashtags -) { - - public static PublicDetailPeopleProfileResponse from(final PeopleProfile profile) { - return new PublicDetailPeopleProfileResponse( - HashtagsResponse.from(profile.getHashtags()) - ); - } -} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/project/command/ProjectCommandMapper.java b/src/main/java/es/princip/getp/api/controller/project/command/ProjectCommandMapper.java deleted file mode 100644 index 09fba4b8..00000000 --- a/src/main/java/es/princip/getp/api/controller/project/command/ProjectCommandMapper.java +++ /dev/null @@ -1,32 +0,0 @@ -package es.princip.getp.api.controller.project.command; - -import es.princip.getp.api.controller.common.mapper.CommandMapper; -import es.princip.getp.api.controller.common.mapper.HashtagMapper; -import es.princip.getp.api.controller.common.mapper.PhoneNumberMapper; -import es.princip.getp.api.controller.common.mapper.URLMapper; -import es.princip.getp.api.controller.people.command.PeopleCommandMapper; -import es.princip.getp.api.controller.project.command.dto.request.ApplyProjectRequest; -import es.princip.getp.api.controller.project.command.dto.request.CommissionProjectRequest; -import es.princip.getp.api.controller.project.command.dto.request.ScheduleMeetingRequest; -import es.princip.getp.application.project.apply.command.ApplyProjectCommand; -import es.princip.getp.application.project.commission.command.CommissionProjectCommand; -import es.princip.getp.application.project.meeting.command.ScheduleMeetingCommand; -import es.princip.getp.domain.common.model.AttachmentFile; -import es.princip.getp.domain.common.model.URL; -import org.mapstruct.Mapper; - -@CommandMapper -@Mapper( - componentModel = "spring", - uses = {URLMapper.class, HashtagMapper.class, PhoneNumberMapper.class, PeopleCommandMapper.class} -) -interface ProjectCommandMapper { - - ApplyProjectCommand mapToCommand(Long memberId, Long projectId, ApplyProjectRequest request); - - AttachmentFile mapToAttachmentFile(URL url); - - CommissionProjectCommand mapToCommand(Long memberId, CommissionProjectRequest request); - - ScheduleMeetingCommand mapToCommand(Long memberId, Long projectId, ScheduleMeetingRequest request); -} diff --git a/src/main/java/es/princip/getp/api/controller/project/command/dto/request/ApplyProjectRequest.java b/src/main/java/es/princip/getp/api/controller/project/command/dto/request/ApplyProjectRequest.java deleted file mode 100644 index 27d928e5..00000000 --- a/src/main/java/es/princip/getp/api/controller/project/command/dto/request/ApplyProjectRequest.java +++ /dev/null @@ -1,13 +0,0 @@ -package es.princip.getp.api.controller.project.command.dto.request; - -import es.princip.getp.domain.common.model.Duration; -import jakarta.validation.constraints.NotNull; - -import java.util.List; - -public record ApplyProjectRequest( - @NotNull Duration expectedDuration, - @NotNull String description, - @NotNull List attachmentFiles -) { -} diff --git a/src/main/java/es/princip/getp/api/controller/project/command/dto/response/ApplyProjectResponse.java b/src/main/java/es/princip/getp/api/controller/project/command/dto/response/ApplyProjectResponse.java deleted file mode 100644 index 34522e5a..00000000 --- a/src/main/java/es/princip/getp/api/controller/project/command/dto/response/ApplyProjectResponse.java +++ /dev/null @@ -1,6 +0,0 @@ -package es.princip.getp.api.controller.project.command.dto.response; - -public record ApplyProjectResponse( - Long applicationId -) { -} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/project/command/dto/response/ScheduleMeetingResponse.java b/src/main/java/es/princip/getp/api/controller/project/command/dto/response/ScheduleMeetingResponse.java deleted file mode 100644 index 081eb15a..00000000 --- a/src/main/java/es/princip/getp/api/controller/project/command/dto/response/ScheduleMeetingResponse.java +++ /dev/null @@ -1,6 +0,0 @@ -package es.princip.getp.api.controller.project.command.dto.response; - -public record ScheduleMeetingResponse( - Long meetingId -) { -} diff --git a/src/main/java/es/princip/getp/api/controller/project/query/AppliedProjectQueryController.java b/src/main/java/es/princip/getp/api/controller/project/query/AppliedProjectQueryController.java deleted file mode 100644 index 30a557eb..00000000 --- a/src/main/java/es/princip/getp/api/controller/project/query/AppliedProjectQueryController.java +++ /dev/null @@ -1,40 +0,0 @@ -package es.princip.getp.api.controller.project.query; - -import es.princip.getp.api.support.dto.ApiResponse; -import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; -import es.princip.getp.api.support.dto.PageResponse; -import es.princip.getp.api.controller.project.query.dto.AppliedProjectCardResponse; -import es.princip.getp.api.security.details.PrincipalDetails; -import es.princip.getp.application.project.apply.port.in.GetAppliedProjectQuery; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.web.PageableDefault; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/people") -@RequiredArgsConstructor -public class AppliedProjectQueryController { - - private final GetAppliedProjectQuery getAppliedProjectQuery; - - @GetMapping("/me/projects") - @PreAuthorize("hasRole('PEOPLE') and isAuthenticated()") - public ResponseEntity>> getMyAppliedProjects( - @PageableDefault(sort = "projectId", direction = Sort.Direction.DESC) final Pageable pageable, - @AuthenticationPrincipal final PrincipalDetails principalDetails - ) { - final Long memberId = principalDetails.getMember().getMemberId(); - final Page page = getAppliedProjectQuery.getPagedCards(memberId, pageable); - final PageResponse response = PageResponse.from(page); - return ApiResponse.success(HttpStatus.OK, response); - } -} diff --git a/src/main/java/es/princip/getp/api/controller/project/query/CommissionedProjectQueryController.java b/src/main/java/es/princip/getp/api/controller/project/query/CommissionedProjectQueryController.java deleted file mode 100644 index 0714fbc4..00000000 --- a/src/main/java/es/princip/getp/api/controller/project/query/CommissionedProjectQueryController.java +++ /dev/null @@ -1,50 +0,0 @@ -package es.princip.getp.api.controller.project.query; - -import es.princip.getp.api.support.dto.ApiResponse; -import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; -import es.princip.getp.api.support.dto.PageResponse; -import es.princip.getp.api.controller.project.query.dto.CommissionedProjectCardResponse; -import es.princip.getp.api.security.details.PrincipalDetails; -import es.princip.getp.application.project.commission.port.in.GetCommissionedProjectQuery; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.web.PageableDefault; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/client/me/projects") -@RequiredArgsConstructor -public class CommissionedProjectQueryController { - - private final GetCommissionedProjectQuery getCommissionedProjectQuery; - - /** - * 의뢰한 프로젝트 목록 조회 - * - * @param pageable 페이지 정보 - * @param principalDetails 로그인 정보 - * @return 의뢰한 프로젝트 목록 - */ - @GetMapping - @PreAuthorize("hasRole('CLIENT') and isAuthenticated()") - public ResponseEntity>> getMyCommissionedProjects( - @PageableDefault(sort = "projectId", direction = Sort.Direction.DESC) final Pageable pageable, - @RequestParam(defaultValue = "false") final Boolean cancelled, // 만료된 프로젝트 보기 여부 - @AuthenticationPrincipal final PrincipalDetails principalDetails - ) { - final Long memberId = principalDetails.getMember().getMemberId(); - final Page page - = getCommissionedProjectQuery.getPagedCards(memberId, cancelled, pageable); - final PageResponse response = PageResponse.from(page); - return ApiResponse.success(HttpStatus.OK, response); - } -} diff --git a/src/main/java/es/princip/getp/api/controller/project/query/ProjectQueryController.java b/src/main/java/es/princip/getp/api/controller/project/query/ProjectQueryController.java deleted file mode 100644 index 4d736d18..00000000 --- a/src/main/java/es/princip/getp/api/controller/project/query/ProjectQueryController.java +++ /dev/null @@ -1,52 +0,0 @@ -package es.princip.getp.api.controller.project.query; - -import es.princip.getp.api.support.dto.ApiResponse; -import es.princip.getp.api.support.dto.ApiResponse.ApiSuccessResult; -import es.princip.getp.api.support.dto.PageResponse; -import es.princip.getp.api.controller.project.query.dto.ProjectCardResponse; -import es.princip.getp.api.controller.project.query.dto.ProjectDetailResponse; -import es.princip.getp.application.project.commission.port.in.GetProjectQuery; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.web.PageableDefault; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/projects") -@RequiredArgsConstructor -public class ProjectQueryController { - - private final GetProjectQuery getProjectQuery; - - /** - * 프로젝트 목록 조회 - * - * @return 프로젝트 목록 - */ - @GetMapping - public ResponseEntity>> getProjects( - @PageableDefault(sort = "projectId", direction = Sort.Direction.DESC) final Pageable pageable) { - final PageResponse response = PageResponse.from(getProjectQuery.getPagedCards(pageable)); - return ApiResponse.success(HttpStatus.OK, response); - } - - /** - * 프로젝트 상세 조회 - * - * @param projectId 프로젝트 ID - * @return 프로젝트 - */ - //TODO: 비로그인 사용자의 경우 특정 필드 내용에 대한 필터 처리가 필요함 - @GetMapping("/{projectId}") - public ResponseEntity> getProjectByProjectId( - @PathVariable final Long projectId) { - final ProjectDetailResponse response = getProjectQuery.getDetailById(projectId); - return ApiResponse.success(HttpStatus.OK, response); - } -} diff --git a/src/main/java/es/princip/getp/api/controller/project/query/dto/AppliedProjectCardResponse.java b/src/main/java/es/princip/getp/api/controller/project/query/dto/AppliedProjectCardResponse.java deleted file mode 100644 index 0b667042..00000000 --- a/src/main/java/es/princip/getp/api/controller/project/query/dto/AppliedProjectCardResponse.java +++ /dev/null @@ -1,33 +0,0 @@ -package es.princip.getp.api.controller.project.query.dto; - -import es.princip.getp.api.controller.common.dto.HashtagsResponse; -import es.princip.getp.domain.common.model.Duration; -import es.princip.getp.domain.project.commission.model.Project; -import es.princip.getp.domain.project.commission.model.ProjectStatus; - -public record AppliedProjectCardResponse( - Long projectId, - String title, - Long payment, - Long applicantsCount, - Long estimatedDays, - Duration applicationDuration, - HashtagsResponse hashtags, - String description, - ProjectStatus status -) { - - public static AppliedProjectCardResponse of(final Project project, final Long applicantsCount) { - return new AppliedProjectCardResponse( - project.getProjectId(), - project.getTitle(), - project.getPayment(), - applicantsCount, - project.getEstimatedDuration().days(), - project.getApplicationDuration(), - HashtagsResponse.from(project.getHashtags()), - project.getDescription(), - project.getStatus() - ); - } -} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/project/query/dto/AttachmentFilesResponse.java b/src/main/java/es/princip/getp/api/controller/project/query/dto/AttachmentFilesResponse.java deleted file mode 100644 index e387bd98..00000000 --- a/src/main/java/es/princip/getp/api/controller/project/query/dto/AttachmentFilesResponse.java +++ /dev/null @@ -1,43 +0,0 @@ -package es.princip.getp.api.controller.project.query.dto; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import es.princip.getp.domain.common.model.AttachmentFile; -import es.princip.getp.domain.common.model.URL; -import lombok.ToString; - -import java.io.IOException; -import java.util.List; - -@JsonSerialize(using = AttachmentFilesResponse.AttachmentFilesResponseSerializer.class) -@ToString -public class AttachmentFilesResponse { - - private final List attachmentFiles; - - private AttachmentFilesResponse(final List attachmentFiles) { - this.attachmentFiles = attachmentFiles; - } - - public static AttachmentFilesResponse from(final List attachmentFiles) { - return new AttachmentFilesResponse(attachmentFiles); - } - - static class AttachmentFilesResponseSerializer extends JsonSerializer { - - @Override - public void serialize( - final AttachmentFilesResponse response, - final JsonGenerator jsonGenerator, - final SerializerProvider serializerProvider - ) throws IOException { - List urls = response.attachmentFiles.stream() - .map(AttachmentFile::getUrl) - .map(URL::getValue) - .toList(); - jsonGenerator.writeObject(urls); - } - } -} diff --git a/src/main/java/es/princip/getp/api/controller/project/query/dto/CommissionedProjectCardResponse.java b/src/main/java/es/princip/getp/api/controller/project/query/dto/CommissionedProjectCardResponse.java deleted file mode 100644 index 662f0db5..00000000 --- a/src/main/java/es/princip/getp/api/controller/project/query/dto/CommissionedProjectCardResponse.java +++ /dev/null @@ -1,33 +0,0 @@ -package es.princip.getp.api.controller.project.query.dto; - -import es.princip.getp.api.controller.common.dto.HashtagsResponse; -import es.princip.getp.domain.common.model.Duration; -import es.princip.getp.domain.project.commission.model.Project; -import es.princip.getp.domain.project.commission.model.ProjectStatus; - -public record CommissionedProjectCardResponse( - Long projectId, - String title, - Long payment, - Long applicantsCount, - Long estimatedDays, - Duration applicationDuration, - HashtagsResponse hashtags, - String description, - ProjectStatus status -) { - - public static CommissionedProjectCardResponse of(final Project project, final Long applicantsCount) { - return new CommissionedProjectCardResponse( - project.getProjectId(), - project.getTitle(), - project.getPayment(), - applicantsCount, - project.getEstimatedDuration().days(), - project.getApplicationDuration(), - HashtagsResponse.from(project.getHashtags()), - project.getDescription(), - project.getStatus() - ); - } -} diff --git a/src/main/java/es/princip/getp/api/controller/project/query/dto/MyProjectSearchOrder.java b/src/main/java/es/princip/getp/api/controller/project/query/dto/MyProjectSearchOrder.java deleted file mode 100644 index 5549b354..00000000 --- a/src/main/java/es/princip/getp/api/controller/project/query/dto/MyProjectSearchOrder.java +++ /dev/null @@ -1,11 +0,0 @@ -package es.princip.getp.api.controller.project.query.dto; - -import es.princip.getp.util.StringUtil; - -public enum MyProjectSearchOrder { - PROJECT_ID, CREATED_AT, PAYMENT, APPLICATION_DURATION; - - public static MyProjectSearchOrder get(final String value) { - return MyProjectSearchOrder.valueOf(StringUtil.camelToSnake(value).toUpperCase()); - } -} diff --git a/src/main/java/es/princip/getp/api/controller/project/query/dto/ProjectClientResponse.java b/src/main/java/es/princip/getp/api/controller/project/query/dto/ProjectClientResponse.java deleted file mode 100644 index 981a06ca..00000000 --- a/src/main/java/es/princip/getp/api/controller/project/query/dto/ProjectClientResponse.java +++ /dev/null @@ -1,10 +0,0 @@ -package es.princip.getp.api.controller.project.query.dto; - -import es.princip.getp.domain.client.model.Address; - -public record ProjectClientResponse( - Long clientId, - String nickname, - Address address -) { -} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/controller/project/query/dto/ProjectDetailResponse.java b/src/main/java/es/princip/getp/api/controller/project/query/dto/ProjectDetailResponse.java deleted file mode 100644 index 950059cf..00000000 --- a/src/main/java/es/princip/getp/api/controller/project/query/dto/ProjectDetailResponse.java +++ /dev/null @@ -1,24 +0,0 @@ -package es.princip.getp.api.controller.project.query.dto; - -import es.princip.getp.api.controller.common.dto.HashtagsResponse; -import es.princip.getp.domain.common.model.Duration; -import es.princip.getp.domain.project.commission.model.MeetingType; -import es.princip.getp.domain.project.commission.model.ProjectCategory; -import es.princip.getp.domain.project.commission.model.ProjectStatus; - -public record ProjectDetailResponse( - Long projectId, - String title, - Long payment, - Duration applicationDuration, - Duration estimatedDuration, - String description, - MeetingType meetingType, - ProjectCategory category, - ProjectStatus status, - AttachmentFilesResponse attachmentFiles, - HashtagsResponse hashtags, - Long likesCount, - ProjectClientResponse client -) { -} diff --git a/src/main/java/es/princip/getp/api/controller/storage/dto/FileUploadResponse.java b/src/main/java/es/princip/getp/api/controller/storage/dto/FileUploadResponse.java deleted file mode 100644 index 157e83f6..00000000 --- a/src/main/java/es/princip/getp/api/controller/storage/dto/FileUploadResponse.java +++ /dev/null @@ -1,6 +0,0 @@ -package es.princip.getp.api.controller.storage.dto; - -import java.net.URI; - -public record FileUploadResponse(URI fileUri) { -} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/api/validation/Enum.java b/src/main/java/es/princip/getp/api/validation/Enum.java deleted file mode 100644 index 7eab1705..00000000 --- a/src/main/java/es/princip/getp/api/validation/Enum.java +++ /dev/null @@ -1,20 +0,0 @@ -package es.princip.getp.api.validation; - -import jakarta.validation.Constraint; -import jakarta.validation.Payload; -import jakarta.validation.constraints.NotNull; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@NotNull(message = "{validation.constraints.Enum.message}") -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.PARAMETER}) -@Constraint(validatedBy = {}) -public @interface Enum { - String message() default "{validation.constraints.Enum.message}"; - Class[] groups() default {}; - Class[] payload() default {}; -} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/application/auth/service/EmailVerificationRepository.java b/src/main/java/es/princip/getp/application/auth/service/EmailVerificationRepository.java deleted file mode 100644 index 839a8d69..00000000 --- a/src/main/java/es/princip/getp/application/auth/service/EmailVerificationRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package es.princip.getp.application.auth.service; - -import org.springframework.data.keyvalue.repository.KeyValueRepository; - -public interface EmailVerificationRepository extends KeyValueRepository { - -} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/application/auth/service/RefreshTokenRepository.java b/src/main/java/es/princip/getp/application/auth/service/RefreshTokenRepository.java deleted file mode 100644 index d851ceec..00000000 --- a/src/main/java/es/princip/getp/application/auth/service/RefreshTokenRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package es.princip.getp.application.auth.service; - -import org.springframework.data.keyvalue.repository.KeyValueRepository; - -public interface RefreshTokenRepository extends KeyValueRepository { - boolean existsByRefreshToken(String refreshToken); -} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/application/client/port/in/RegisterClientUseCase.java b/src/main/java/es/princip/getp/application/client/port/in/RegisterClientUseCase.java deleted file mode 100644 index f6626bdc..00000000 --- a/src/main/java/es/princip/getp/application/client/port/in/RegisterClientUseCase.java +++ /dev/null @@ -1,8 +0,0 @@ -package es.princip.getp.application.client.port.in; - -import es.princip.getp.application.client.command.RegisterClientCommand; - -public interface RegisterClientUseCase { - - Long register(RegisterClientCommand command); -} diff --git a/src/main/java/es/princip/getp/application/client/port/out/CheckClientPort.java b/src/main/java/es/princip/getp/application/client/port/out/CheckClientPort.java deleted file mode 100644 index 7b241a59..00000000 --- a/src/main/java/es/princip/getp/application/client/port/out/CheckClientPort.java +++ /dev/null @@ -1,6 +0,0 @@ -package es.princip.getp.application.client.port.out; - -public interface CheckClientPort { - - boolean existsBy(Long memberId); -} diff --git a/src/main/java/es/princip/getp/application/client/port/out/ClientQuery.java b/src/main/java/es/princip/getp/application/client/port/out/ClientQuery.java deleted file mode 100644 index 602e3b33..00000000 --- a/src/main/java/es/princip/getp/application/client/port/out/ClientQuery.java +++ /dev/null @@ -1,13 +0,0 @@ -package es.princip.getp.application.client.port.out; - -import es.princip.getp.api.controller.client.query.dto.ClientResponse; -import es.princip.getp.api.controller.project.query.dto.ProjectClientResponse; - -public interface ClientQuery { - - ClientResponse findClientById(final Long clientId); - - ClientResponse findClientByMemberId(final Long memberId); - - ProjectClientResponse findProjectClientById(final Long clientId); -} diff --git a/src/main/java/es/princip/getp/application/config/SwaggerConfig.java b/src/main/java/es/princip/getp/application/config/SwaggerConfig.java deleted file mode 100644 index 0615137b..00000000 --- a/src/main/java/es/princip/getp/application/config/SwaggerConfig.java +++ /dev/null @@ -1,37 +0,0 @@ -package es.princip.getp.application.config; - -import io.swagger.v3.oas.models.Components; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.info.Info; -import io.swagger.v3.oas.models.security.SecurityRequirement; -import io.swagger.v3.oas.models.security.SecurityScheme; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class SwaggerConfig { - - @Bean - public OpenAPI openAPI() { - String jwt = "JWT"; - SecurityRequirement securityRequirement = new SecurityRequirement().addList(jwt); - Components components = new Components().addSecuritySchemes(jwt, new SecurityScheme() - .name(jwt) - .type(SecurityScheme.Type.HTTP) - .scheme("Bearer") - .bearerFormat("JWT") - ); - return new OpenAPI() - .components(new Components()) - .info(apiInfo()) - .addSecurityItem(securityRequirement) - .components(components); - } - - private Info apiInfo() { - return new Info() - .title("GET-P API") - .description("GET-P API 명세서") - .version("1.0.0"); - } -} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/application/like/people/port/in/LikePeopleUseCase.java b/src/main/java/es/princip/getp/application/like/people/port/in/LikePeopleUseCase.java deleted file mode 100644 index 95e74730..00000000 --- a/src/main/java/es/princip/getp/application/like/people/port/in/LikePeopleUseCase.java +++ /dev/null @@ -1,6 +0,0 @@ -package es.princip.getp.application.like.people.port.in; - -public interface LikePeopleUseCase { - - void like(Long memberId, Long peopleId); -} diff --git a/src/main/java/es/princip/getp/application/like/people/port/in/UnlikePeopleUseCase.java b/src/main/java/es/princip/getp/application/like/people/port/in/UnlikePeopleUseCase.java deleted file mode 100644 index 8a711660..00000000 --- a/src/main/java/es/princip/getp/application/like/people/port/in/UnlikePeopleUseCase.java +++ /dev/null @@ -1,6 +0,0 @@ -package es.princip.getp.application.like.people.port.in; - -public interface UnlikePeopleUseCase { - - void unlike(Long memberId, Long peopleId); -} diff --git a/src/main/java/es/princip/getp/application/like/people/port/out/CheckPeopleLikePort.java b/src/main/java/es/princip/getp/application/like/people/port/out/CheckPeopleLikePort.java deleted file mode 100644 index 1d9b18c8..00000000 --- a/src/main/java/es/princip/getp/application/like/people/port/out/CheckPeopleLikePort.java +++ /dev/null @@ -1,6 +0,0 @@ -package es.princip.getp.application.like.people.port.out; - -public interface CheckPeopleLikePort { - - boolean existsBy(Long clientId, Long peopleId); -} diff --git a/src/main/java/es/princip/getp/application/like/people/port/out/CountPeopleLikePort.java b/src/main/java/es/princip/getp/application/like/people/port/out/CountPeopleLikePort.java deleted file mode 100644 index b968776a..00000000 --- a/src/main/java/es/princip/getp/application/like/people/port/out/CountPeopleLikePort.java +++ /dev/null @@ -1,10 +0,0 @@ -package es.princip.getp.application.like.people.port.out; - -import java.util.Map; - -public interface CountPeopleLikePort { - - Long countBy(Long peopleId); - - Map countBy(Long... peopleIds); -} diff --git a/src/main/java/es/princip/getp/application/like/people/port/out/LoadPeopleLikePort.java b/src/main/java/es/princip/getp/application/like/people/port/out/LoadPeopleLikePort.java deleted file mode 100644 index 682f8a92..00000000 --- a/src/main/java/es/princip/getp/application/like/people/port/out/LoadPeopleLikePort.java +++ /dev/null @@ -1,8 +0,0 @@ -package es.princip.getp.application.like.people.port.out; - -import es.princip.getp.domain.like.people.model.PeopleLike; - -public interface LoadPeopleLikePort { - - PeopleLike loadBy(Long clientId, Long peopleId); -} diff --git a/src/main/java/es/princip/getp/application/like/project/port/in/LikeProjectUseCase.java b/src/main/java/es/princip/getp/application/like/project/port/in/LikeProjectUseCase.java deleted file mode 100644 index 965d5498..00000000 --- a/src/main/java/es/princip/getp/application/like/project/port/in/LikeProjectUseCase.java +++ /dev/null @@ -1,6 +0,0 @@ -package es.princip.getp.application.like.project.port.in; - -public interface LikeProjectUseCase { - - void like(Long memberId, Long peopleId); -} diff --git a/src/main/java/es/princip/getp/application/like/project/port/in/UnlikeProjectUseCase.java b/src/main/java/es/princip/getp/application/like/project/port/in/UnlikeProjectUseCase.java deleted file mode 100644 index 15b46db1..00000000 --- a/src/main/java/es/princip/getp/application/like/project/port/in/UnlikeProjectUseCase.java +++ /dev/null @@ -1,6 +0,0 @@ -package es.princip.getp.application.like.project.port.in; - -public interface UnlikeProjectUseCase { - - void unlike(Long memberId, Long peopleId); -} diff --git a/src/main/java/es/princip/getp/application/like/project/port/out/CheckProjectLikePort.java b/src/main/java/es/princip/getp/application/like/project/port/out/CheckProjectLikePort.java deleted file mode 100644 index 92e5fa12..00000000 --- a/src/main/java/es/princip/getp/application/like/project/port/out/CheckProjectLikePort.java +++ /dev/null @@ -1,6 +0,0 @@ -package es.princip.getp.application.like.project.port.out; - -public interface CheckProjectLikePort { - - boolean existsBy(Long peopleId, Long projectId); -} diff --git a/src/main/java/es/princip/getp/application/like/project/port/out/CountProjectLikePort.java b/src/main/java/es/princip/getp/application/like/project/port/out/CountProjectLikePort.java deleted file mode 100644 index f3cf27bd..00000000 --- a/src/main/java/es/princip/getp/application/like/project/port/out/CountProjectLikePort.java +++ /dev/null @@ -1,10 +0,0 @@ -package es.princip.getp.application.like.project.port.out; - -import java.util.Map; - -public interface CountProjectLikePort { - - Long countBy(Long projectId); - - Map countBy(Long... projectIds); -} diff --git a/src/main/java/es/princip/getp/application/like/project/port/out/LoadProjectLikePort.java b/src/main/java/es/princip/getp/application/like/project/port/out/LoadProjectLikePort.java deleted file mode 100644 index 3d24beab..00000000 --- a/src/main/java/es/princip/getp/application/like/project/port/out/LoadProjectLikePort.java +++ /dev/null @@ -1,8 +0,0 @@ -package es.princip.getp.application.like.project.port.out; - -import es.princip.getp.domain.like.project.model.ProjectLike; - -public interface LoadProjectLikePort { - - ProjectLike loadBy(Long peopleId, Long projectId); -} diff --git a/src/main/java/es/princip/getp/application/mail/SendMailService.java b/src/main/java/es/princip/getp/application/mail/SendMailService.java deleted file mode 100644 index 0a1e87dc..00000000 --- a/src/main/java/es/princip/getp/application/mail/SendMailService.java +++ /dev/null @@ -1,30 +0,0 @@ -package es.princip.getp.application.mail; - -import es.princip.getp.application.mail.command.SendMailCommand; -import es.princip.getp.application.mail.port.in.SendMailUseCase; -import lombok.RequiredArgsConstructor; -import org.springframework.mail.MailSender; -import org.springframework.mail.SimpleMailMessage; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -class SendMailService implements SendMailUseCase { - - private final MailSender mailSender; - - @Async - public void send(final SendMailCommand command) { - final SimpleMailMessage message = from(command); - mailSender.send(message); - } - - private static SimpleMailMessage from(final SendMailCommand command) { - final SimpleMailMessage message = new SimpleMailMessage(); - message.setTo(command.email().getValue()); - message.setSubject(command.title()); - message.setText(command.text()); - return message; - } -} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/application/member/command/RegisterProfileImageCommand.java b/src/main/java/es/princip/getp/application/member/command/RegisterProfileImageCommand.java deleted file mode 100644 index 25493832..00000000 --- a/src/main/java/es/princip/getp/application/member/command/RegisterProfileImageCommand.java +++ /dev/null @@ -1,9 +0,0 @@ -package es.princip.getp.application.member.command; - -import org.springframework.web.multipart.MultipartFile; - -public record RegisterProfileImageCommand( - Long memberId, - MultipartFile image -) { -} diff --git a/src/main/java/es/princip/getp/application/people/mapper/PeopleMapper.java b/src/main/java/es/princip/getp/application/people/mapper/PeopleMapper.java deleted file mode 100644 index 53a5d142..00000000 --- a/src/main/java/es/princip/getp/application/people/mapper/PeopleMapper.java +++ /dev/null @@ -1,17 +0,0 @@ -package es.princip.getp.application.people.mapper; - -import es.princip.getp.domain.common.model.Email; -import es.princip.getp.domain.people.model.People; -import es.princip.getp.domain.people.model.PeopleInfo; -import es.princip.getp.domain.people.model.PeopleType; -import org.mapstruct.Mapper; - -@Mapper(componentModel = "spring") -public interface PeopleMapper { - - default People mapToPeople(Long memberId, Email email, PeopleType peopleType) { - return People.of(memberId, mapToInfo(email, peopleType)); - } - - PeopleInfo mapToInfo(Email email, PeopleType peopleType); -} diff --git a/src/main/java/es/princip/getp/application/people/port/in/GetMyPeopleQuery.java b/src/main/java/es/princip/getp/application/people/port/in/GetMyPeopleQuery.java deleted file mode 100644 index 36312c3e..00000000 --- a/src/main/java/es/princip/getp/application/people/port/in/GetMyPeopleQuery.java +++ /dev/null @@ -1,11 +0,0 @@ -package es.princip.getp.application.people.port.in; - -import es.princip.getp.api.controller.people.query.dto.people.MyPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.peopleProfile.DetailPeopleProfileResponse; - -public interface GetMyPeopleQuery { - - MyPeopleResponse getByMemberId(Long memberId); - - DetailPeopleProfileResponse getDetailProfileByMemberId(Long memberId); -} diff --git a/src/main/java/es/princip/getp/application/people/port/in/GetPeopleQuery.java b/src/main/java/es/princip/getp/application/people/port/in/GetPeopleQuery.java deleted file mode 100644 index ad2b7d91..00000000 --- a/src/main/java/es/princip/getp/application/people/port/in/GetPeopleQuery.java +++ /dev/null @@ -1,16 +0,0 @@ -package es.princip.getp.application.people.port.in; - -import es.princip.getp.api.controller.people.query.dto.people.CardPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.people.DetailPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.people.PublicDetailPeopleResponse; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; - -public interface GetPeopleQuery { - - Page getPagedCards(Pageable pageable); - - DetailPeopleResponse getDetailById(Long peopleId); - - PublicDetailPeopleResponse getPublicDetailById(Long peopleId); -} diff --git a/src/main/java/es/princip/getp/application/people/port/in/RegisterPeopleUseCase.java b/src/main/java/es/princip/getp/application/people/port/in/RegisterPeopleUseCase.java deleted file mode 100644 index dd63395c..00000000 --- a/src/main/java/es/princip/getp/application/people/port/in/RegisterPeopleUseCase.java +++ /dev/null @@ -1,8 +0,0 @@ -package es.princip.getp.application.people.port.in; - -import es.princip.getp.application.people.command.RegisterPeopleCommand; - -public interface RegisterPeopleUseCase { - - Long register(RegisterPeopleCommand command); -} diff --git a/src/main/java/es/princip/getp/application/people/port/out/CheckPeoplePort.java b/src/main/java/es/princip/getp/application/people/port/out/CheckPeoplePort.java deleted file mode 100644 index b68ed4d7..00000000 --- a/src/main/java/es/princip/getp/application/people/port/out/CheckPeoplePort.java +++ /dev/null @@ -1,6 +0,0 @@ -package es.princip.getp.application.people.port.out; - -public interface CheckPeoplePort { - - boolean existsBy(Long memberId); -} diff --git a/src/main/java/es/princip/getp/application/people/port/out/FindMyPeoplePort.java b/src/main/java/es/princip/getp/application/people/port/out/FindMyPeoplePort.java deleted file mode 100644 index 22f5766e..00000000 --- a/src/main/java/es/princip/getp/application/people/port/out/FindMyPeoplePort.java +++ /dev/null @@ -1,11 +0,0 @@ -package es.princip.getp.application.people.port.out; - -import es.princip.getp.api.controller.people.query.dto.people.MyPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.peopleProfile.DetailPeopleProfileResponse; - -public interface FindMyPeoplePort { - - MyPeopleResponse findBy(Long memberId); - - DetailPeopleProfileResponse findDetailProfileBy(Long memberId); -} diff --git a/src/main/java/es/princip/getp/application/people/port/out/FindPeoplePort.java b/src/main/java/es/princip/getp/application/people/port/out/FindPeoplePort.java deleted file mode 100644 index c5694066..00000000 --- a/src/main/java/es/princip/getp/application/people/port/out/FindPeoplePort.java +++ /dev/null @@ -1,16 +0,0 @@ -package es.princip.getp.application.people.port.out; - -import es.princip.getp.api.controller.people.query.dto.people.CardPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.people.DetailPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.people.PublicDetailPeopleResponse; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; - -public interface FindPeoplePort { - - Page findCardBy(Pageable pageable); - - DetailPeopleResponse findDetailBy(Long peopleId); - - PublicDetailPeopleResponse findPublicDetailBy(Long peopleId); -} diff --git a/src/main/java/es/princip/getp/application/people/port/out/LoadPeoplePort.java b/src/main/java/es/princip/getp/application/people/port/out/LoadPeoplePort.java deleted file mode 100644 index c11a0eae..00000000 --- a/src/main/java/es/princip/getp/application/people/port/out/LoadPeoplePort.java +++ /dev/null @@ -1,9 +0,0 @@ -package es.princip.getp.application.people.port.out; - -import es.princip.getp.domain.people.model.People; - -public interface LoadPeoplePort { - - People loadBy(Long memberId); - People loadByPeopleId(Long peopleId); -} diff --git a/src/main/java/es/princip/getp/application/people/service/GetPeopleService.java b/src/main/java/es/princip/getp/application/people/service/GetPeopleService.java deleted file mode 100644 index 164b9de0..00000000 --- a/src/main/java/es/princip/getp/application/people/service/GetPeopleService.java +++ /dev/null @@ -1,35 +0,0 @@ -package es.princip.getp.application.people.service; - -import es.princip.getp.api.controller.people.query.dto.people.CardPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.people.DetailPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.people.PublicDetailPeopleResponse; -import es.princip.getp.application.people.port.in.GetPeopleQuery; -import es.princip.getp.application.people.port.out.FindPeoplePort; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class GetPeopleService implements GetPeopleQuery { - - private final FindPeoplePort findPeoplePort; - - @Override - public Page getPagedCards(final Pageable pageable) { - return findPeoplePort.findCardBy(pageable); - } - - @Override - public DetailPeopleResponse getDetailById(final Long peopleId) { - return findPeoplePort.findDetailBy(peopleId); - } - - @Override - public PublicDetailPeopleResponse getPublicDetailById(final Long peopleId) { - return findPeoplePort.findPublicDetailBy(peopleId); - } -} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/application/project/apply/GetAppliedProjectService.java b/src/main/java/es/princip/getp/application/project/apply/GetAppliedProjectService.java deleted file mode 100644 index a7ab6002..00000000 --- a/src/main/java/es/princip/getp/application/project/apply/GetAppliedProjectService.java +++ /dev/null @@ -1,23 +0,0 @@ -package es.princip.getp.application.project.apply; - -import es.princip.getp.api.controller.project.query.dto.AppliedProjectCardResponse; -import es.princip.getp.application.project.apply.port.in.GetAppliedProjectQuery; -import es.princip.getp.application.project.apply.port.out.FindAppliedProjectPort; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -class GetAppliedProjectService implements GetAppliedProjectQuery { - - private final FindAppliedProjectPort findAppliedProjectPort; - - @Override - public Page getPagedCards(final Long memberId, final Pageable pageable) { - return findAppliedProjectPort.findBy(memberId, pageable); - } -} diff --git a/src/main/java/es/princip/getp/application/project/apply/ProjectApplicationService.java b/src/main/java/es/princip/getp/application/project/apply/ProjectApplicationService.java deleted file mode 100644 index 8c65ae39..00000000 --- a/src/main/java/es/princip/getp/application/project/apply/ProjectApplicationService.java +++ /dev/null @@ -1,55 +0,0 @@ -package es.princip.getp.application.project.apply; - -import es.princip.getp.application.people.port.out.LoadPeoplePort; -import es.princip.getp.application.project.apply.command.ApplyProjectCommand; -import es.princip.getp.application.project.apply.exception.AlreadyAppliedProjectException; -import es.princip.getp.application.project.apply.port.in.ApplyProjectUseCase; -import es.princip.getp.application.project.apply.port.out.CheckProjectApplicationPort; -import es.princip.getp.application.project.apply.port.out.SaveProjectApplicationPort; -import es.princip.getp.application.project.commission.port.out.LoadProjectPort; -import es.princip.getp.domain.people.model.People; -import es.princip.getp.domain.project.apply.model.ProjectApplication; -import es.princip.getp.domain.project.apply.service.ProjectApplier; -import es.princip.getp.domain.project.commission.model.Project; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -class ProjectApplicationService implements ApplyProjectUseCase { - - private final LoadProjectPort loadProjectPort; - private final CheckProjectApplicationPort checkProjectApplicationPort; - private final SaveProjectApplicationPort saveProjectApplicationPort; - private final LoadPeoplePort loadPeoplePort; - - private final ProjectApplier projectApplier; - - /** - * 프로젝트 지원 - * - * @param command 프로젝트 지원 명령 - * @return 프로젝트 지원 ID - */ - @Override - @Transactional - public Long apply(final ApplyProjectCommand command) { - final People applicant = loadPeoplePort.loadBy(command.memberId()); - final Long applicantId = applicant.getId(); - final Long projectId = command.projectId(); - final Project project = loadProjectPort.loadBy(command.projectId()); - if (checkProjectApplicationPort.existsBy(applicantId, projectId)) { - throw new AlreadyAppliedProjectException(); - } - final ProjectApplication application = projectApplier.applyForProject( - applicant, - project, - command.expectedDuration(), - command.description(), - command.attachmentFiles() - ); - return saveProjectApplicationPort.save(application); - } -} diff --git a/src/main/java/es/princip/getp/application/project/apply/command/ApplyProjectCommand.java b/src/main/java/es/princip/getp/application/project/apply/command/ApplyProjectCommand.java deleted file mode 100644 index c53b1114..00000000 --- a/src/main/java/es/princip/getp/application/project/apply/command/ApplyProjectCommand.java +++ /dev/null @@ -1,15 +0,0 @@ -package es.princip.getp.application.project.apply.command; - -import es.princip.getp.domain.common.model.AttachmentFile; -import es.princip.getp.domain.common.model.Duration; - -import java.util.List; - -public record ApplyProjectCommand( - Long memberId, - Long projectId, - Duration expectedDuration, - String description, - List attachmentFiles -) { -} diff --git a/src/main/java/es/princip/getp/application/project/apply/port/in/ApplyProjectUseCase.java b/src/main/java/es/princip/getp/application/project/apply/port/in/ApplyProjectUseCase.java deleted file mode 100644 index 1034eeb3..00000000 --- a/src/main/java/es/princip/getp/application/project/apply/port/in/ApplyProjectUseCase.java +++ /dev/null @@ -1,8 +0,0 @@ -package es.princip.getp.application.project.apply.port.in; - -import es.princip.getp.application.project.apply.command.ApplyProjectCommand; - -public interface ApplyProjectUseCase { - - Long apply(final ApplyProjectCommand command); -} diff --git a/src/main/java/es/princip/getp/application/project/apply/port/in/GetAppliedProjectQuery.java b/src/main/java/es/princip/getp/application/project/apply/port/in/GetAppliedProjectQuery.java deleted file mode 100644 index b2141c10..00000000 --- a/src/main/java/es/princip/getp/application/project/apply/port/in/GetAppliedProjectQuery.java +++ /dev/null @@ -1,10 +0,0 @@ -package es.princip.getp.application.project.apply.port.in; - -import es.princip.getp.api.controller.project.query.dto.AppliedProjectCardResponse; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; - -public interface GetAppliedProjectQuery { - - Page getPagedCards(Long memberId, Pageable pageable); -} diff --git a/src/main/java/es/princip/getp/application/project/apply/port/out/CheckProjectApplicationPort.java b/src/main/java/es/princip/getp/application/project/apply/port/out/CheckProjectApplicationPort.java deleted file mode 100644 index ab7108c5..00000000 --- a/src/main/java/es/princip/getp/application/project/apply/port/out/CheckProjectApplicationPort.java +++ /dev/null @@ -1,6 +0,0 @@ -package es.princip.getp.application.project.apply.port.out; - -public interface CheckProjectApplicationPort { - - boolean existsBy(Long applicantId, Long projectId); -} diff --git a/src/main/java/es/princip/getp/application/project/apply/port/out/FindAppliedProjectPort.java b/src/main/java/es/princip/getp/application/project/apply/port/out/FindAppliedProjectPort.java deleted file mode 100644 index b2c572b5..00000000 --- a/src/main/java/es/princip/getp/application/project/apply/port/out/FindAppliedProjectPort.java +++ /dev/null @@ -1,10 +0,0 @@ -package es.princip.getp.application.project.apply.port.out; - -import es.princip.getp.api.controller.project.query.dto.AppliedProjectCardResponse; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; - -public interface FindAppliedProjectPort { - - Page findBy(Long memberId, Pageable pageable); -} diff --git a/src/main/java/es/princip/getp/application/project/apply/port/out/FindProjectApplicantPort.java b/src/main/java/es/princip/getp/application/project/apply/port/out/FindProjectApplicantPort.java deleted file mode 100644 index 6deadb44..00000000 --- a/src/main/java/es/princip/getp/application/project/apply/port/out/FindProjectApplicantPort.java +++ /dev/null @@ -1,10 +0,0 @@ -package es.princip.getp.application.project.apply.port.out; - -import es.princip.getp.api.controller.people.query.dto.people.DetailPeopleResponse; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; - -public interface FindProjectApplicantPort { - - Page findBy(Long projectId, Pageable pageable); -} diff --git a/src/main/java/es/princip/getp/application/project/commission/GetCommissionedProjectService.java b/src/main/java/es/princip/getp/application/project/commission/GetCommissionedProjectService.java deleted file mode 100644 index 66452468..00000000 --- a/src/main/java/es/princip/getp/application/project/commission/GetCommissionedProjectService.java +++ /dev/null @@ -1,27 +0,0 @@ -package es.princip.getp.application.project.commission; - -import es.princip.getp.api.controller.project.query.dto.CommissionedProjectCardResponse; -import es.princip.getp.application.project.commission.port.in.GetCommissionedProjectQuery; -import es.princip.getp.application.project.commission.port.out.FindCommissionedProjectPort; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class GetCommissionedProjectService implements GetCommissionedProjectQuery { - - private final FindCommissionedProjectPort findCommissionedProjectPort; - - @Override - public Page getPagedCards( - final Long memberId, - final Boolean cancelled, - final Pageable pageable - ) { - return findCommissionedProjectPort.findBy(memberId, cancelled, pageable); - } -} diff --git a/src/main/java/es/princip/getp/application/project/commission/GetProjectService.java b/src/main/java/es/princip/getp/application/project/commission/GetProjectService.java deleted file mode 100644 index 2f0f6dc4..00000000 --- a/src/main/java/es/princip/getp/application/project/commission/GetProjectService.java +++ /dev/null @@ -1,29 +0,0 @@ -package es.princip.getp.application.project.commission; - -import es.princip.getp.api.controller.project.query.dto.ProjectCardResponse; -import es.princip.getp.api.controller.project.query.dto.ProjectDetailResponse; -import es.princip.getp.application.project.commission.port.in.GetProjectQuery; -import es.princip.getp.application.project.commission.port.out.FindProjectPort; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class GetProjectService implements GetProjectQuery { - - private final FindProjectPort findProjectPort; - - @Override - public Page getPagedCards(final Pageable pageable) { - return findProjectPort.findBy(pageable); - } - - @Override - public ProjectDetailResponse getDetailById(final Long projectId) { - return findProjectPort.findBy(projectId); - } -} diff --git a/src/main/java/es/princip/getp/application/project/commission/ProjectDataMapper.java b/src/main/java/es/princip/getp/application/project/commission/ProjectDataMapper.java deleted file mode 100644 index 26b9e2f0..00000000 --- a/src/main/java/es/princip/getp/application/project/commission/ProjectDataMapper.java +++ /dev/null @@ -1,11 +0,0 @@ -package es.princip.getp.application.project.commission; - -import es.princip.getp.application.project.commission.command.CommissionProjectCommand; -import es.princip.getp.domain.project.commission.model.ProjectData; -import org.mapstruct.Mapper; - -@Mapper(componentModel = "spring") -interface ProjectDataMapper { - - ProjectData mapToData(Long clientId, CommissionProjectCommand command); -} diff --git a/src/main/java/es/princip/getp/application/project/commission/port/in/CommissionProjectUseCase.java b/src/main/java/es/princip/getp/application/project/commission/port/in/CommissionProjectUseCase.java deleted file mode 100644 index c5daa07b..00000000 --- a/src/main/java/es/princip/getp/application/project/commission/port/in/CommissionProjectUseCase.java +++ /dev/null @@ -1,8 +0,0 @@ -package es.princip.getp.application.project.commission.port.in; - -import es.princip.getp.application.project.commission.command.CommissionProjectCommand; - -public interface CommissionProjectUseCase { - - Long commission(CommissionProjectCommand command); -} diff --git a/src/main/java/es/princip/getp/application/project/commission/port/in/GetCommissionedProjectQuery.java b/src/main/java/es/princip/getp/application/project/commission/port/in/GetCommissionedProjectQuery.java deleted file mode 100644 index fa8d615e..00000000 --- a/src/main/java/es/princip/getp/application/project/commission/port/in/GetCommissionedProjectQuery.java +++ /dev/null @@ -1,18 +0,0 @@ -package es.princip.getp.application.project.commission.port.in; - -import es.princip.getp.api.controller.project.query.dto.CommissionedProjectCardResponse; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; - -public interface GetCommissionedProjectQuery { - - /** - * 의뢰자가 의뢰한 프로젝트 목록을 페이징하여 조회한다. - * - * @param pageable 페이지 정보 - * @param memberId 의뢰자의 회원 ID - * @return 의뢰자가 의뢰한 프로젝트 목록 페이지 - */ - // TODO: 검색 조건이 아직 명확하지 않음. 추후 변경 필요 - Page getPagedCards(Long memberId, Boolean cancelled, Pageable pageable); -} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/application/project/commission/port/in/GetProjectQuery.java b/src/main/java/es/princip/getp/application/project/commission/port/in/GetProjectQuery.java deleted file mode 100644 index d2a7acca..00000000 --- a/src/main/java/es/princip/getp/application/project/commission/port/in/GetProjectQuery.java +++ /dev/null @@ -1,13 +0,0 @@ -package es.princip.getp.application.project.commission.port.in; - -import es.princip.getp.api.controller.project.query.dto.ProjectCardResponse; -import es.princip.getp.api.controller.project.query.dto.ProjectDetailResponse; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; - -public interface GetProjectQuery { - - Page getPagedCards(Pageable pageable); - - ProjectDetailResponse getDetailById(Long projectId); -} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/application/project/commission/port/out/CheckProjectPort.java b/src/main/java/es/princip/getp/application/project/commission/port/out/CheckProjectPort.java deleted file mode 100644 index 245be511..00000000 --- a/src/main/java/es/princip/getp/application/project/commission/port/out/CheckProjectPort.java +++ /dev/null @@ -1,5 +0,0 @@ -package es.princip.getp.application.project.commission.port.out; - -public interface CheckProjectPort { - -} diff --git a/src/main/java/es/princip/getp/application/project/commission/port/out/FindCommissionedProjectPort.java b/src/main/java/es/princip/getp/application/project/commission/port/out/FindCommissionedProjectPort.java deleted file mode 100644 index cedb4b41..00000000 --- a/src/main/java/es/princip/getp/application/project/commission/port/out/FindCommissionedProjectPort.java +++ /dev/null @@ -1,10 +0,0 @@ -package es.princip.getp.application.project.commission.port.out; - -import es.princip.getp.api.controller.project.query.dto.CommissionedProjectCardResponse; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; - -public interface FindCommissionedProjectPort { - - Page findBy(Long memberId, Boolean cancelled, Pageable pageable); -} diff --git a/src/main/java/es/princip/getp/application/project/commission/port/out/FindProjectPort.java b/src/main/java/es/princip/getp/application/project/commission/port/out/FindProjectPort.java deleted file mode 100644 index 2094f7a0..00000000 --- a/src/main/java/es/princip/getp/application/project/commission/port/out/FindProjectPort.java +++ /dev/null @@ -1,13 +0,0 @@ -package es.princip.getp.application.project.commission.port.out; - -import es.princip.getp.api.controller.project.query.dto.ProjectCardResponse; -import es.princip.getp.api.controller.project.query.dto.ProjectDetailResponse; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; - -public interface FindProjectPort { - - Page findBy(Pageable pageable); - - ProjectDetailResponse findBy(Long projectId); -} diff --git a/src/main/java/es/princip/getp/application/project/meeting/command/ScheduleMeetingCommand.java b/src/main/java/es/princip/getp/application/project/meeting/command/ScheduleMeetingCommand.java deleted file mode 100644 index 402b25f1..00000000 --- a/src/main/java/es/princip/getp/application/project/meeting/command/ScheduleMeetingCommand.java +++ /dev/null @@ -1,15 +0,0 @@ -package es.princip.getp.application.project.meeting.command; - -import es.princip.getp.domain.common.model.MeetingSchedule; -import es.princip.getp.domain.common.model.PhoneNumber; - -public record ScheduleMeetingCommand( - Long memberId, - Long projectId, - Long applicantId, - String location, - MeetingSchedule schedule, - PhoneNumber phoneNumber, - String description -) { -} diff --git a/src/main/java/es/princip/getp/application/project/meeting/port/out/CheckProjectMeetingPort.java b/src/main/java/es/princip/getp/application/project/meeting/port/out/CheckProjectMeetingPort.java deleted file mode 100644 index d306926c..00000000 --- a/src/main/java/es/princip/getp/application/project/meeting/port/out/CheckProjectMeetingPort.java +++ /dev/null @@ -1,6 +0,0 @@ -package es.princip.getp.application.project.meeting.port.out; - -public interface CheckProjectMeetingPort { - - boolean existsApplicantByProjectIdAndMemberId(Long memberId, Long projectId); -} diff --git a/src/main/java/es/princip/getp/application/storage/command/UploadFileCommand.java b/src/main/java/es/princip/getp/application/storage/command/UploadFileCommand.java deleted file mode 100644 index 91750c06..00000000 --- a/src/main/java/es/princip/getp/application/storage/command/UploadFileCommand.java +++ /dev/null @@ -1,9 +0,0 @@ -package es.princip.getp.application.storage.command; - -import org.springframework.web.multipart.MultipartFile; - -public record UploadFileCommand( - Long memberId, - MultipartFile file -) { -} diff --git a/src/main/java/es/princip/getp/application/storage/port/out/ImageStorage.java b/src/main/java/es/princip/getp/application/storage/port/out/ImageStorage.java deleted file mode 100644 index cd8a3859..00000000 --- a/src/main/java/es/princip/getp/application/storage/port/out/ImageStorage.java +++ /dev/null @@ -1,13 +0,0 @@ -package es.princip.getp.application.storage.port.out; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.nio.file.Path; - -public interface ImageStorage { - - URI storeImage(Path destination, InputStream imageStream) throws IOException; - - void deleteImage(URI destination); -} diff --git a/src/main/java/es/princip/getp/domain/people/model/PeopleOrder.java b/src/main/java/es/princip/getp/domain/people/model/PeopleOrder.java deleted file mode 100644 index 9ea06a07..00000000 --- a/src/main/java/es/princip/getp/domain/people/model/PeopleOrder.java +++ /dev/null @@ -1,22 +0,0 @@ -package es.princip.getp.domain.people.model; - -import com.fasterxml.jackson.annotation.JsonCreator; -import es.princip.getp.util.StringUtil; - -import java.util.stream.Stream; - -public enum PeopleOrder { - PEOPLE_ID, INTEREST_COUNT, CREATED_AT; - - @JsonCreator - public static PeopleOrder parsing(String inputValue) { - return Stream.of(PeopleOrder.values()) - .filter(peopleOrder -> peopleOrder.toString().equals(StringUtil.camelToSnake(inputValue).toUpperCase())) - .findFirst() - .orElse(null); - } - - public static PeopleOrder get(String value) { - return PeopleOrder.valueOf(StringUtil.camelToSnake(value).toUpperCase()); - } -} diff --git a/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplication.java b/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplication.java deleted file mode 100644 index 46199257..00000000 --- a/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplication.java +++ /dev/null @@ -1,53 +0,0 @@ -package es.princip.getp.domain.project.apply.model; - -import es.princip.getp.domain.support.BaseEntity; -import es.princip.getp.domain.common.model.AttachmentFile; -import es.princip.getp.domain.common.model.Duration; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import lombok.Builder; -import lombok.Getter; - -import java.time.LocalDateTime; -import java.util.List; - -@Getter -public class ProjectApplication extends BaseEntity { - - private Long applicationId; - @NotNull private final Long applicantId; - @NotNull private final Long projectId; - @NotNull private Duration expectedDuration; - @NotNull private ProjectApplicationStatus applicationStatus; - @NotBlank private String description; - private final List<@NotNull AttachmentFile> attachmentFiles; - - @Builder - public ProjectApplication( - final Long applicationId, - final Long applicantId, - final Long projectId, - final Duration expectedDuration, - final ProjectApplicationStatus applicationStatus, - final String description, - final List attachmentFiles, - final LocalDateTime createdAt, - final LocalDateTime updatedAt - ) { - super(createdAt, updatedAt); - - this.applicationId = applicationId; - this.applicantId = applicantId; - this.projectId = projectId; - this.expectedDuration = expectedDuration; - this.applicationStatus = applicationStatus; - this.description = description; - this.attachmentFiles = attachmentFiles; - - validate(); - } - - public void setStatus(final ProjectApplicationStatus applicationStatus) { - this.applicationStatus = applicationStatus; - } -} diff --git a/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplicationId.java b/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplicationId.java deleted file mode 100644 index 97b83a24..00000000 --- a/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplicationId.java +++ /dev/null @@ -1,12 +0,0 @@ -package es.princip.getp.domain.project.apply.model; - -import lombok.Getter; - -@Getter -public class ProjectApplicationId { - private Long value; - - public ProjectApplicationId(final Long value) { - this.value = value; - } -} diff --git a/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplicationStatus.java b/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplicationStatus.java deleted file mode 100644 index 8e2c0ce6..00000000 --- a/src/main/java/es/princip/getp/domain/project/apply/model/ProjectApplicationStatus.java +++ /dev/null @@ -1,10 +0,0 @@ -package es.princip.getp.domain.project.apply.model; - -public enum ProjectApplicationStatus { - APPLICATION_COMPLETED, // 지원 완료 - APPLICATION_REJECTED, // 지원 거절됨 - WAITING_MEETING, // 미팅 대기중 - PROCEEDING_MEETING, // 미팅 진행중 - APPLICATION_ACCEPTED, // 승인됨 - APPLICATION_WITHDRAWN // 철회됨 -} diff --git a/src/main/java/es/princip/getp/domain/project/apply/service/ProjectApplier.java b/src/main/java/es/princip/getp/domain/project/apply/service/ProjectApplier.java deleted file mode 100644 index 6c970985..00000000 --- a/src/main/java/es/princip/getp/domain/project/apply/service/ProjectApplier.java +++ /dev/null @@ -1,57 +0,0 @@ -package es.princip.getp.domain.project.apply.service; - -import es.princip.getp.domain.common.model.AttachmentFile; -import es.princip.getp.domain.common.model.Duration; -import es.princip.getp.domain.common.service.ClockHolder; -import es.princip.getp.domain.people.exception.NotRegisteredPeopleProfileException; -import es.princip.getp.domain.people.model.People; -import es.princip.getp.domain.project.apply.exception.ClosedProjectApplicationException; -import es.princip.getp.domain.project.apply.model.ProjectApplication; -import es.princip.getp.domain.project.apply.model.ProjectApplicationStatus; -import es.princip.getp.domain.project.commission.model.Project; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; - -import java.time.Clock; -import java.util.List; - -@Component -@RequiredArgsConstructor -public class ProjectApplier { - - private final ClockHolder clockHolder; - - /** - * 프로젝트 지원 - * - * @param people 지원하는 피플 - * @param project 지원할 프로젝트 - * @param expectedDuration 예상 기간 - * @param description 설명 - * @param attachmentFiles 첨부 파일 - * @return 프로젝트 지원 - */ - public ProjectApplication applyForProject( - final People people, - final Project project, - final Duration expectedDuration, - final String description, - final List attachmentFiles - ) { - final Clock clock = clockHolder.getClock(); - if (project.isApplicationClosed(clock)) { - throw new ClosedProjectApplicationException(); - } - if (!people.isProfileRegistered()) { - throw new NotRegisteredPeopleProfileException(); - } - return ProjectApplication.builder() - .applicantId(people.getId()) - .projectId(project.getProjectId()) - .expectedDuration(expectedDuration) - .description(description) - .attachmentFiles(attachmentFiles) - .applicationStatus(ProjectApplicationStatus.APPLICATION_COMPLETED) - .build(); - } -} diff --git a/src/main/java/es/princip/getp/domain/project/commission/model/ProjectId.java b/src/main/java/es/princip/getp/domain/project/commission/model/ProjectId.java deleted file mode 100644 index 5b89cd87..00000000 --- a/src/main/java/es/princip/getp/domain/project/commission/model/ProjectId.java +++ /dev/null @@ -1,12 +0,0 @@ -package es.princip.getp.domain.project.commission.model; - -import lombok.Getter; - -@Getter -public class ProjectId { - private Long value; - - public ProjectId(final Long value) { - this.value = value; - } -} diff --git a/src/main/java/es/princip/getp/persistence/adapter/people/NotFoundPeopleException.java b/src/main/java/es/princip/getp/persistence/adapter/people/NotFoundPeopleException.java deleted file mode 100644 index 9d1c7083..00000000 --- a/src/main/java/es/princip/getp/persistence/adapter/people/NotFoundPeopleException.java +++ /dev/null @@ -1,14 +0,0 @@ -package es.princip.getp.persistence.adapter.people; - -import es.princip.getp.domain.support.ErrorDescription; -import es.princip.getp.persistence.support.NotFoundException; - -class NotFoundPeopleException extends NotFoundException { - - private static final String code = "NOT_FOUND_PEOPLE"; - private static final String message = "존재하지 않는 피플입니다."; - - public NotFoundPeopleException() { - super(ErrorDescription.of(code, message)); - } -} diff --git a/src/main/java/es/princip/getp/persistence/adapter/people/PeoplePersistenceAdapter.java b/src/main/java/es/princip/getp/persistence/adapter/people/PeoplePersistenceAdapter.java deleted file mode 100644 index 171f31fd..00000000 --- a/src/main/java/es/princip/getp/persistence/adapter/people/PeoplePersistenceAdapter.java +++ /dev/null @@ -1,62 +0,0 @@ -package es.princip.getp.persistence.adapter.people; - -import es.princip.getp.application.people.port.out.*; -import es.princip.getp.domain.people.model.People; -import es.princip.getp.persistence.adapter.people.mapper.PeoplePersistenceMapper; -import es.princip.getp.persistence.adapter.people.model.PeopleJpaEntity; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -class PeoplePersistenceAdapter implements - SavePeoplePort, - LoadPeoplePort, - CheckPeoplePort, - UpdatePeoplePort, - DeletePeoplePort { - - private final PeoplePersistenceMapper mapper; - - private final PeopleJpaRepository peopleJpaRepository; - - @Override - public boolean existsBy(final Long memberId) { - return peopleJpaRepository.existsByMemberId(memberId); - } - - @Override - public People loadBy(final Long memberId) { - final PeopleJpaEntity peopleJpaEntity = peopleJpaRepository.findByMemberId(memberId) - .orElseThrow(NotFoundPeopleException::new); - return mapper.mapToDomain(peopleJpaEntity); - } - - @Override - public People loadByPeopleId(final Long peopleId) { - final PeopleJpaEntity peopleJpaEntity = peopleJpaRepository.findById(peopleId) - .orElseThrow(NotFoundPeopleException::new); - return mapper.mapToDomain(peopleJpaEntity); - } - - @Override - public Long save(final People people) { - return peopleJpaRepository.save(mapper.mapToJpa(people)).getId(); - } - - @Override - public void update(final People people) { - if (!peopleJpaRepository.existsById(people.getId())) { - throw new NotFoundPeopleException(); - } - peopleJpaRepository.save(mapper.mapToJpa(people)); - } - - @Override - public void delete(final Long peopleId) { - if (!peopleJpaRepository.existsById(peopleId)) { - throw new NotFoundPeopleException(); - } - peopleJpaRepository.deleteById(peopleId); - } -} diff --git a/src/main/java/es/princip/getp/persistence/adapter/people/PeopleQueryAdapter.java b/src/main/java/es/princip/getp/persistence/adapter/people/PeopleQueryAdapter.java deleted file mode 100644 index d801eb06..00000000 --- a/src/main/java/es/princip/getp/persistence/adapter/people/PeopleQueryAdapter.java +++ /dev/null @@ -1,202 +0,0 @@ -package es.princip.getp.persistence.adapter.people; - -import com.querydsl.core.Tuple; -import com.querydsl.core.types.Projections; -import com.querydsl.core.types.dsl.Expressions; -import es.princip.getp.api.controller.people.query.dto.people.CardPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.people.DetailPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.people.MyPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.people.PublicDetailPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.peopleProfile.DetailPeopleProfileResponse; -import es.princip.getp.application.like.people.port.out.CountPeopleLikePort; -import es.princip.getp.application.people.port.out.FindMyPeoplePort; -import es.princip.getp.application.people.port.out.FindPeoplePort; -import es.princip.getp.domain.people.exception.NotRegisteredPeopleProfileException; -import es.princip.getp.persistence.adapter.member.QMemberJpaEntity; -import es.princip.getp.persistence.adapter.people.mapper.PeopleQueryMapper; -import es.princip.getp.persistence.adapter.people.model.PeopleJpaEntity; -import es.princip.getp.persistence.adapter.people.model.PeopleProfileJpaVO; -import es.princip.getp.persistence.adapter.people.model.QPeopleJpaEntity; -import es.princip.getp.persistence.support.QueryDslSupport; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; - -import static es.princip.getp.persistence.adapter.people.PeopleQueryUtil.orderSpecifiersFromSort; -import static es.princip.getp.persistence.adapter.people.PeopleQueryUtil.toPeopleIds; -import static java.util.stream.Collectors.toMap; - -@Repository -@RequiredArgsConstructor -// TODO: 조회 성능 개선 필요 -public class PeopleQueryAdapter extends QueryDslSupport implements FindPeoplePort, FindMyPeoplePort { - - private static final QPeopleJpaEntity people = QPeopleJpaEntity.peopleJpaEntity; - private static final QMemberJpaEntity member = QMemberJpaEntity.memberJpaEntity; - - private final CountPeopleLikePort countPeopleLikePort; - - private final PeopleQueryMapper mapper; - - private Map findMemberAndPeopleByPeopleId(final Long... peopleId) { - return queryFactory.select( - member.nickname, - member.profileImage, - people.id, - people.peopleType - ) - .from(people) - .join(member) - .on(people.memberId.eq(member.memberId)) - .where(people.id.in(peopleId)).fetch().stream() - .collect(toMap(tuple -> tuple.get(people.id), Function.identity())); - } - - private Optional findMemberAndPeopleByPeopleId(final Long peopleId) { - return Optional.ofNullable( - queryFactory.select( - member.nickname, - member.profileImage, - people.id, - people.peopleType - ) - .from(people) - .join(member).on(people.memberId.eq(member.memberId)) - .where(people.id.eq(peopleId)) - .fetchOne() - ); - } - - private List getCardPeopleContent(final Pageable pageable) { - final List result = queryFactory.selectFrom(people) - .where(people.profile.isNotNull()) - .orderBy(orderSpecifiersFromSort(pageable.getSort())) - .offset(pageable.getOffset()) - .limit(pageable.getPageSize()) - .fetch(); - - final Long[] peopleIds = toPeopleIds(result); - - final Map likesCounts = countPeopleLikePort.countBy(peopleIds); - final Map memberAndPeople = findMemberAndPeopleByPeopleId(peopleIds); - - return result.stream().map(people -> { - final Long peopleId = people.getId(); - return new CardPeopleResponse( - peopleId, - memberAndPeople.get(peopleId).get(member.nickname), - memberAndPeople.get(peopleId).get(member.profileImage), - memberAndPeople.get(peopleId).get(QPeopleJpaEntity.peopleJpaEntity.peopleType), - 0, - likesCounts.get(peopleId), - mapper.mapToCardPeopleProfileResponse(people.getProfile()) - ); - }).toList(); - } - - @Override - public Page findCardBy(final Pageable pageable) { - return applyPagination( - pageable, - getCardPeopleContent(pageable), - queryFactory -> queryFactory.select(people.count()).from(people) - ); - } - - @Override - public DetailPeopleResponse findDetailBy(final Long peopleId) { - final PeopleProfileJpaVO profile = Optional.ofNullable( - queryFactory.select(people) - .from(people) - .where(people.id.eq(peopleId) - .and(people.profile.isNotNull())) - .fetchOne() - ) - .orElseThrow(NotFoundPeopleException::new) - .getProfile(); - - final Tuple memberAndPeople = findMemberAndPeopleByPeopleId(peopleId) - .orElseThrow(NotFoundPeopleException::new); - - return new DetailPeopleResponse( - peopleId, - memberAndPeople.get(member.nickname), - memberAndPeople.get(member.profileImage), - memberAndPeople.get(people.peopleType), - 0, - countPeopleLikePort.countBy(peopleId), - mapper.mapToDetailPeopleProfileResponse(profile) - ); - } - - @Override - public PublicDetailPeopleResponse findPublicDetailBy(final Long peopleId) { - final PeopleProfileJpaVO profile = Optional.ofNullable( - queryFactory.select(people) - .from(people) - .where(people.id.eq(peopleId) - .and(people.profile.isNotNull())) - .fetchOne() - ) - .orElseThrow(NotFoundPeopleException::new) - .getProfile(); - - final Tuple memberAndPeople = findMemberAndPeopleByPeopleId(peopleId) - .orElseThrow(NotFoundPeopleException::new); - - return new PublicDetailPeopleResponse( - peopleId, - memberAndPeople.get(member.nickname), - memberAndPeople.get(member.profileImage), - memberAndPeople.get(people.peopleType), - 0, - countPeopleLikePort.countBy(peopleId), - mapper.mapToPublicPeopleProfileResponse(profile) - ); - } - - @Override - public MyPeopleResponse findBy(final Long memberId) { - return Optional.ofNullable( - queryFactory.select( - Projections.constructor( - MyPeopleResponse.class, - people.id, - people.email, - member.nickname, - member.phoneNumber, - member.profileImage, - people.peopleType, - Expressions.asNumber(0).as("completedProjectsCount"), - Expressions.asNumber(0).as("likesCount"), - people.createdAt, - people.updatedAt - ) - ) - .from(people) - .join(member).on(people.memberId.eq(member.memberId)) - .where(people.memberId.eq(memberId)) - .fetchOne() - ).orElseThrow(NotFoundPeopleException::new); - } - - @Override - public DetailPeopleProfileResponse findDetailProfileBy(final Long memberId) { - final PeopleProfileJpaVO profile = Optional.ofNullable( - queryFactory.select(people) - .from(people) - .where(people.memberId.eq(memberId) - .and(people.profile.isNotNull())) - .fetchOne() - ) - .orElseThrow(NotRegisteredPeopleProfileException::new) - .getProfile(); - return mapper.mapToDetailPeopleProfileResponse(profile); - } -} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/persistence/adapter/people/PeopleQueryUtil.java b/src/main/java/es/princip/getp/persistence/adapter/people/PeopleQueryUtil.java deleted file mode 100644 index 7b63eb93..00000000 --- a/src/main/java/es/princip/getp/persistence/adapter/people/PeopleQueryUtil.java +++ /dev/null @@ -1,43 +0,0 @@ -package es.princip.getp.persistence.adapter.people; - -import com.querydsl.core.types.Order; -import com.querydsl.core.types.OrderSpecifier; -import es.princip.getp.domain.people.model.PeopleOrder; -import es.princip.getp.persistence.adapter.people.model.PeopleJpaEntity; -import es.princip.getp.persistence.adapter.people.model.QPeopleJpaEntity; -import org.springframework.data.domain.Sort; - -import java.util.List; - -import static com.querydsl.core.types.Order.ASC; -import static com.querydsl.core.types.Order.DESC; - -class PeopleQueryUtil { - - private static final QPeopleJpaEntity people = QPeopleJpaEntity.peopleJpaEntity; - - private static Order convertTo(final Sort.Order order) { - return order.isAscending() ? ASC : DESC; - } - - private static OrderSpecifier getPeopleOrderSpecifier(final Sort.Order order, final PeopleOrder peopleOrder) { - final Order converted = convertTo(order); - return switch (peopleOrder) { - // TODO: case INTEREST_COUNT - case CREATED_AT -> new OrderSpecifier<>(converted, people.createdAt); - default -> new OrderSpecifier<>(converted, people.id); - }; - } - - static OrderSpecifier[] orderSpecifiersFromSort(Sort sort) { - return sort.stream() - .map(order -> getPeopleOrderSpecifier(order, PeopleOrder.get(order.getProperty()))) - .toArray(OrderSpecifier[]::new); - } - - static Long[] toPeopleIds(final List peoples) { - return peoples.stream() - .map(PeopleJpaEntity::getId) - .toArray(Long[]::new); - } -} diff --git a/src/main/java/es/princip/getp/persistence/adapter/people/mapper/PeopleQueryMapper.java b/src/main/java/es/princip/getp/persistence/adapter/people/mapper/PeopleQueryMapper.java deleted file mode 100644 index 75ea39fa..00000000 --- a/src/main/java/es/princip/getp/persistence/adapter/people/mapper/PeopleQueryMapper.java +++ /dev/null @@ -1,33 +0,0 @@ -package es.princip.getp.persistence.adapter.people.mapper; - -import es.princip.getp.api.controller.people.query.dto.peopleProfile.CardPeopleProfileResponse; -import es.princip.getp.api.controller.people.query.dto.peopleProfile.DetailPeopleProfileResponse; -import es.princip.getp.api.controller.people.query.dto.peopleProfile.PortfolioResponse; -import es.princip.getp.api.controller.people.query.dto.peopleProfile.PublicDetailPeopleProfileResponse; -import es.princip.getp.persistence.adapter.common.mapper.HashtagPersistenceMapper; -import es.princip.getp.persistence.adapter.common.mapper.TechStackPersistenceMapper; -import es.princip.getp.persistence.adapter.people.model.PeopleProfileJpaVO; -import es.princip.getp.persistence.adapter.people.model.PortfolioJpaVO; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; - -@Mapper( - componentModel = "spring", - uses = { - PeoplePersistenceMapper.class, - TechStackPersistenceMapper.class, - HashtagPersistenceMapper.class - } -) -public abstract class PeopleQueryMapper { - - protected abstract PortfolioResponse mapToResponse(PortfolioJpaVO portfolio); - - public abstract CardPeopleProfileResponse mapToCardPeopleProfileResponse(PeopleProfileJpaVO profile); - - @Mapping(source = "school", target = "education.school") - @Mapping(source = "major", target = "education.major") - public abstract DetailPeopleProfileResponse mapToDetailPeopleProfileResponse(PeopleProfileJpaVO profile); - - public abstract PublicDetailPeopleProfileResponse mapToPublicPeopleProfileResponse(PeopleProfileJpaVO profile); -} diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/apply/FindAppliedProjectAdapter.java b/src/main/java/es/princip/getp/persistence/adapter/project/apply/FindAppliedProjectAdapter.java deleted file mode 100644 index 8f9d4ed9..00000000 --- a/src/main/java/es/princip/getp/persistence/adapter/project/apply/FindAppliedProjectAdapter.java +++ /dev/null @@ -1,79 +0,0 @@ -package es.princip.getp.persistence.adapter.project.apply; - -import com.querydsl.jpa.impl.JPAQuery; -import es.princip.getp.api.controller.project.query.dto.AppliedProjectCardResponse; -import es.princip.getp.application.project.apply.port.out.FindAppliedProjectPort; -import es.princip.getp.domain.project.commission.model.Project; -import es.princip.getp.persistence.adapter.people.model.QPeopleJpaEntity; -import es.princip.getp.persistence.adapter.project.ProjectPersistenceMapper; -import es.princip.getp.persistence.adapter.project.commission.QProjectJpaEntity; -import es.princip.getp.persistence.support.QueryDslSupport; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.Map; - -import static es.princip.getp.persistence.adapter.project.ProjectPersistenceUtil.toProjectIds; - -@Repository -@RequiredArgsConstructor -class FindAppliedProjectAdapter extends QueryDslSupport implements FindAppliedProjectPort { - - private static final QPeopleJpaEntity people = QPeopleJpaEntity.peopleJpaEntity; - private static final QProjectApplicationJpaEntity projectApplication - = QProjectApplicationJpaEntity.projectApplicationJpaEntity; - private static final QProjectJpaEntity project = QProjectJpaEntity.projectJpaEntity; - - private final FindProjectApplicationAdapter findProjectApplicationAdapter; - private final ProjectPersistenceMapper mapper; - - @Override - public Page findBy(final Long memberId, final Pageable pageable) { - final List projects = getContent(pageable, memberId); - final Long[] projectIds = toProjectIds(projects); - final Map projectApplicationCounts - = findProjectApplicationAdapter.countByProjectIds(projectIds); - final List content = assemble(projects, projectApplicationCounts); - return applyPagination( - pageable, - content, - countQuery -> buildCountQuery(memberId) - ); - } - - private JPAQuery buildQuery(final Long memberId) { - return queryFactory.from(project) - .join(projectApplication).on(projectApplication.projectId.eq(project.projectId)) - .join(people).on(people.id.eq(projectApplication.applicantId)) - .where(people.memberId.eq(memberId)); - } - - private List getContent(final Pageable pageable, final Long memberId) { - return buildQuery(memberId).select(project) - .offset(pageable.getOffset()) - .limit(pageable.getPageSize()) - .fetch() - .stream() - .map(mapper::mapToDomain) - .toList(); - } - - private JPAQuery buildCountQuery(final Long memberId) { - return buildQuery(memberId).select(project.count()); - } - - private List assemble( - final List projects, - final Map projectApplicationCounts - ) { - return projects.stream() - .map(project -> AppliedProjectCardResponse.of( - project, - projectApplicationCounts.get(project.getProjectId()) - )) - .toList(); - } -} diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/apply/FindProjectApplicationAdapter.java b/src/main/java/es/princip/getp/persistence/adapter/project/apply/FindProjectApplicationAdapter.java deleted file mode 100644 index b52d1fd2..00000000 --- a/src/main/java/es/princip/getp/persistence/adapter/project/apply/FindProjectApplicationAdapter.java +++ /dev/null @@ -1,31 +0,0 @@ -package es.princip.getp.persistence.adapter.project.apply; - -import es.princip.getp.persistence.support.QueryDslSupport; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; - -@Repository -@RequiredArgsConstructor -public class FindProjectApplicationAdapter extends QueryDslSupport { - - private static final QProjectApplicationJpaEntity projectApplication - = QProjectApplicationJpaEntity.projectApplicationJpaEntity; - - public Map countByProjectIds(final Long... projectId) { - return queryFactory.select(projectApplication.projectId, projectApplication.count()) - .from(projectApplication) - .where(projectApplication.projectId.in(projectId)) - .groupBy(projectApplication.projectId) - .fetch() - .stream() - .collect(Collectors.toMap( - tuple -> tuple.get(projectApplication.projectId), - tuple -> Optional.ofNullable(tuple.get(projectApplication.count())) - .orElse(0L) - )); - } -} \ No newline at end of file diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationJpaEntity.java b/src/main/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationJpaEntity.java deleted file mode 100644 index 98a94c61..00000000 --- a/src/main/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationJpaEntity.java +++ /dev/null @@ -1,54 +0,0 @@ -package es.princip.getp.persistence.adapter.project.apply; - -import es.princip.getp.domain.project.apply.model.ProjectApplicationStatus; -import es.princip.getp.persistence.adapter.BaseTimeJpaEntity; -import es.princip.getp.persistence.adapter.common.DurationJpaVO; -import jakarta.persistence.*; -import lombok.*; - -import java.util.ArrayList; -import java.util.List; - -@Getter -@Entity -@Builder -@AllArgsConstructor -@Table(name = "project_application") -@NoArgsConstructor(access = AccessLevel.PROTECTED) -class ProjectApplicationJpaEntity extends BaseTimeJpaEntity { - - @Id - @Column(name = "project_application_id") - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long applicationId; - - @Column(name = "people_id") - private Long applicantId; - - @Column(name = "project_id") - private Long projectId; - - @Embedded - @AttributeOverrides( - { - @AttributeOverride(name = "startDate", column = @Column(name = "expected_start_date")), - @AttributeOverride(name = "endDate", column = @Column(name = "expected_end_date")) - } - ) - private DurationJpaVO expectedDuration; - - @Enumerated(EnumType.STRING) - @Column(name = "status") - private ProjectApplicationStatus applicationStatus; - - @Column(name = "description") - private String description; - - @Builder.Default - @ElementCollection - @CollectionTable( - name = "project_application_attachment_file", - joinColumns = @JoinColumn(name = "project_application_id") - ) - private List attachmentFiles = new ArrayList<>(); -} diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationPersistenceAdapter.java b/src/main/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationPersistenceAdapter.java deleted file mode 100644 index 8d12ca18..00000000 --- a/src/main/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationPersistenceAdapter.java +++ /dev/null @@ -1,28 +0,0 @@ -package es.princip.getp.persistence.adapter.project.apply; - -import es.princip.getp.application.project.apply.port.out.CheckProjectApplicationPort; -import es.princip.getp.application.project.apply.port.out.SaveProjectApplicationPort; -import es.princip.getp.domain.project.apply.model.ProjectApplication; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -class ProjectApplicationPersistenceAdapter implements - SaveProjectApplicationPort, - CheckProjectApplicationPort { - - private final ProjectApplicationPersistenceMapper mapper; - private final ProjectApplicationJpaRepository repository; - - @Override - public Long save(final ProjectApplication application) { - final ProjectApplicationJpaEntity jpaEntity = mapper.mapToJpa(application); - return repository.save(jpaEntity).getApplicationId(); - } - - @Override - public boolean existsBy(final Long applicantId, final Long projectId) { - return repository.existsByApplicantIdAndProjectId(applicantId, projectId); - } -} diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationPersistenceMapper.java b/src/main/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationPersistenceMapper.java deleted file mode 100644 index 5f457464..00000000 --- a/src/main/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationPersistenceMapper.java +++ /dev/null @@ -1,16 +0,0 @@ -package es.princip.getp.persistence.adapter.project.apply; - -import es.princip.getp.domain.project.apply.model.ProjectApplication; -import es.princip.getp.persistence.adapter.common.mapper.AttachmentFilePersistenceMapper; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; - -@Mapper(componentModel = "spring", uses = {AttachmentFilePersistenceMapper.class}) -interface ProjectApplicationPersistenceMapper { - - @Mapping(source = "applicantId", target = "applicantId") - ProjectApplication mapToDomain(ProjectApplicationJpaEntity applicationJpaEntity); - - @Mapping(target = "applicantId", source = "applicantId") - ProjectApplicationJpaEntity mapToJpa(ProjectApplication application); -} diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/commission/FindCommissionedProjectAdapter.java b/src/main/java/es/princip/getp/persistence/adapter/project/commission/FindCommissionedProjectAdapter.java deleted file mode 100644 index 1df3a753..00000000 --- a/src/main/java/es/princip/getp/persistence/adapter/project/commission/FindCommissionedProjectAdapter.java +++ /dev/null @@ -1,111 +0,0 @@ -package es.princip.getp.persistence.adapter.project.commission; - -import com.querydsl.core.types.Order; -import com.querydsl.core.types.OrderSpecifier; -import com.querydsl.core.types.dsl.BooleanExpression; -import com.querydsl.jpa.impl.JPAQuery; -import es.princip.getp.api.controller.project.query.dto.CommissionedProjectCardResponse; -import es.princip.getp.api.controller.project.query.dto.MyProjectSearchOrder; -import es.princip.getp.application.project.commission.port.out.FindCommissionedProjectPort; -import es.princip.getp.domain.project.commission.model.Project; -import es.princip.getp.domain.project.commission.model.ProjectStatus; -import es.princip.getp.persistence.adapter.client.QClientJpaEntity; -import es.princip.getp.persistence.adapter.project.ProjectPersistenceMapper; -import es.princip.getp.persistence.adapter.project.apply.FindProjectApplicationAdapter; -import es.princip.getp.persistence.support.QueryDslSupport; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.Map; - -import static com.querydsl.core.types.Order.ASC; -import static com.querydsl.core.types.Order.DESC; -import static es.princip.getp.persistence.adapter.project.ProjectPersistenceUtil.toProjectIds; - -@Repository -@RequiredArgsConstructor -class FindCommissionedProjectAdapter extends QueryDslSupport implements FindCommissionedProjectPort { - - private static final QProjectJpaEntity project = QProjectJpaEntity.projectJpaEntity; - private static final QClientJpaEntity client = QClientJpaEntity.clientJpaEntity; - private final FindProjectApplicationAdapter projectApplicationQuery; - private final ProjectPersistenceMapper mapper; - - @Override - public Page findBy( - final Long memberId, - final Boolean cancelled, - final Pageable pageable - ) { - final List projects = getMyProjects(pageable, memberId, cancelled); - final Long[] projectIds = toProjectIds(projects); - final Map projectApplicationCounts = projectApplicationQuery.countByProjectIds(projectIds); - final List content = assembleProjectCardResponse(projects, projectApplicationCounts); - - return applyPagination( - pageable, - content, - countQuery -> getMyProjectsCountQuery(memberId, cancelled) - ); - } - - private List assembleProjectCardResponse( - final List projects, - final Map projectApplicationCounts - ) { - return projects.stream() - .map(project -> CommissionedProjectCardResponse.of( - project, - projectApplicationCounts.get(project.getProjectId()) - )) - .toList(); - } - - private JPAQuery getMyProjectsCountQuery(final Long memberId, final Boolean cancelled) { - return queryFactory.select(project.count()) - .from(project) - .join(client).on(project.clientId.eq(client.clientId)) - .where(client.memberId.eq(memberId), cancelledFilter(cancelled)); - } - - private List getMyProjects(final Pageable pageable, final Long memberId, final Boolean cancelled) { - return queryFactory.selectFrom(project) - .join(client).on(project.clientId.eq(client.clientId)) - .where(client.memberId.eq(memberId), cancelledFilter(cancelled)) - .orderBy(orderSpecifiersFromSort(pageable.getSort())) - .offset(pageable.getOffset()) - .limit(pageable.getPageSize()) - .fetch() - .stream() - .map(mapper::mapToDomain) - .toList(); - } - - private BooleanExpression cancelledFilter(final Boolean cancelled) { - return cancelled ? null : project.status.ne(ProjectStatus.CANCELLED); - } - - private static Order convertTo(final Sort.Order order) { - return order.isAscending() ? ASC : DESC; - } - - private static OrderSpecifier toOrderSpecifier(final Sort.Order order, final MyProjectSearchOrder searchOrder) { - final Order converted = convertTo(order); - return switch (searchOrder) { - case CREATED_AT -> new OrderSpecifier<>(converted, project.createdAt); - case PAYMENT -> new OrderSpecifier<>(converted, project.payment); - case APPLICATION_DURATION -> new OrderSpecifier<>(converted, project.applicationDuration.endDate); - case PROJECT_ID -> new OrderSpecifier<>(converted, project.projectId); - }; - } - - static OrderSpecifier[] orderSpecifiersFromSort(Sort sort) { - return sort.stream() - .map(order -> toOrderSpecifier(order, MyProjectSearchOrder.get(order.getProperty()))) - .toArray(OrderSpecifier[]::new); - } -} diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/commission/FindProjectAdapter.java b/src/main/java/es/princip/getp/persistence/adapter/project/commission/FindProjectAdapter.java deleted file mode 100644 index 8dce0f4e..00000000 --- a/src/main/java/es/princip/getp/persistence/adapter/project/commission/FindProjectAdapter.java +++ /dev/null @@ -1,97 +0,0 @@ -package es.princip.getp.persistence.adapter.project.commission; - -import es.princip.getp.api.controller.common.dto.HashtagsResponse; -import es.princip.getp.api.controller.project.query.dto.AttachmentFilesResponse; -import es.princip.getp.api.controller.project.query.dto.ProjectCardResponse; -import es.princip.getp.api.controller.project.query.dto.ProjectDetailResponse; -import es.princip.getp.application.client.port.out.ClientQuery; -import es.princip.getp.application.like.project.port.out.CountProjectLikePort; -import es.princip.getp.application.project.commission.port.out.FindProjectPort; -import es.princip.getp.domain.project.commission.model.Project; -import es.princip.getp.persistence.adapter.project.ProjectPersistenceMapper; -import es.princip.getp.persistence.adapter.project.apply.FindProjectApplicationAdapter; -import es.princip.getp.persistence.support.QueryDslSupport; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.Map; -import java.util.Optional; - -import static es.princip.getp.persistence.adapter.project.ProjectPersistenceUtil.toProjectIds; - -@Repository -@RequiredArgsConstructor -class FindProjectAdapter extends QueryDslSupport implements FindProjectPort { - - private static final QProjectJpaEntity project = QProjectJpaEntity.projectJpaEntity; - - private final ClientQuery clientQuery; - private final CountProjectLikePort projectLikeDao; - private final FindProjectApplicationAdapter projectApplicationDao; - private final ProjectPersistenceMapper mapper; - - private List getProjects(Pageable pageable) { - return queryFactory.selectFrom(project) - .offset(pageable.getOffset()) - .limit(pageable.getPageSize()) - .fetch() - .stream() - .map(mapper::mapToDomain) - .toList(); - } - - private List assembleProjectCardResponse( - final List projects, - final Map projectApplicationCounts - ) { - return projects.stream() - .map(project -> ProjectCardResponse.of( - project, - projectApplicationCounts.getOrDefault(project.getProjectId(), 0L) - )) - .toList(); - } - - @Override - public Page findBy(Pageable pageable) { - final List projects = getProjects(pageable); - final Long[] projectIds = toProjectIds(projects); - final Map projectApplicationCounts = projectApplicationDao.countByProjectIds(projectIds); - final List content = assembleProjectCardResponse(projects, projectApplicationCounts); - return applyPagination( - pageable, - content, - countQuery -> countQuery.select(project.count()).from(project) - ); - } - - @Override - public ProjectDetailResponse findBy(final Long projectId) { - final Project result = mapper.mapToDomain(Optional.ofNullable( - queryFactory.selectFrom(project) - .where(project.projectId.eq(projectId)) - .fetchOne() - ) - .orElseThrow(NotFoundProjectException::new)); - final Long likesCount = projectLikeDao.countBy(projectId); - - return new ProjectDetailResponse( - result.getProjectId(), - result.getTitle(), - result.getPayment(), - result.getApplicationDuration(), - result.getEstimatedDuration(), - result.getDescription(), - result.getMeetingType(), - result.getCategory(), - result.getStatus(), - AttachmentFilesResponse.from(result.getAttachmentFiles()), - HashtagsResponse.from(result.getHashtags()), - likesCount, - clientQuery.findProjectClientById(result.getClientId()) - ); - } -} diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingQueryDslRepository.java b/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingQueryDslRepository.java deleted file mode 100644 index 39f21c41..00000000 --- a/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingQueryDslRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package es.princip.getp.persistence.adapter.project.meeting; - -interface ProjectMeetingQueryDslRepository { - - boolean existsApplicantByProjectIdAndMemberId(Long projectId, Long memberId); -} diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingQueryDslRepositoryImpl.java b/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingQueryDslRepositoryImpl.java deleted file mode 100644 index 8657d292..00000000 --- a/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingQueryDslRepositoryImpl.java +++ /dev/null @@ -1,27 +0,0 @@ -package es.princip.getp.persistence.adapter.project.meeting; - -import es.princip.getp.persistence.adapter.client.QClientJpaEntity; -import es.princip.getp.persistence.adapter.project.commission.QProjectJpaEntity; -import es.princip.getp.persistence.support.QueryDslSupport; - -class ProjectMeetingQueryDslRepositoryImpl - extends QueryDslSupport - implements ProjectMeetingQueryDslRepository { - - private static final QProjectJpaEntity project = QProjectJpaEntity.projectJpaEntity; - private static final QClientJpaEntity client = QClientJpaEntity.clientJpaEntity; - - @Override - public boolean existsApplicantByProjectIdAndMemberId(final Long projectId, final Long memberId) { - final Integer count = queryFactory.selectOne() - .from(project) - .join(client) - .on( - project.clientId.eq(client.clientId) - .and(project.projectId.eq(projectId)) - .and(client.memberId.eq(memberId)) - ) - .fetchFirst(); - return count != null; - } -} diff --git a/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingRepository.java b/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingRepository.java deleted file mode 100644 index d16f9a87..00000000 --- a/src/main/java/es/princip/getp/persistence/adapter/project/meeting/ProjectMeetingRepository.java +++ /dev/null @@ -1,5 +0,0 @@ -package es.princip.getp.persistence.adapter.project.meeting; - -interface ProjectMeetingRepository - extends ProjectMeetingJpaRepository, ProjectMeetingQueryDslRepository { -} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml deleted file mode 100644 index b1bad0dc..00000000 --- a/src/main/resources/application-dev.yml +++ /dev/null @@ -1,103 +0,0 @@ -server: - port: ${SPRING_PORT} - servlet: - context-path: ${BASE_PATH} - -spring: - storage: - local: - path: ${STORAGE_PATH} - base-uri: ${STORAGE_BASE_URI} - - datasource: - url: ${DB_URL} - username: ${DB_USERNAME} - password: ${DB_PASSWORD} - - flyway: - enabled: true - baseline-on-migrate: true - - jpa: - hibernate: - ddl-auto: validate - properties: - hibernate: - show_sql: true - format_sql: true - highlight_sql: true - use_sql_comments: true - jdbc: - time_zone: Asia/Seoul - default_batch_fetch_size: 20 - dialect: org.hibernate.dialect.MySQLDialect - - mail: - host: smtp.gmail.com - port: ${GMAIL_PORT} - username: ${GMAIL_USERNAME} - password: ${GMAIL_PASSWORD} - properties: - mail: - smtp: - auth: true - starttls: - enable: true - required: true - connectiontimeout: 5000 - timeout: 5000 - writetimeout: 5000 - - verification-code: - length: ${VERIFICATION_CODE_LENGTH} - expire-time: ${VERIFICATION_CODE_EXPIRE_TIME} - - jwt: - secret: ${JWT_SECRET} - access-token: - expire-time: ${JWT_ACCESS_TOKEN_EXPIRE_TIME} - refresh-token: - expire-time: ${JWT_REFRESH_TOKEN_EXPIRE_TIME} - - data: - redis: - host: ${REDIS_HOST} - port: ${REDIS_PORT} - password: ${REDIS_PASSWORD} - - messages: - basename: messages/validation, messages/error - -logging: - level: - es.princip.getp: DEBUG - org: - springframework: - security: DEBUG - type: - descriptor: - sql: - BasicBinder: TRACE - -springdoc: - swagger-ui: - path: ${SWAGGER_PATH} - -management: - endpoints: - web: - exposure: - include: prometheus - base-path: ${MONITORING_URL} - enabled-by-default: false - jmx: - exposure: - exclude: '*' - include: info, health - endpoint: - prometheus: - enabled: true - info: - enabled: true - health: - enabled: true \ No newline at end of file diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml deleted file mode 100644 index 4b4ade9b..00000000 --- a/src/main/resources/application-local.yml +++ /dev/null @@ -1,88 +0,0 @@ -server: - port: ${SPRING_PORT} - servlet: - context-path: ${BASE_PATH} - -spring: - storage: - local: - path: ${STORAGE_PATH} - base-uri: ${STORAGE_BASE_URI} - - datasource: - url: ${DB_URL} - username: ${DB_USERNAME} - password: ${DB_PASSWORD} - - flyway: - enabled: true - baseline-on-migrate: true - - jpa: - hibernate: - ddl-auto: validate - properties: - hibernate: - show_sql: true - format_sql: true - highlight_sql: true - use_sql_comments: true - jdbc: - time_zone: Asia/Seoul - default_batch_fetch_size: 20 - dialect: org.hibernate.dialect.MySQLDialect - - mail: - host: smtp.gmail.com - port: ${GMAIL_PORT} - username: ${GMAIL_USERNAME} - password: ${GMAIL_PASSWORD} - properties: - mail: - smtp: - auth: true - starttls: - enable: true - required: true - connectiontimeout: 5000 - timeout: 5000 - writetimeout: 5000 - - verification-code: - length: ${VERIFICATION_CODE_LENGTH} - expire-time: ${VERIFICATION_CODE_EXPIRE_TIME} - - jwt: - secret: ${JWT_SECRET} - access-token: - expire-time: ${JWT_ACCESS_TOKEN_EXPIRE_TIME} - refresh-token: - expire-time: ${JWT_REFRESH_TOKEN_EXPIRE_TIME} - - data: - redis: - host: ${REDIS_HOST} - port: ${REDIS_PORT} - password: ${REDIS_PASSWORD} - - org: - gradle: - caching: true - - messages: - basename: messages/validation, messages/error - -logging: - level: - es.princip.getp: DEBUG - org: - springframework: - security: DEBUG - type: - descriptor: - sql: - BasicBinder: TRACE - -springdoc: - swagger-ui: - path: ${SWAGGER_PATH} \ No newline at end of file diff --git a/src/main/resources/messages/error.properties b/src/main/resources/messages/error.properties deleted file mode 100644 index e69de29b..00000000 diff --git a/src/test/java/es/princip/getp/api/config/MockCommandMapperBeanFactoryPostProcessor.java b/src/test/java/es/princip/getp/api/config/MockCommandMapperBeanFactoryPostProcessor.java deleted file mode 100644 index 4f52183c..00000000 --- a/src/test/java/es/princip/getp/api/config/MockCommandMapperBeanFactoryPostProcessor.java +++ /dev/null @@ -1,23 +0,0 @@ -package es.princip.getp.api.config; - -import es.princip.getp.api.controller.common.mapper.CommandMapper; -import org.jetbrains.annotations.NotNull; -import org.junit.platform.commons.util.ClassFilter; -import org.junit.platform.commons.util.ReflectionUtils; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; - -import static org.mockito.Mockito.mock; - -public class MockCommandMapperBeanFactoryPostProcessor implements BeanFactoryPostProcessor { - - @Override - public void postProcessBeanFactory(@NotNull final ConfigurableListableBeanFactory beanFactory) throws BeansException { - final ClassFilter classFilter = ClassFilter.of(clazz -> clazz.isAnnotationPresent(CommandMapper.class)); - ReflectionUtils.findAllClassesInPackage("es.princip.getp.api", classFilter) - .forEach(clazz -> { - beanFactory.registerSingleton(clazz.getSimpleName(), mock(clazz)); - }); - } -} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/auth/AuthControllerTest.java b/src/test/java/es/princip/getp/api/controller/auth/AuthControllerTest.java deleted file mode 100644 index 7af1052a..00000000 --- a/src/test/java/es/princip/getp/api/controller/auth/AuthControllerTest.java +++ /dev/null @@ -1,90 +0,0 @@ -package es.princip.getp.api.controller.auth; - -import es.princip.getp.api.support.ControllerTest; -import es.princip.getp.api.controller.auth.dto.request.LoginRequest; -import es.princip.getp.api.controller.auth.dto.response.Token; -import es.princip.getp.application.auth.service.AuthService; -import jakarta.servlet.http.HttpServletRequest; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; -import static es.princip.getp.api.docs.HeaderDescriptorHelper.refreshTokenHeaderDescriptor; -import static es.princip.getp.api.docs.PayloadDocumentationHelper.responseFields; -import static es.princip.getp.fixture.common.EmailFixture.EMAIL; -import static es.princip.getp.fixture.member.PasswordFixture.PASSWORD; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; -import static org.springframework.restdocs.snippet.Attributes.key; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -class AuthControllerTest extends ControllerTest { - - @Autowired - private AuthService authService; - - @Nested - class Login { - - final LoginRequest request = new LoginRequest(EMAIL, PASSWORD); - - @DisplayName("사용자는 로그인을 할 수 있다.") - @Test - void login() throws Exception { - final Token token = new Token("Bearer", "${ACCESS_TOKEN}", "${REFRESH_TOKEN}"); - given(authService.login(request)).willReturn(token); - - mockMvc.perform(post("/auth/login") - .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isCreated()) - .andDo( - restDocs.document( - requestFields( - getDescriptor("email", "이메일", LoginRequest.class), - getDescriptor("password", "비밀번호", LoginRequest.class) - ), - responseFields( - getDescriptor("grantType", "토큰 타입", Token.class) - .attributes(key("format").value("Bearer")), - getDescriptor("accessToken", "Access Token", Token.class), - getDescriptor("refreshToken", "Refresh Token", Token.class) - ) - ) - ) - .andDo(print()); - } - } - - @Nested - class ReissueAccessToken { - - @DisplayName("사용자는 로그인 유지를 할 수 있다.") - @Test - void reissueAccessToken() throws Exception { - final Token token = new Token("Bearer", "${ACCESS_TOKEN}", "${REFRESH_TOKEN}"); - given(authService.reissueAccessToken(any(HttpServletRequest.class))) - .willReturn(token); - - mockMvc.perform(post("/auth/reissue") - .header("Refresh-Token", "Bearer ${REFRESH_TOKEN}")) - .andExpect(status().isCreated()) - .andDo( - restDocs.document( - requestHeaders(refreshTokenHeaderDescriptor()), - responseFields( - getDescriptor("grantType", "토큰 타입", Token.class) - .attributes(key("format").value("Bearer")), - getDescriptor("accessToken", "Access Token", Token.class), - getDescriptor("refreshToken", "Refresh Token", Token.class) - ) - ) - ) - .andDo(print()); - } - } -} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/auth/SignUpControllerTest.java b/src/test/java/es/princip/getp/api/controller/auth/SignUpControllerTest.java deleted file mode 100644 index e502e45d..00000000 --- a/src/test/java/es/princip/getp/api/controller/auth/SignUpControllerTest.java +++ /dev/null @@ -1,123 +0,0 @@ -package es.princip.getp.api.controller.auth; - -import es.princip.getp.api.support.ControllerTest; -import es.princip.getp.api.controller.auth.dto.request.EmailVerificationCodeRequest; -import es.princip.getp.api.controller.auth.dto.request.ServiceTermAgreementRequest; -import es.princip.getp.api.controller.auth.dto.request.SignUpRequest; -import es.princip.getp.application.auth.command.SignUpCommand; -import es.princip.getp.application.auth.service.SignUpService; -import es.princip.getp.domain.member.model.MemberType; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; -import org.springframework.beans.factory.annotation.Autowired; - -import java.util.Set; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; -import static es.princip.getp.fixture.auth.EmailVerificationFixture.VERIFICATION_CODE; -import static es.princip.getp.fixture.common.EmailFixture.EMAIL; -import static es.princip.getp.fixture.member.PasswordFixture.PASSWORD; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.BDDMockito.willDoNothing; -import static org.mockito.Mockito.mock; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; -import static org.springframework.restdocs.snippet.Attributes.key; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -class SignUpControllerTest extends ControllerTest { - - @Autowired - private SignUpCommandMapper mapper; - - @Autowired - private SignUpService signUpService; - - @Nested - class SendEmailVerificationCodeForSignUp { - - private final EmailVerificationCodeRequest request = new EmailVerificationCodeRequest(EMAIL); - - @DisplayName("사용자는 회원 가입 시 이메일 인증을 해야 한다.") - @Test - void sendEmailVerificationCodeForSignUp() throws Exception { - mockMvc.perform(post("/auth/signup/email/send") - .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isOk()) - .andDo( - restDocs.document( - requestFields( - getDescriptor("email", "이메일", EmailVerificationCodeRequest.class) - ) - ) - ) - .andDo(print()); - } - } - - @Nested - class SignUp { - - @DisplayName("사용자는 회원 가입을 할 수 있다.") - @ParameterizedTest - @EnumSource(value = MemberType.class, names = { "ROLE_PEOPLE", "ROLE_CLIENT" }) - void signUp(MemberType memberType) throws Exception { - SignUpRequest request = new SignUpRequest( - EMAIL, - PASSWORD, - VERIFICATION_CODE, - Set.of( - new ServiceTermAgreementRequest("tag1", true), - new ServiceTermAgreementRequest("tag2", false) - ), - memberType - ); - given(mapper.mapToCommand(request)).willReturn(mock(SignUpCommand.class)); - willDoNothing().given(signUpService).signUp(any(SignUpCommand.class)); - - mockMvc.perform(post("/auth/signup") - .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isCreated()) - .andDo( - restDocs.document( - requestFields( - getDescriptor("email", "이메일", SignUpRequest.class), - getDescriptor("password", "비밀번호", SignUpRequest.class), - getDescriptor("memberType", "회원 유형", SignUpRequest.class) - .attributes(key("format").value("ROLE_PEOPLE, ROLE_CLIENT")), - getDescriptor("verificationCode", "이메일 인증 코드", SignUpRequest.class), - getDescriptor("serviceTerms[].tag", "서비스 약관 태그", ServiceTermAgreementRequest.class), - getDescriptor("serviceTerms[].agreed", "서비스 약관 동의 여부", ServiceTermAgreementRequest.class) - ) - ) - ) - .andDo(print()); - } - - @DisplayName("관리자 또는 매니저로 회원 가입할 수 없다.") - @ParameterizedTest - @EnumSource(value = MemberType.class, names = { "ROLE_ADMIN", "ROLE_MANAGER" }) - void signUp_WhenMemberTypeIsNotPeopleOrClient_ShouldFail(MemberType memberType) - throws Exception { - SignUpRequest request = new SignUpRequest( - EMAIL, - PASSWORD, - VERIFICATION_CODE, - Set.of( - new ServiceTermAgreementRequest("tag1", true), - new ServiceTermAgreementRequest("tag2", false) - ), - memberType - ); - - mockMvc.perform(post("/auth/signup") - .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isBadRequest()) - .andDo(print()); - } - } -} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/client/command/description/AddressDescription.java b/src/test/java/es/princip/getp/api/controller/client/command/description/AddressDescription.java deleted file mode 100644 index 75c41d9b..00000000 --- a/src/test/java/es/princip/getp/api/controller/client/command/description/AddressDescription.java +++ /dev/null @@ -1,18 +0,0 @@ -package es.princip.getp.api.controller.client.command.description; - -import es.princip.getp.domain.client.model.Address; -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; - -public class AddressDescription { - - public static FieldDescriptor[] description() { - final Class clazz = Address.class; - return new FieldDescriptor[] { - getDescriptor("address.zipcode", "우편번호", clazz), - getDescriptor("address.street", "도로명", clazz), - getDescriptor("address.detail", "상세 주소", clazz), - }; - } -} diff --git a/src/test/java/es/princip/getp/api/controller/client/command/description/BankAccountDescription.java b/src/test/java/es/princip/getp/api/controller/client/command/description/BankAccountDescription.java deleted file mode 100644 index 261a0692..00000000 --- a/src/test/java/es/princip/getp/api/controller/client/command/description/BankAccountDescription.java +++ /dev/null @@ -1,18 +0,0 @@ -package es.princip.getp.api.controller.client.command.description; - -import es.princip.getp.domain.client.model.BankAccount; -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; - -public class BankAccountDescription { - - public static FieldDescriptor[] description() { - final Class clazz = BankAccount.class; - return new FieldDescriptor[] { - getDescriptor("bankAccount.bank", "은행명", clazz), - getDescriptor("bankAccount.accountNumber", "계좌번호", clazz), - getDescriptor("bankAccount.accountHolder", "예금주", clazz) - }; - } -} diff --git a/src/test/java/es/princip/getp/api/controller/client/command/description/EditMyClientRequestDescription.java b/src/test/java/es/princip/getp/api/controller/client/command/description/EditMyClientRequestDescription.java deleted file mode 100644 index 57a69c45..00000000 --- a/src/test/java/es/princip/getp/api/controller/client/command/description/EditMyClientRequestDescription.java +++ /dev/null @@ -1,24 +0,0 @@ -package es.princip.getp.api.controller.client.command.description; - -import es.princip.getp.api.controller.client.command.dto.request.EditMyClientRequest; -import org.springframework.restdocs.payload.FieldDescriptor; - -import java.util.ArrayList; -import java.util.List; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; - -public class EditMyClientRequestDescription { - - public static FieldDescriptor[] description() { - final Class clazz = EditMyClientRequest.class; - final List descriptions = new ArrayList<>(List.of( - getDescriptor("nickname", "닉네임", clazz), - getDescriptor("email", "이메일", clazz), - getDescriptor("phoneNumber", "전화번호", clazz) - )); - descriptions.addAll(List.of(AddressDescription.description())); - descriptions.addAll(List.of(BankAccountDescription.description())); - return descriptions.toArray(new FieldDescriptor[0]); - } -} diff --git a/src/test/java/es/princip/getp/api/controller/client/command/description/RegisterMyClientRequestDescription.java b/src/test/java/es/princip/getp/api/controller/client/command/description/RegisterMyClientRequestDescription.java deleted file mode 100644 index c973a675..00000000 --- a/src/test/java/es/princip/getp/api/controller/client/command/description/RegisterMyClientRequestDescription.java +++ /dev/null @@ -1,24 +0,0 @@ -package es.princip.getp.api.controller.client.command.description; - -import es.princip.getp.api.controller.client.command.dto.request.RegisterMyClientRequest; -import org.springframework.restdocs.payload.FieldDescriptor; - -import java.util.ArrayList; -import java.util.List; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; - -public class RegisterMyClientRequestDescription { - - public static FieldDescriptor[] description() { - final Class clazz = RegisterMyClientRequest.class; - final List descriptions = new ArrayList<>(List.of( - getDescriptor("nickname", "닉네임", clazz), - getDescriptor("email", "이메일. 미입력 시 회원 정보의 이메일로 등록됩니다.", clazz), - getDescriptor("phoneNumber", "전화번호", clazz) - )); - descriptions.addAll(List.of(AddressDescription.description())); - descriptions.addAll(List.of(BankAccountDescription.description())); - return descriptions.toArray(new FieldDescriptor[0]); - } -} diff --git a/src/test/java/es/princip/getp/api/controller/client/query/description/ClientResponseDescription.java b/src/test/java/es/princip/getp/api/controller/client/query/description/ClientResponseDescription.java deleted file mode 100644 index 4047e194..00000000 --- a/src/test/java/es/princip/getp/api/controller/client/query/description/ClientResponseDescription.java +++ /dev/null @@ -1,28 +0,0 @@ -package es.princip.getp.api.controller.client.query.description; - -import es.princip.getp.api.controller.client.command.description.AddressDescription; -import es.princip.getp.api.controller.client.command.description.BankAccountDescription; -import org.springframework.restdocs.payload.FieldDescriptor; - -import java.util.ArrayList; -import java.util.List; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; - -public class ClientResponseDescription { - - public static FieldDescriptor[] description() { - final List descriptions = new ArrayList<>(List.of( - getDescriptor("clientId", "의뢰자 ID"), - getDescriptor("email", "이메일"), - getDescriptor("nickname", "닉네임"), - getDescriptor("phoneNumber", "전화번호"), - getDescriptor("profileImageUri", "프로필 이미지 URI"), - getDescriptor("createdAt", "의뢰자 정보 등록 일시"), - getDescriptor("updatedAt", "최근 의뢰자 정보 수정 일시") - )); - descriptions.addAll(List.of(AddressDescription.description())); - descriptions.addAll(List.of(BankAccountDescription.description())); - return descriptions.toArray(new FieldDescriptor[0]); - } -} diff --git a/src/test/java/es/princip/getp/api/controller/like/command/PeopleLikeControllerTest.java b/src/test/java/es/princip/getp/api/controller/like/command/PeopleLikeControllerTest.java deleted file mode 100644 index 62c5aca2..00000000 --- a/src/test/java/es/princip/getp/api/controller/like/command/PeopleLikeControllerTest.java +++ /dev/null @@ -1,80 +0,0 @@ -package es.princip.getp.api.controller.like.command; - -import es.princip.getp.api.support.ControllerTest; -import es.princip.getp.api.security.annotation.WithCustomMockUser; -import es.princip.getp.application.like.exception.AlreadyLikedException; -import es.princip.getp.application.like.people.port.in.LikePeopleUseCase; -import es.princip.getp.application.like.people.port.in.UnlikePeopleUseCase; -import es.princip.getp.domain.member.model.MemberType; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.BDDMockito.willDoNothing; -import static org.mockito.BDDMockito.willThrow; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -class PeopleLikeControllerTest extends ControllerTest { - - @Autowired private LikePeopleUseCase likePeopleUseCase; - @Autowired private UnlikePeopleUseCase unlikePeopleUseCase; - - @DisplayName("의뢰자는 피플에게 좋아요를 누를 수 있다.") - @Nested - class Like { - - private final Long peopleId = 1L; - - @WithCustomMockUser(memberType = MemberType.ROLE_CLIENT) - @Test - void like() throws Exception { - willDoNothing().given(likePeopleUseCase).like(any(), eq(peopleId)); - - mockMvc.perform(post("/people/{peopleId}/likes", peopleId)) - .andExpect(status().isCreated()); - } - - @WithCustomMockUser(memberType = MemberType.ROLE_CLIENT) - @Test - void like_WhenPeopleIsAlreadyLiked_ShouldBeFailed() throws Exception { - willThrow(new AlreadyLikedException()) - .given(likePeopleUseCase).like(any(), eq(peopleId)); - - mockMvc.perform(post("/people/{peopleId}/likes", peopleId)) - .andExpect(status().isConflict()); - } - - @WithCustomMockUser(memberType = MemberType.ROLE_PEOPLE) - @Test - void like_WhenMemberTypeIsPeople_ShouldBeFailed() throws Exception { - mockMvc.perform(post("/people/{peopleId}/likes", peopleId)) - .andExpect(status().isForbidden()); - } - } - - @DisplayName("의뢰자는 피플에게 눌렀던 좋아요를 취소를 할 수 있다.") - @Nested - class Unlike { - - private final Long peopleId = 1L; - - @WithCustomMockUser(memberType = MemberType.ROLE_CLIENT) - @Test - void unlike() throws Exception { - willDoNothing().given(unlikePeopleUseCase).unlike(any(), eq(peopleId)); - - mockMvc.perform(delete("/people/{peopleId}/likes", peopleId)) - .andExpect(status().isNoContent()); - } - - @WithCustomMockUser(memberType = MemberType.ROLE_PEOPLE) - @Test - void unlike_WhenMemberTypeIsPeople_ShouldBeFailed() throws Exception { - mockMvc.perform(delete("/people/{peopleId}/likes", peopleId)) - .andExpect(status().isForbidden()); - } - } -} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/like/command/ProjectLikeControllerTest.java b/src/test/java/es/princip/getp/api/controller/like/command/ProjectLikeControllerTest.java deleted file mode 100644 index 83375c9a..00000000 --- a/src/test/java/es/princip/getp/api/controller/like/command/ProjectLikeControllerTest.java +++ /dev/null @@ -1,77 +0,0 @@ -package es.princip.getp.api.controller.like.command; - -import es.princip.getp.api.support.ControllerTest; -import es.princip.getp.api.security.annotation.WithCustomMockUser; -import es.princip.getp.application.like.project.port.in.LikeProjectUseCase; -import es.princip.getp.application.like.project.port.in.UnlikeProjectUseCase; -import es.princip.getp.domain.member.model.MemberType; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.web.servlet.ResultActions; - -import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescriptor; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.BDDMockito.willDoNothing; -import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -class ProjectLikeControllerTest extends ControllerTest { - - @Autowired private LikeProjectUseCase likeProjectUseCase; - @Autowired private UnlikeProjectUseCase unlikeProjectUseCase; - - @Nested - @DisplayName("프로젝트 좋아요") - class LikeProject { - - private final Long projectId = 1L; - - private ResultActions perform() throws Exception { - return mockMvc.perform(post("/projects/{projectId}/likes", projectId) - .header("Authorization", "Bearer ${ACCESS_TOKEN}")); - } - - @DisplayName("피플은 프로젝트에 좋아요를 누를 수 있다.") - @WithCustomMockUser(memberType = MemberType.ROLE_PEOPLE) - @Test - void likeProject() throws Exception { - willDoNothing().given(likeProjectUseCase).like(anyLong(), anyLong()); - - perform() - .andExpect(status().isCreated()) - .andDo(restDocs.document( - requestHeaders(authorizationHeaderDescriptor()) - )) - .andDo(print()); - } - } - - @Nested - @DisplayName("프로젝트 좋아요 취소") - class UnlikeProject { - - private final Long projectId = 1L; - - private ResultActions perform() throws Exception { - return mockMvc.perform(delete("/projects/{projectId}/likes", projectId) - .header("Authorization", "Bearer ${ACCESS_TOKEN}")); - } - - @DisplayName("피플은 프로젝트에 눌렀던 좋아요를 취소할 수 있다.") - @WithCustomMockUser(memberType = MemberType.ROLE_PEOPLE) - @Test - void unlikeProject() throws Exception { - willDoNothing().given(unlikeProjectUseCase).unlike(anyLong(), anyLong()); - - perform() - .andExpect(status().isNoContent()) - .andDo(restDocs.document( - requestHeaders(authorizationHeaderDescriptor()) - )) - .andDo(print()); - } - } -} diff --git a/src/test/java/es/princip/getp/api/controller/member/query/MyMemberQueryControllerTest.java b/src/test/java/es/princip/getp/api/controller/member/query/MyMemberQueryControllerTest.java deleted file mode 100644 index 6ccf6aad..00000000 --- a/src/test/java/es/princip/getp/api/controller/member/query/MyMemberQueryControllerTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package es.princip.getp.api.controller.member.query; - -import es.princip.getp.api.support.ControllerTest; -import es.princip.getp.api.security.annotation.WithCustomMockUser; -import es.princip.getp.domain.member.model.MemberType; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.test.web.servlet.ResultActions; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; -import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescriptor; -import static es.princip.getp.api.docs.PayloadDocumentationHelper.responseFields; -import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; -import static org.springframework.restdocs.snippet.Attributes.key; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -class MyMemberQueryControllerTest extends ControllerTest { - - @DisplayName("내 회원 정보 조회") - @Nested - class GetMyMember { - - private ResultActions perform() throws Exception { - return mockMvc.perform(get("/member/me") - .header("Authorization", "Bearer ${ACCESS_TOKEN}")); - } - - @DisplayName("사용자는 자신의 회원 정보를 조회할 수 있다.") - @WithCustomMockUser(memberType = MemberType.ROLE_PEOPLE) - @Test - void getMyMember() throws Exception { - - perform() - .andExpect(status().isOk()) - .andDo( - restDocs.document( - requestHeaders(authorizationHeaderDescriptor()), - responseFields( - getDescriptor("memberId", "회원 ID"), - getDescriptor("email", "이메일"), - getDescriptor("nickname", "닉네임"), - getDescriptor("profileImageUri", "프로필 사진 URI"), - getDescriptor("memberType", "회원 유형"), - getDescriptor("createdAt", "회원 가입일") - .attributes(key("format").value("yyyy-MM-dd'T'HH:mm:ss")), - getDescriptor("updatedAt", "회원 정보 수정일") - .attributes(key("format").value("yyyy-MM-dd'T'HH:mm:ss")) - ) - ) - ) - .andDo(print()); - } - } -} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/people/command/description/request/CreatePeopleRequestDescription.java b/src/test/java/es/princip/getp/api/controller/people/command/description/request/CreatePeopleRequestDescription.java deleted file mode 100644 index 8dd69592..00000000 --- a/src/test/java/es/princip/getp/api/controller/people/command/description/request/CreatePeopleRequestDescription.java +++ /dev/null @@ -1,20 +0,0 @@ -package es.princip.getp.api.controller.people.command.description.request; - -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; -import static org.springframework.restdocs.snippet.Attributes.key; - -public class CreatePeopleRequestDescription { - - public static FieldDescriptor[] description() { - final Class clazz = CreatePeopleRequestDescription.class; - return new FieldDescriptor[] { - getDescriptor("nickname", "닉네임", clazz), - getDescriptor("email", "이메일(기본값은 회원 가입 시 기입한 이메일)", clazz), - getDescriptor("phoneNumber", "전화번호", clazz), - getDescriptor("peopleType", "피플 유형", clazz) - .attributes(key("format").value("TEAM, INDIVIDUAL")) - }; - } -} diff --git a/src/test/java/es/princip/getp/api/controller/people/command/description/request/EditPeopleProfileRequestDescription.java b/src/test/java/es/princip/getp/api/controller/people/command/description/request/EditPeopleProfileRequestDescription.java deleted file mode 100644 index d6923bb3..00000000 --- a/src/test/java/es/princip/getp/api/controller/people/command/description/request/EditPeopleProfileRequestDescription.java +++ /dev/null @@ -1,23 +0,0 @@ -package es.princip.getp.api.controller.people.command.description.request; - -import es.princip.getp.api.controller.people.command.dto.request.EditPeopleProfileRequest; -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; - -public class EditPeopleProfileRequestDescription { - - public static FieldDescriptor[] description() { - final Class clazz = EditPeopleProfileRequest.class; - return new FieldDescriptor[] { - getDescriptor("education.school", "학력", clazz), - getDescriptor("education.major", "전공", clazz), - getDescriptor("activityArea", "활동 지역", clazz), - getDescriptor("introduction", "소개", clazz), - getDescriptor("techStacks[]", "기술 스택", clazz), - getDescriptor("portfolios[].url", "포트폴리오 URL", clazz), - getDescriptor("portfolios[].description", "포트폴리오 설명", clazz), - getDescriptor("hashtags[]", "해시태그", clazz) - }; - } -} diff --git a/src/test/java/es/princip/getp/api/controller/people/command/description/request/UpdatePeopleRequestDescription.java b/src/test/java/es/princip/getp/api/controller/people/command/description/request/UpdatePeopleRequestDescription.java deleted file mode 100644 index 91cd9b2d..00000000 --- a/src/test/java/es/princip/getp/api/controller/people/command/description/request/UpdatePeopleRequestDescription.java +++ /dev/null @@ -1,20 +0,0 @@ -package es.princip.getp.api.controller.people.command.description.request; - -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; -import static org.springframework.restdocs.snippet.Attributes.key; - -public class UpdatePeopleRequestDescription { - - public static FieldDescriptor[] description() { - final Class clazz = UpdatePeopleRequestDescription.class; - return new FieldDescriptor[] { - getDescriptor("nickname", "닉네임", clazz), - getDescriptor("email", "이메일(기본값은 회원 가입 시 기입한 이메일)", clazz), - getDescriptor("phoneNumber", "전화번호", clazz), - getDescriptor("peopleType", "피플 유형", clazz) - .attributes(key("format").value("TEAM, INDIVIDUAL")) - }; - } -} diff --git a/src/test/java/es/princip/getp/api/controller/people/command/description/request/WritePeopleProfileRequestDescription.java b/src/test/java/es/princip/getp/api/controller/people/command/description/request/WritePeopleProfileRequestDescription.java deleted file mode 100644 index 8e5149e3..00000000 --- a/src/test/java/es/princip/getp/api/controller/people/command/description/request/WritePeopleProfileRequestDescription.java +++ /dev/null @@ -1,23 +0,0 @@ -package es.princip.getp.api.controller.people.command.description.request; - -import es.princip.getp.api.controller.people.command.dto.request.RegisterPeopleProfileRequest; -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; - -public class WritePeopleProfileRequestDescription { - - public static FieldDescriptor[] description() { - final Class clazz = RegisterPeopleProfileRequest.class; - return new FieldDescriptor[] { - getDescriptor("education.school", "학력", clazz), - getDescriptor("education.major", "전공", clazz), - getDescriptor("activityArea", "활동 지역", clazz), - getDescriptor("introduction", "소개", clazz), - getDescriptor("techStacks[]", "기술 스택", clazz), - getDescriptor("portfolios[].url", "포트폴리오 URL", clazz), - getDescriptor("portfolios[].description", "포트폴리오 설명", clazz), - getDescriptor("hashtags[]", "해시태그", clazz) - }; - } -} diff --git a/src/test/java/es/princip/getp/api/controller/people/command/description/response/CreatePeopleResponseDescription.java b/src/test/java/es/princip/getp/api/controller/people/command/description/response/CreatePeopleResponseDescription.java deleted file mode 100644 index d0cc0980..00000000 --- a/src/test/java/es/princip/getp/api/controller/people/command/description/response/CreatePeopleResponseDescription.java +++ /dev/null @@ -1,14 +0,0 @@ -package es.princip.getp.api.controller.people.command.description.response; - -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; - -public class CreatePeopleResponseDescription { - - public static FieldDescriptor[] description() { - return new FieldDescriptor[] { - getDescriptor("peopleId", "피플 ID") - }; - } -} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/people/query/MyPeopleProfileQueryControllerTest.java b/src/test/java/es/princip/getp/api/controller/people/query/MyPeopleProfileQueryControllerTest.java deleted file mode 100644 index 99959438..00000000 --- a/src/test/java/es/princip/getp/api/controller/people/query/MyPeopleProfileQueryControllerTest.java +++ /dev/null @@ -1,75 +0,0 @@ -package es.princip.getp.api.controller.people.query; - -import es.princip.getp.api.support.ControllerTest; -import es.princip.getp.api.controller.people.query.description.DetailPeopleProfileResponseDescription; -import es.princip.getp.api.controller.people.query.dto.peopleProfile.DetailPeopleProfileResponse; -import es.princip.getp.api.docs.PayloadDocumentationHelper; -import es.princip.getp.api.security.annotation.WithCustomMockUser; -import es.princip.getp.api.security.details.PrincipalDetails; -import es.princip.getp.application.people.port.in.GetMyPeopleQuery; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.web.servlet.ResultActions; - -import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescriptor; -import static es.princip.getp.domain.member.model.MemberType.ROLE_CLIENT; -import static es.princip.getp.domain.member.model.MemberType.ROLE_PEOPLE; -import static es.princip.getp.fixture.common.HashtagFixture.hashtagsResponse; -import static es.princip.getp.fixture.common.TechStackFixture.techStacksResponse; -import static es.princip.getp.fixture.people.ActivityAreaFixture.activityArea; -import static es.princip.getp.fixture.people.EducationFixture.education; -import static es.princip.getp.fixture.people.IntroductionFixture.introduction; -import static es.princip.getp.fixture.people.PortfolioFixture.portfoliosResponse; -import static org.mockito.BDDMockito.given; -import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -class MyPeopleProfileQueryControllerTest extends ControllerTest { - - @Autowired - private GetMyPeopleQuery getMyPeopleQuery; - - private static final String MY_PEOPLE_PROFILE_URI = "/people/me/profile"; - - @DisplayName("피플은 자신의 프로필을 조회할 수 있다.") - @Nested - class GetMyPeopleProfile { - - private ResultActions perform() throws Exception { - return mockMvc.perform(get(MY_PEOPLE_PROFILE_URI) - .header("Authorization", "Bearer ${ACCESS_TOKEN}")); - } - - @WithCustomMockUser(memberType = ROLE_PEOPLE) - @Test - public void getMyPeopleProfile(PrincipalDetails principalDetails) throws Exception { - Long memberId = principalDetails.getMember().getMemberId(); - DetailPeopleProfileResponse response = new DetailPeopleProfileResponse( - introduction(), - activityArea(), - education(), - techStacksResponse(), - hashtagsResponse(), - portfoliosResponse() - ); - given(getMyPeopleQuery.getDetailProfileByMemberId(memberId)).willReturn(response); - - perform() - .andExpect(status().isOk()) - .andDo(restDocs.document( - requestHeaders(authorizationHeaderDescriptor()), - PayloadDocumentationHelper.responseFields(DetailPeopleProfileResponseDescription.description()) - )) - .andDo(print()); - } - - @WithCustomMockUser(memberType = ROLE_CLIENT) - @Test - public void getMyPeopleProfile_WhenMemberTypeIsClient_ShouldFail() throws Exception { - expectForbidden(perform()); - } - } -} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/people/query/PeopleQueryControllerTest.java b/src/test/java/es/princip/getp/api/controller/people/query/PeopleQueryControllerTest.java deleted file mode 100644 index d83aa672..00000000 --- a/src/test/java/es/princip/getp/api/controller/people/query/PeopleQueryControllerTest.java +++ /dev/null @@ -1,183 +0,0 @@ -package es.princip.getp.api.controller.people.query; - -import es.princip.getp.api.support.ControllerTest; -import es.princip.getp.api.controller.people.query.description.DetailPeopleResponseDescription; -import es.princip.getp.api.controller.people.query.description.PublicDetailPeopleResponseDescription; -import es.princip.getp.api.controller.people.query.dto.people.CardPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.people.DetailPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.people.PublicDetailPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.peopleProfile.CardPeopleProfileResponse; -import es.princip.getp.api.controller.people.query.dto.peopleProfile.DetailPeopleProfileResponse; -import es.princip.getp.api.controller.people.query.dto.peopleProfile.PublicDetailPeopleProfileResponse; -import es.princip.getp.api.docs.PaginationDescription; -import es.princip.getp.api.docs.PayloadDocumentationHelper; -import es.princip.getp.api.security.annotation.WithCustomMockUser; -import es.princip.getp.application.people.port.in.GetPeopleQuery; -import es.princip.getp.domain.people.model.PeopleType; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.*; -import org.springframework.test.web.servlet.ResultActions; - -import java.util.List; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; -import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescriptor; -import static es.princip.getp.api.docs.PageResponseDescriptor.pageResponseFieldDescriptors; -import static es.princip.getp.api.docs.PayloadDocumentationHelper.responseFields; -import static es.princip.getp.domain.member.model.MemberType.ROLE_PEOPLE; -import static es.princip.getp.fixture.common.HashtagFixture.hashtagsResponse; -import static es.princip.getp.fixture.common.TechStackFixture.techStacksResponse; -import static es.princip.getp.fixture.member.NicknameFixture.NICKNAME; -import static es.princip.getp.fixture.member.ProfileImageFixture.profileImage; -import static es.princip.getp.fixture.people.ActivityAreaFixture.activityArea; -import static es.princip.getp.fixture.people.EducationFixture.education; -import static es.princip.getp.fixture.people.IntroductionFixture.introduction; -import static es.princip.getp.fixture.people.PortfolioFixture.portfoliosResponse; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; -import static org.springframework.restdocs.request.RequestDocumentation.*; -import static org.springframework.restdocs.snippet.Attributes.key; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -class PeopleQueryControllerTest extends ControllerTest { - - @Autowired - private GetPeopleQuery getPeopleQuery; - - @DisplayName("사용자는 피플 목록을 조회할 수 있다.") - @Nested - class GetCardPeoplePage { - - private final int page = 0; - private final int size = 3; - private final Sort sort = Sort.by(Sort.Order.desc("peopleId")); - - private ResultActions perform() throws Exception { - return mockMvc.perform(get("/people") - .queryParam("page", String.valueOf(page)) - .queryParam("size", String.valueOf(size)) - .queryParam("sort", "peopleId,desc")); - } - - @Test - public void getCardPeoplePage() throws Exception { - final Pageable pageable = PageRequest.of(page, size, sort); - final List content = List.of( - new CardPeopleResponse( - 1L, - NICKNAME, - profileImage(1L).getUrl(), - PeopleType.INDIVIDUAL, - 0, - 0, - new CardPeopleProfileResponse( - "소개", - activityArea(), - hashtagsResponse() - ) - ) - ); - final Page page = new PageImpl<>(content, pageable, content.size()); - given(getPeopleQuery.getPagedCards(any(Pageable.class))).willReturn(page); - - perform() - .andExpect(status().isOk()) - .andDo( - restDocs.document( - queryParameters(PaginationDescription.description(this.page, size, "peopleId,desc")), - responseFields( - getDescriptor("content[].peopleId", "피플 ID"), - getDescriptor("content[].peopleType", "피플 유형") - .attributes(key("format").value("TEAM, INDIVIDUAL")), - getDescriptor("content[].nickname", "닉네임"), - getDescriptor("content[].profileImageUri", "프로필 이미지 URI"), - getDescriptor("content[].completedProjectsCount", "완수한 프로젝트 수"), - getDescriptor("content[].likesCount", "받은 좋아요 수"), - getDescriptor("content[].profile.introduction", "소개"), - getDescriptor("content[].profile.activityArea", "활동 지역"), - getDescriptor("content[].profile.hashtags[]", "해시태그") - ).and(pageResponseFieldDescriptors()) - ) - ) - .andDo(print()); - } - } - - @DisplayName("사용자는 피플의 상세 정보를 조회할 수 있다.") - @Nested - class GetPeople { - - private final Long peopleId = 1L; - - private ResultActions perform() throws Exception { - return mockMvc.perform(get("/people/{peopleId}", peopleId)); - } - - private ResultActions performWithAccessToken() throws Exception { - return mockMvc.perform(get("/people/{peopleId}", peopleId) - .header("Authorization", "Bearer ${ACCESS_TOKEN}")); - } - - @Test - public void getPeople_WhenUserNotLogined() throws Exception { - PublicDetailPeopleResponse response = new PublicDetailPeopleResponse( - peopleId, - NICKNAME, - profileImage(1L).getUrl(), - PeopleType.INDIVIDUAL, - 0, - 0, - new PublicDetailPeopleProfileResponse( - hashtagsResponse() - ) - ); - given(getPeopleQuery.getPublicDetailById(peopleId)).willReturn(response); - - perform() - .andExpect(status().isOk()) - .andDo( - restDocs.document( - pathParameters(parameterWithName("peopleId").description("피플 ID")), - PayloadDocumentationHelper.responseFields(PublicDetailPeopleResponseDescription.description()) - ) - ); - } - - @Test - @WithCustomMockUser(memberType = ROLE_PEOPLE) - public void getPeople_WhenUserLogined() throws Exception { - DetailPeopleResponse response = new DetailPeopleResponse( - 1L, - NICKNAME, - profileImage(1L).getUrl(), - PeopleType.INDIVIDUAL, - 0, - 0, - new DetailPeopleProfileResponse( - introduction(), - activityArea(), - education(), - techStacksResponse(), - hashtagsResponse(), - portfoliosResponse() - ) - ); - given(getPeopleQuery.getDetailById(peopleId)).willReturn(response); - - performWithAccessToken() - .andExpect(status().isOk()) - .andDo( - restDocs.document( - requestHeaders(authorizationHeaderDescriptor()), - pathParameters(parameterWithName("peopleId").description("피플 ID")), - PayloadDocumentationHelper.responseFields(DetailPeopleResponseDescription.description()) - ) - ); - } - } -} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/people/query/description/DetailPeopleProfileResponseDescription.java b/src/test/java/es/princip/getp/api/controller/people/query/description/DetailPeopleProfileResponseDescription.java deleted file mode 100644 index 82bf47ed..00000000 --- a/src/test/java/es/princip/getp/api/controller/people/query/description/DetailPeopleProfileResponseDescription.java +++ /dev/null @@ -1,22 +0,0 @@ -package es.princip.getp.api.controller.people.query.description; - -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; - - -public class DetailPeopleProfileResponseDescription { - - public static FieldDescriptor[] description() { - return new FieldDescriptor[] { - getDescriptor("education.school", "학력"), - getDescriptor("education.major", "전공"), - getDescriptor("activityArea", "활동 지역"), - getDescriptor("introduction", "소개"), - getDescriptor("techStacks[]", "기술 스택"), - getDescriptor("portfolios[].description", "포트폴리오 설명"), - getDescriptor("portfolios[].url", "포트폴리오 URL"), - getDescriptor("hashtags[]", "해시태그") - }; - } -} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/people/query/description/DetailPeopleResponseDescription.java b/src/test/java/es/princip/getp/api/controller/people/query/description/DetailPeopleResponseDescription.java deleted file mode 100644 index 93da1363..00000000 --- a/src/test/java/es/princip/getp/api/controller/people/query/description/DetailPeopleResponseDescription.java +++ /dev/null @@ -1,30 +0,0 @@ -package es.princip.getp.api.controller.people.query.description; - -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; -import static org.springframework.restdocs.snippet.Attributes.key; - - -public class DetailPeopleResponseDescription { - - public static FieldDescriptor[] description() { - return new FieldDescriptor[] { - getDescriptor("peopleId", "피플 ID"), - getDescriptor("nickname", "닉네임"), - getDescriptor("profileImageUri", "프로필 이미지 URI"), - getDescriptor("peopleType", "피플 유형") - .attributes(key("format").value("TEAM, INDIVIDUAL")), - getDescriptor("completedProjectsCount", "완수한 프로젝트 수"), - getDescriptor("likesCount", "받은 좋아요 수"), - getDescriptor("profile.introduction", "소개"), - getDescriptor("profile.activityArea", "활동 지역"), - getDescriptor("profile.techStacks[]", "기술 스택"), - getDescriptor("profile.education.school", "학교"), - getDescriptor("profile.education.major", "전공"), - getDescriptor("profile.hashtags[]", "해시태그"), - getDescriptor("profile.portfolios[].url", "포트폴리오 URL"), - getDescriptor("profile.portfolios[].description", "포트폴리오 설명") - }; - } -} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/people/query/description/MyPeopleResponseDescription.java b/src/test/java/es/princip/getp/api/controller/people/query/description/MyPeopleResponseDescription.java deleted file mode 100644 index d351d4b0..00000000 --- a/src/test/java/es/princip/getp/api/controller/people/query/description/MyPeopleResponseDescription.java +++ /dev/null @@ -1,25 +0,0 @@ -package es.princip.getp.api.controller.people.query.description; - -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; -import static org.springframework.restdocs.snippet.Attributes.key; - -public class MyPeopleResponseDescription { - - public static FieldDescriptor[] description() { - return new FieldDescriptor[] { - getDescriptor("peopleId", "피플 ID"), - getDescriptor("email", "이메일"), - getDescriptor("nickname", "닉네임"), - getDescriptor("phoneNumber", "전화번호"), - getDescriptor("profileImageUri", "프로필 이미지 URI"), - getDescriptor("peopleType", "피플 유형") - .attributes(key("format").value("TEAM, INDIVIDUAL")), - getDescriptor("completedProjectsCount", "완수한 프로젝트 수"), - getDescriptor("likesCount", "받은 좋아요 수"), - getDescriptor("createdAt", "피플 정보 등록 일시"), - getDescriptor("updatedAt", "최근 피플 정보 수정 일시") - }; - } -} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/people/query/description/PagedDetailPeopleResponseDescription.java b/src/test/java/es/princip/getp/api/controller/people/query/description/PagedDetailPeopleResponseDescription.java deleted file mode 100644 index 3ef7f40a..00000000 --- a/src/test/java/es/princip/getp/api/controller/people/query/description/PagedDetailPeopleResponseDescription.java +++ /dev/null @@ -1,30 +0,0 @@ -package es.princip.getp.api.controller.people.query.description; - -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; -import static org.springframework.restdocs.snippet.Attributes.key; - - -public class PagedDetailPeopleResponseDescription { - - public static FieldDescriptor[] description() { - return new FieldDescriptor[] { - getDescriptor("content[].peopleId", "피플 ID"), - getDescriptor("content[].nickname", "닉네임"), - getDescriptor("content[].profileImageUri", "프로필 이미지 URI"), - getDescriptor("content[].peopleType", "피플 유형") - .attributes(key("format").value("TEAM, INDIVIDUAL")), - getDescriptor("content[].completedProjectsCount", "완수한 프로젝트 수"), - getDescriptor("content[].likesCount", "받은 좋아요 수"), - getDescriptor("content[].profile.introduction", "소개"), - getDescriptor("content[].profile.activityArea", "활동 지역"), - getDescriptor("content[].profile.techStacks[]", "기술 스택"), - getDescriptor("content[].profile.education.school", "학교"), - getDescriptor("content[].profile.education.major", "전공"), - getDescriptor("content[].profile.hashtags[]", "해시태그"), - getDescriptor("content[].profile.portfolios[].url", "포트폴리오 URL"), - getDescriptor("content[].profile.portfolios[].description", "포트폴리오 설명") - }; - } -} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/people/query/description/PublicDetailPeopleResponseDescription.java b/src/test/java/es/princip/getp/api/controller/people/query/description/PublicDetailPeopleResponseDescription.java deleted file mode 100644 index afc70702..00000000 --- a/src/test/java/es/princip/getp/api/controller/people/query/description/PublicDetailPeopleResponseDescription.java +++ /dev/null @@ -1,23 +0,0 @@ -package es.princip.getp.api.controller.people.query.description; - -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; -import static org.springframework.restdocs.snippet.Attributes.key; - - -public class PublicDetailPeopleResponseDescription { - - public static FieldDescriptor[] description() { - return new FieldDescriptor[] { - getDescriptor("peopleId", "피플 ID"), - getDescriptor("nickname", "닉네임"), - getDescriptor("profileImageUri", "프로필 이미지 URI"), - getDescriptor("peopleType", "피플 유형") - .attributes(key("format").value("TEAM, INDIVIDUAL")), - getDescriptor("completedProjectsCount", "완수한 프로젝트 수"), - getDescriptor("likesCount", "받은 좋아요 수"), - getDescriptor("profile.hashtags[]", "해시태그") - }; - } -} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/project/command/ApplyProjectRequestFixture.java b/src/test/java/es/princip/getp/api/controller/project/command/ApplyProjectRequestFixture.java deleted file mode 100644 index 4c11c296..00000000 --- a/src/test/java/es/princip/getp/api/controller/project/command/ApplyProjectRequestFixture.java +++ /dev/null @@ -1,24 +0,0 @@ -package es.princip.getp.api.controller.project.command; - -import es.princip.getp.api.controller.project.command.dto.request.ApplyProjectRequest; -import es.princip.getp.domain.common.model.Duration; - -import java.time.LocalDate; -import java.util.List; - -class ApplyProjectRequestFixture { - - static ApplyProjectRequest applyProjectRequest() { - return new ApplyProjectRequest( - Duration.of( - LocalDate.of(2024, 7, 1), - LocalDate.of(2024, 7, 31) - ), - "프로젝트 지원 설명", - List.of( - "https://example.com/attachment1", - "https://example.com/attachment2" - ) - ); - } -} diff --git a/src/test/java/es/princip/getp/api/controller/project/command/ProjectApplicationControllerTest.java b/src/test/java/es/princip/getp/api/controller/project/command/ProjectApplicationControllerTest.java deleted file mode 100644 index b020975f..00000000 --- a/src/test/java/es/princip/getp/api/controller/project/command/ProjectApplicationControllerTest.java +++ /dev/null @@ -1,84 +0,0 @@ -package es.princip.getp.api.controller.project.command; - -import es.princip.getp.api.support.ControllerTest; -import es.princip.getp.api.controller.project.command.description.ApplyProjectRequestDescription; -import es.princip.getp.api.controller.project.command.description.ApplyProjectResponseDescription; -import es.princip.getp.api.controller.project.command.dto.request.ApplyProjectRequest; -import es.princip.getp.api.docs.PayloadDocumentationHelper; -import es.princip.getp.api.security.annotation.WithCustomMockUser; -import es.princip.getp.application.project.apply.command.ApplyProjectCommand; -import es.princip.getp.application.project.apply.port.in.ApplyProjectUseCase; -import es.princip.getp.domain.member.model.MemberType; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.restdocs.payload.PayloadDocumentation; - -import static es.princip.getp.api.controller.project.command.ApplyProjectRequestFixture.applyProjectRequest; -import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescriptor; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -class ProjectApplicationControllerTest extends ControllerTest { - - @Autowired - private ProjectCommandMapper projectCommandMapper; - - @Autowired - private ApplyProjectUseCase applyProjectUseCase; - - @BeforeEach - void setUp() { - given(projectCommandMapper.mapToCommand(anyLong(), anyLong(), any(ApplyProjectRequest.class))) - .willReturn(mock(ApplyProjectCommand.class)); - } - - @Nested - @DisplayName("프로젝트 지원") - class ApplyForProject { - - private final Long projectId = 1L; - private final Long applicationId = 1L; - private final ApplyProjectRequest request = applyProjectRequest(); - - @Test - @WithCustomMockUser(memberType = MemberType.ROLE_PEOPLE) - @DisplayName("피플은 프로젝트에 지원할 수 있다.") - void applyForProject() throws Exception { - given(applyProjectUseCase.apply(any(ApplyProjectCommand.class))) - .willReturn(applicationId); - - mockMvc.perform(post("/projects/{projectId}/applications", projectId) - .header("Authorization", "Bearer ${ACCESS_TOKEN}") - .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isCreated()) - .andDo( - restDocs.document( - requestHeaders(authorizationHeaderDescriptor()), - pathParameters(parameterWithName("projectId").description("프로젝트 ID")), - PayloadDocumentation.requestFields(ApplyProjectRequestDescription.description()), - PayloadDocumentationHelper.responseFields(ApplyProjectResponseDescription.description()) - ) - ) - .andDo(print()); - } - - @Test - @WithCustomMockUser(memberType = MemberType.ROLE_CLIENT) - @DisplayName("의뢰자는 프로젝트에 지원할 수 없다.") - void applyForProject_WhenMemberTypeIsClient_ShouldFail() throws Exception { - mockMvc.perform(post("/projects/{projectId}/applications", projectId) - .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isForbidden()); - } - } -} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/project/command/ProjectCommissionControllerTest.java b/src/test/java/es/princip/getp/api/controller/project/command/ProjectCommissionControllerTest.java deleted file mode 100644 index 159e7b31..00000000 --- a/src/test/java/es/princip/getp/api/controller/project/command/ProjectCommissionControllerTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package es.princip.getp.api.controller.project.command; - -import es.princip.getp.api.support.ControllerTest; -import es.princip.getp.api.controller.project.command.description.RegisterProjectRequestDescription; -import es.princip.getp.api.controller.project.command.description.RegisterProjectResponseDescription; -import es.princip.getp.api.controller.project.command.dto.request.CommissionProjectRequest; -import es.princip.getp.api.docs.PayloadDocumentationHelper; -import es.princip.getp.api.security.annotation.WithCustomMockUser; -import es.princip.getp.application.project.commission.ProjectCommissionService; -import es.princip.getp.application.project.commission.command.CommissionProjectCommand; -import es.princip.getp.domain.member.model.MemberType; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.restdocs.payload.PayloadDocumentation; -import org.springframework.test.web.servlet.ResultActions; - -import static es.princip.getp.api.controller.project.command.CommissionProjectRequestFixture.registerProjectRequest; -import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescriptor; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -class ProjectCommissionControllerTest extends ControllerTest { - - @Autowired - private ProjectCommissionService projectCommissionService; - - @Autowired - private ProjectCommandMapper projectCommandMapper; - - @DisplayName("프로젝트 의뢰") - @Nested - class CommissionProject { - - final CommissionProjectRequest request = registerProjectRequest(); - - private ResultActions perform() throws Exception { - return mockMvc.perform(post("/projects") - .header("Authorization", "Bearer ${ACCESS_TOKEN}") - .content(objectMapper.writeValueAsString(request))); - } - - @Test - @DisplayName("의뢰자는 프로젝트를 의뢰할 수 있다.") - @WithCustomMockUser(memberType = MemberType.ROLE_CLIENT) - void commissionProject() throws Exception { - given(projectCommandMapper.mapToCommand(anyLong(), any(CommissionProjectRequest.class))) - .willReturn(mock(CommissionProjectCommand.class)); - given(projectCommissionService.commission(any(CommissionProjectCommand.class))) - .willReturn(1L); - - perform() - .andExpect(status().isCreated()) - .andDo(restDocs.document( - requestHeaders(authorizationHeaderDescriptor()), - PayloadDocumentation.requestFields(RegisterProjectRequestDescription.description()), - PayloadDocumentationHelper.responseFields(RegisterProjectResponseDescription.description()) - )) - .andDo(print()); - } - } -} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/project/command/description/ApplyProjectRequestDescription.java b/src/test/java/es/princip/getp/api/controller/project/command/description/ApplyProjectRequestDescription.java deleted file mode 100644 index 5106823b..00000000 --- a/src/test/java/es/princip/getp/api/controller/project/command/description/ApplyProjectRequestDescription.java +++ /dev/null @@ -1,19 +0,0 @@ -package es.princip.getp.api.controller.project.command.description; - -import es.princip.getp.api.controller.project.command.dto.request.ApplyProjectRequest; -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; - -public class ApplyProjectRequestDescription { - - public static FieldDescriptor[] description() { - final Class clazz = ApplyProjectRequest.class; - return new FieldDescriptor[] { - getDescriptor("expectedDuration.startDate", "예상 작업 시작 기간", clazz), - getDescriptor("expectedDuration.endDate", "예상 작업 종료 기간", clazz), - getDescriptor("description", "프로젝트 지원 설명", clazz), - getDescriptor("attachmentFiles", "첨부 파일", clazz) - }; - } -} diff --git a/src/test/java/es/princip/getp/api/controller/project/command/description/ApplyProjectResponseDescription.java b/src/test/java/es/princip/getp/api/controller/project/command/description/ApplyProjectResponseDescription.java deleted file mode 100644 index b5007f76..00000000 --- a/src/test/java/es/princip/getp/api/controller/project/command/description/ApplyProjectResponseDescription.java +++ /dev/null @@ -1,14 +0,0 @@ -package es.princip.getp.api.controller.project.command.description; - -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; - -public class ApplyProjectResponseDescription { - - public static FieldDescriptor[] description() { - return new FieldDescriptor[] { - getDescriptor("applicationId", "프로젝트 지원 ID") - }; - } -} diff --git a/src/test/java/es/princip/getp/api/controller/project/command/description/RegisterProjectRequestDescription.java b/src/test/java/es/princip/getp/api/controller/project/command/description/RegisterProjectRequestDescription.java deleted file mode 100644 index ab569f5f..00000000 --- a/src/test/java/es/princip/getp/api/controller/project/command/description/RegisterProjectRequestDescription.java +++ /dev/null @@ -1,24 +0,0 @@ -package es.princip.getp.api.controller.project.command.description; - -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; - -public class RegisterProjectRequestDescription { - - public static FieldDescriptor[] description() { - return new FieldDescriptor[] { - getDescriptor("title", "제목"), - getDescriptor("payment", "금액"), - getDescriptor("applicationDuration.startDate", "지원자 모집 시작 기간"), - getDescriptor("applicationDuration.endDate", "지원자 모집 종료 기간"), - getDescriptor("estimatedDuration.startDate", "예상 작업 시작 기간"), - getDescriptor("estimatedDuration.endDate", "예상 작업 종료 기간"), - getDescriptor("description", "설명"), - getDescriptor("meetingType", "미팅 방식"), - getDescriptor("category", "카테고리"), - getDescriptor("attachmentFiles[]", "첨부 파일"), - getDescriptor("hashtags[]", "해시태그") - }; - } -} diff --git a/src/test/java/es/princip/getp/api/controller/project/command/description/RegisterProjectResponseDescription.java b/src/test/java/es/princip/getp/api/controller/project/command/description/RegisterProjectResponseDescription.java deleted file mode 100644 index 497a54bb..00000000 --- a/src/test/java/es/princip/getp/api/controller/project/command/description/RegisterProjectResponseDescription.java +++ /dev/null @@ -1,14 +0,0 @@ -package es.princip.getp.api.controller.project.command.description; - -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; - -public class RegisterProjectResponseDescription { - - public static FieldDescriptor[] description() { - return new FieldDescriptor[] { - getDescriptor("projectId", "프로젝트 ID") - }; - } -} diff --git a/src/test/java/es/princip/getp/api/controller/project/command/description/ScheduleMeetingRequestDescription.java b/src/test/java/es/princip/getp/api/controller/project/command/description/ScheduleMeetingRequestDescription.java deleted file mode 100644 index f6c4b4bb..00000000 --- a/src/test/java/es/princip/getp/api/controller/project/command/description/ScheduleMeetingRequestDescription.java +++ /dev/null @@ -1,22 +0,0 @@ -package es.princip.getp.api.controller.project.command.description; - -import es.princip.getp.api.controller.project.command.dto.request.ScheduleMeetingRequest; -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; - -public class ScheduleMeetingRequestDescription { - - public static FieldDescriptor[] description() { - final Class clazz = ScheduleMeetingRequest.class; - return new FieldDescriptor[] { - getDescriptor("applicantId", "지원자 ID", clazz), - getDescriptor("location", "미팅 장소", clazz), - getDescriptor("schedule.date", "미팅 날짜", clazz), - getDescriptor("schedule.startTime", "미팅 시작 시간", clazz), - getDescriptor("schedule.endTime", "미팅 종료 시간", clazz), - getDescriptor("phoneNumber", "연락처", clazz), - getDescriptor("description", "요구사항", clazz) - }; - } -} diff --git a/src/test/java/es/princip/getp/api/controller/project/command/description/ScheduleMeetingResponseDescription.java b/src/test/java/es/princip/getp/api/controller/project/command/description/ScheduleMeetingResponseDescription.java deleted file mode 100644 index 3b46f950..00000000 --- a/src/test/java/es/princip/getp/api/controller/project/command/description/ScheduleMeetingResponseDescription.java +++ /dev/null @@ -1,14 +0,0 @@ -package es.princip.getp.api.controller.project.command.description; - -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; - -public class ScheduleMeetingResponseDescription { - - public static FieldDescriptor[] description() { - return new FieldDescriptor[] { - getDescriptor("meetingId","미팅 ID") - }; - } -} diff --git a/src/test/java/es/princip/getp/api/controller/project/query/AppliedProjectQueryControllerTest.java b/src/test/java/es/princip/getp/api/controller/project/query/AppliedProjectQueryControllerTest.java deleted file mode 100644 index fd0213e1..00000000 --- a/src/test/java/es/princip/getp/api/controller/project/query/AppliedProjectQueryControllerTest.java +++ /dev/null @@ -1,92 +0,0 @@ -package es.princip.getp.api.controller.project.query; - -import es.princip.getp.api.support.ControllerTest; -import es.princip.getp.api.controller.project.query.description.AppliedProjectCardResponseDescription; -import es.princip.getp.api.controller.project.query.dto.AppliedProjectCardResponse; -import es.princip.getp.api.docs.PaginationDescription; -import es.princip.getp.api.docs.PayloadDocumentationHelper; -import es.princip.getp.api.security.annotation.WithCustomMockUser; -import es.princip.getp.application.project.apply.port.in.GetAppliedProjectQuery; -import es.princip.getp.domain.common.model.Duration; -import es.princip.getp.domain.member.model.MemberType; -import es.princip.getp.domain.project.commission.model.ProjectStatus; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.*; -import org.springframework.test.web.servlet.ResultActions; - -import java.time.LocalDate; -import java.util.List; - -import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescriptor; -import static es.princip.getp.api.docs.PageResponseDescriptor.pageResponseFieldDescriptors; -import static es.princip.getp.fixture.common.HashtagFixture.hashtagsResponse; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.BDDMockito.given; -import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; -import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -public class AppliedProjectQueryControllerTest extends ControllerTest { - - @Autowired - private GetAppliedProjectQuery getAppliedProjectQuery; - - @DisplayName("피플은 자신이 지원한 프로젝트 목록을 조회할 수 있다.") - @Nested - class GetMyAppliedProjects { - - private final int page = 0; - private final int size = 10; - private final Sort sort = Sort.by(Sort.Order.desc("projectId")); - - private ResultActions perform() throws Exception { - return mockMvc.perform(get("/people/me/projects") - .header("Authorization", "Bearer ${ACCESS_TOKEN}") - .queryParam("page", String.valueOf(page)) - .queryParam("size", String.valueOf(size)) - .queryParam("sort", "projectId,desc")); - } - - @Test - @WithCustomMockUser(memberType = MemberType.ROLE_PEOPLE) - public void getMyAppliedProjects() throws Exception { - final Pageable pageable = PageRequest.of(page, size, sort); - final List content = List.of( - new AppliedProjectCardResponse( - 1L, - "프로젝트 제목", - 1_000_000L, - 5L, - 30L, - Duration.of( - LocalDate.of(2024, 7, 1), - LocalDate.of(2024, 7, 31) - ), - hashtagsResponse(), - "프로젝트 설명", - ProjectStatus.APPLYING - ) - ); - final Page page = new PageImpl<>(content, pageable, content.size()); - given(getAppliedProjectQuery.getPagedCards(anyLong(), any(Pageable.class))) - .willReturn(page); - - perform() - .andExpect(status().isOk()) - .andDo( - restDocs.document( - requestHeaders(authorizationHeaderDescriptor()), - queryParameters(PaginationDescription.description(this.page, size, "projectId,desc")), - PayloadDocumentationHelper.responseFields(AppliedProjectCardResponseDescription.description()) - .and(pageResponseFieldDescriptors()) - ) - ) - .andDo(print()); - } - } -} diff --git a/src/test/java/es/princip/getp/api/controller/project/query/CommissionedProjectQueryControllerTest.java b/src/test/java/es/princip/getp/api/controller/project/query/CommissionedProjectQueryControllerTest.java deleted file mode 100644 index db4ff649..00000000 --- a/src/test/java/es/princip/getp/api/controller/project/query/CommissionedProjectQueryControllerTest.java +++ /dev/null @@ -1,93 +0,0 @@ -package es.princip.getp.api.controller.project.query; - -import es.princip.getp.api.support.ControllerTest; -import es.princip.getp.api.controller.project.query.description.GetMyCommissionedProjectQueryParameterDescription; -import es.princip.getp.api.controller.project.query.description.ProjectCardResponseDescription; -import es.princip.getp.api.controller.project.query.dto.CommissionedProjectCardResponse; -import es.princip.getp.api.docs.PayloadDocumentationHelper; -import es.princip.getp.api.security.annotation.WithCustomMockUser; -import es.princip.getp.application.project.commission.port.in.GetCommissionedProjectQuery; -import es.princip.getp.domain.common.model.Duration; -import es.princip.getp.domain.member.model.MemberType; -import es.princip.getp.domain.project.commission.model.ProjectStatus; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.*; -import org.springframework.restdocs.request.RequestDocumentation; -import org.springframework.test.web.servlet.ResultActions; - -import java.time.LocalDate; -import java.util.List; - -import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescriptor; -import static es.princip.getp.api.docs.PageResponseDescriptor.pageResponseFieldDescriptors; -import static es.princip.getp.fixture.common.HashtagFixture.hashtagsResponse; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.BDDMockito.given; -import static org.springframework.data.domain.Sort.Order.desc; -import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -class CommissionedProjectQueryControllerTest extends ControllerTest { - - @Autowired - private GetCommissionedProjectQuery getCommissionedProjectQuery; - - @Nested - @DisplayName("의뢰한 프로젝트 목록 조회") - class GetMyCommissionedProjects { - - private final int page = 0; - private final int size = 10; - private final Sort sort = Sort.by(desc("projectId")); - - private ResultActions perform() throws Exception { - return mockMvc.perform(get("/client/me/projects") - .header("Authorization", "Bearer ${ACCESS_TOKEN}") - .queryParam("page", String.valueOf(page)) - .queryParam("size", String.valueOf(size)) - .queryParam("sort", "projectId,desc")); - } - - @Test - @WithCustomMockUser(memberType = MemberType.ROLE_CLIENT) - @DisplayName("의뢰자는 자신이 의뢰한 프로젝트 목록을 조회할 수 있다.") - public void getMyCommissionedProjects() throws Exception { - final Pageable pageable = PageRequest.of(page, size, sort); - final List content = List.of( - new CommissionedProjectCardResponse( - 1L, - "프로젝트 제목", - 1_000_000L, - 5L, - 30L, - Duration.of( - LocalDate.of(2024, 7, 1), - LocalDate.of(2024, 7, 31) - ), - hashtagsResponse(), - "프로젝트 설명", - ProjectStatus.APPLYING - ) - ); - final Page page = new PageImpl<>(content, pageable, content.size()); - given(getCommissionedProjectQuery.getPagedCards(anyLong(), anyBoolean(), any(Pageable.class))) - .willReturn(page); - - perform() - .andExpect(status().isOk()) - .andDo( - restDocs.document( - requestHeaders(authorizationHeaderDescriptor()), - RequestDocumentation.queryParameters(GetMyCommissionedProjectQueryParameterDescription.description()), - PayloadDocumentationHelper.responseFields(ProjectCardResponseDescription.description()) - .and(pageResponseFieldDescriptors()) - ) - ) - .andDo(print()); - } - } -} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/project/query/ProjectQueryControllerTest.java b/src/test/java/es/princip/getp/api/controller/project/query/ProjectQueryControllerTest.java deleted file mode 100644 index 9d41ab04..00000000 --- a/src/test/java/es/princip/getp/api/controller/project/query/ProjectQueryControllerTest.java +++ /dev/null @@ -1,161 +0,0 @@ -package es.princip.getp.api.controller.project.query; - -import es.princip.getp.api.controller.common.dto.HashtagsResponse; -import es.princip.getp.api.controller.project.query.description.ProjectCardResponseDescription; -import es.princip.getp.api.controller.project.query.description.ProjectDetailResponseDescription; -import es.princip.getp.api.controller.project.query.dto.AttachmentFilesResponse; -import es.princip.getp.api.controller.project.query.dto.ProjectCardResponse; -import es.princip.getp.api.controller.project.query.dto.ProjectClientResponse; -import es.princip.getp.api.controller.project.query.dto.ProjectDetailResponse; -import es.princip.getp.api.docs.PayloadDocumentationHelper; -import es.princip.getp.api.support.ControllerTest; -import es.princip.getp.application.project.commission.port.in.GetProjectQuery; -import es.princip.getp.domain.common.model.AttachmentFile; -import es.princip.getp.domain.common.model.Duration; -import es.princip.getp.domain.common.model.Hashtag; -import es.princip.getp.domain.project.commission.model.MeetingType; -import es.princip.getp.domain.project.commission.model.ProjectCategory; -import es.princip.getp.domain.project.commission.model.ProjectStatus; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.*; -import org.springframework.test.web.servlet.ResultActions; - -import java.time.LocalDate; -import java.util.List; - -import static es.princip.getp.api.docs.HeaderDescriptorHelper.authorizationHeaderDescriptor; -import static es.princip.getp.api.docs.PageResponseDescriptor.pageResponseFieldDescriptors; -import static es.princip.getp.fixture.client.AddressFixture.address; -import static es.princip.getp.fixture.member.NicknameFixture.NICKNAME; -import static org.mockito.BDDMockito.given; -import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -class ProjectQueryControllerTest extends ControllerTest { - - @Autowired - private GetProjectQuery getProjectQuery; - - @DisplayName("프로젝트 목록 조회") - @Nested - class GetProjects { - - final int page = 0; - final int pageSize = 10; - final Sort sort = Sort.by(Sort.Order.desc("projectId")); - final Pageable pageable = PageRequest.of(page, pageSize, sort); - - private ResultActions perform() throws Exception { - return mockMvc.perform(get("/projects") - .header("Authorization", "Bearer ${ACCESS_TOKEN}") - .queryParam("page", String.valueOf(page)) - .queryParam("size", String.valueOf(pageSize)) - .queryParam("sort", "projectId,desc")); - } - - @Test - @DisplayName("사용자는 프로젝트 목록을 조회할 수 있다.") - void getProjects() throws Exception { - final List content = List.of( - new ProjectCardResponse( - 1L, - "프로젝트 제목", - 1_000_000L, - 5L, - 10L, - Duration.of( - LocalDate.of(2024, 7, 1), - LocalDate.of(2024, 7, 7) - ), - HashtagsResponse.from( - List.of( - Hashtag.from("#해시태그1"), - Hashtag.from("#해시태그2") - ) - ), - "프로젝트 설명", - ProjectStatus.APPLYING - ) - ); - final Page response = new PageImpl<>(content, pageable, content.size()); - given(getProjectQuery.getPagedCards(pageable)).willReturn(response); - - perform() - .andExpect(status().isOk()) - .andDo(restDocs.document( - requestHeaders(authorizationHeaderDescriptor()), - PayloadDocumentationHelper.responseFields(ProjectCardResponseDescription.description()) - .and(pageResponseFieldDescriptors()) - )) - .andDo(print()); - } - } - - @DisplayName("프로젝트 상세 조회") - @Nested - class GetProjectByProjectId { - - private final Long projectId = 1L; - - private ResultActions perform() throws Exception { - return mockMvc.perform(get("/projects/{projectId}", projectId) - .header("Authorization", "Bearer ${ACCESS_TOKEN}")); - } - - @Test - @DisplayName("사용자는 프로젝트의 상세 정보를 조회할 수 있다.") - void getProjectByProjectId() throws Exception { - ProjectDetailResponse response = new ProjectDetailResponse( - projectId, - "프로젝트 제목", - 1_000_000L, - Duration.of( - LocalDate.of(2024, 7, 1), - LocalDate.of(2024, 7, 7) - ), - Duration.of( - LocalDate.of(2024, 7, 14), - LocalDate.of(2024, 7, 21) - ), - "프로젝트 설명", - MeetingType.IN_PERSON, - ProjectCategory.BACKEND, - ProjectStatus.APPLYING, - AttachmentFilesResponse.from( - List.of( - AttachmentFile.from("https://example.com/attachment1"), - AttachmentFile.from("https://example.com/attachment2") - ) - ), - HashtagsResponse.from( - List.of( - Hashtag.from("#해시태그1"), - Hashtag.from("#해시태그2") - ) - ), - 5L, - new ProjectClientResponse( - 1L, - NICKNAME, - address() - ) - ); - given(getProjectQuery.getDetailById(projectId)).willReturn(response); - - perform() - .andExpect(status().isOk()) - .andDo(restDocs.document( - requestHeaders(authorizationHeaderDescriptor()), - pathParameters(parameterWithName("projectId").description("프로젝트 ID")), - PayloadDocumentationHelper.responseFields(ProjectDetailResponseDescription.description()) - )) - .andDo(print()); - } - } -} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/api/controller/project/query/description/AppliedProjectCardResponseDescription.java b/src/test/java/es/princip/getp/api/controller/project/query/description/AppliedProjectCardResponseDescription.java deleted file mode 100644 index 61086643..00000000 --- a/src/test/java/es/princip/getp/api/controller/project/query/description/AppliedProjectCardResponseDescription.java +++ /dev/null @@ -1,23 +0,0 @@ -package es.princip.getp.api.controller.project.query.description; - -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; - -public class AppliedProjectCardResponseDescription { - - public static FieldDescriptor[] description() { - return new FieldDescriptor[] { - getDescriptor("content[].projectId", "프로젝트 ID"), - getDescriptor("content[].title", "제목"), - getDescriptor("content[].payment", "금액"), - getDescriptor("content[].applicantsCount", "지원자 수"), - getDescriptor("content[].estimatedDays", "예상 작업 일수"), - getDescriptor("content[].applicationDuration.startDate", "지원자 모집 시작 기간"), - getDescriptor("content[].applicationDuration.endDate", "지원자 모집 종료 기간"), - getDescriptor("content[].hashtags[]", "해시태그"), - getDescriptor("content[].description", "상세 설명"), - getDescriptor("content[].status", "프로젝트 상태") - }; - } -} diff --git a/src/test/java/es/princip/getp/api/controller/project/query/description/GetMyCommissionedProjectQueryParameterDescription.java b/src/test/java/es/princip/getp/api/controller/project/query/description/GetMyCommissionedProjectQueryParameterDescription.java deleted file mode 100644 index 45c77043..00000000 --- a/src/test/java/es/princip/getp/api/controller/project/query/description/GetMyCommissionedProjectQueryParameterDescription.java +++ /dev/null @@ -1,26 +0,0 @@ -package es.princip.getp.api.controller.project.query.description; - -import org.springframework.restdocs.request.ParameterDescriptor; - -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.snippet.Attributes.key; - -public class GetMyCommissionedProjectQueryParameterDescription { - - public static ParameterDescriptor[] description() { - return new ParameterDescriptor[] { - parameterWithName("page").description("페이지 번호") - .optional() - .attributes(key("default").value("0")), - parameterWithName("size").description("페이지 크기") - .optional() - .attributes(key("default").value("10")), - parameterWithName("sort").description("정렬 방식") - .optional() - .attributes(key("default").value("projectId,desc")), - parameterWithName("cancelled").description("만료된 프로젝트 보기 여부") - .optional() - .attributes(key("default").value("false")) - }; - } -} diff --git a/src/test/java/es/princip/getp/api/controller/project/query/description/ProjectCardResponseDescription.java b/src/test/java/es/princip/getp/api/controller/project/query/description/ProjectCardResponseDescription.java deleted file mode 100644 index 2734b744..00000000 --- a/src/test/java/es/princip/getp/api/controller/project/query/description/ProjectCardResponseDescription.java +++ /dev/null @@ -1,23 +0,0 @@ -package es.princip.getp.api.controller.project.query.description; - -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; - -public class ProjectCardResponseDescription { - - public static FieldDescriptor[] description() { - return new FieldDescriptor[] { - getDescriptor("content[].projectId", "프로젝트 ID"), - getDescriptor("content[].title", "제목"), - getDescriptor("content[].payment", "금액"), - getDescriptor("content[].applicantsCount", "지원자 수"), - getDescriptor("content[].estimatedDays", "예상 작업 일수"), - getDescriptor("content[].applicationDuration.startDate", "지원자 모집 시작 기간"), - getDescriptor("content[].applicationDuration.endDate", "지원자 모집 종료 기간"), - getDescriptor("content[].hashtags[]", "해시태그"), - getDescriptor("content[].description", "상세 설명"), - getDescriptor("content[].status", "프로젝트 상태") - }; - } -} diff --git a/src/test/java/es/princip/getp/api/controller/project/query/description/ProjectDetailResponseDescription.java b/src/test/java/es/princip/getp/api/controller/project/query/description/ProjectDetailResponseDescription.java deleted file mode 100644 index aafcef42..00000000 --- a/src/test/java/es/princip/getp/api/controller/project/query/description/ProjectDetailResponseDescription.java +++ /dev/null @@ -1,32 +0,0 @@ -package es.princip.getp.api.controller.project.query.description; - -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; - -public class ProjectDetailResponseDescription { - - public static FieldDescriptor[] description() { - return new FieldDescriptor[] { - getDescriptor("projectId", "프로젝트 ID"), - getDescriptor("title", "프로젝트 제목"), - getDescriptor("payment", "프로젝트 금액"), - getDescriptor("applicationDuration.startDate", "지원자 모집 시작 날짜"), - getDescriptor("applicationDuration.endDate", "지원자 모집 종료 날짜"), - getDescriptor("estimatedDuration.startDate", "예상 작업 시작 날짜"), - getDescriptor("estimatedDuration.endDate", "예상 작업 종료 날짜"), - getDescriptor("description", "프로젝트 설명"), - getDescriptor("meetingType", "프로젝트 미팅 방식"), - getDescriptor("category", "프로젝트 카테고리"), - getDescriptor("status", "프로젝트 상태"), - getDescriptor("attachmentFiles[]", "첨부 파일 목록"), - getDescriptor("hashtags[]", "해시태그"), - getDescriptor("likesCount", "프로젝트 좋아요 수"), - getDescriptor("client.clientId", "의뢰자 ID"), - getDescriptor("client.nickname", "의뢰자 닉네임"), - getDescriptor("client.address.zipcode", "의뢰자 우편번호"), - getDescriptor("client.address.street", "의뢰자 도로명"), - getDescriptor("client.address.detail", "의뢰자 상세 주소") - }; - } -} diff --git a/src/test/java/es/princip/getp/api/docs/FieldDescriptorHelper.java b/src/test/java/es/princip/getp/api/docs/FieldDescriptorHelper.java deleted file mode 100644 index ea8af66c..00000000 --- a/src/test/java/es/princip/getp/api/docs/FieldDescriptorHelper.java +++ /dev/null @@ -1,62 +0,0 @@ -package es.princip.getp.api.docs; - -import org.springframework.restdocs.constraints.Constraint; -import org.springframework.restdocs.constraints.ConstraintDescriptionResolver; -import org.springframework.restdocs.constraints.ResourceBundleConstraintDescriptionResolver; -import org.springframework.restdocs.constraints.ValidatorConstraintResolver; -import org.springframework.restdocs.payload.FieldDescriptor; - -import java.util.MissingResourceException; -import java.util.ResourceBundle; -import java.util.stream.Collectors; - -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.snippet.Attributes.key; - -public class FieldDescriptorHelper { - - private static final ResourceBundle resourceBundle = ResourceBundle.getBundle("messages/validation"); - private static final String DELIMITER = " "; - private static final ValidatorConstraintResolver constraintResolver = new ValidatorConstraintResolver(); - private static final ConstraintDescriptionResolver descriptionResolver = new ResourceBundleConstraintDescriptionResolver(); - - public static FieldDescriptor getDescriptor(String path, String description) { - return getDescriptor(path, description, ""); - } - - public static FieldDescriptor getDescriptor(String path, String description, String constraint) { - return fieldWithPath(path) - .description(description) - .attributes(key("constraints").value(constraint)); - } - - public static FieldDescriptor getDescriptor(String path, String description, Class clazz) { - String[] properties = path.split("\\."); - String property = properties[properties.length - 1]; - String constraintMessages = constraintResolver.resolveForProperty(property, clazz).stream() - .map(FieldDescriptorHelper::getConstraintMessage) - .collect(Collectors.joining(DELIMITER)); - return getDescriptor(path, description, constraintMessages); - } - - private static String getConstraintMessage(Constraint constraint) { - try { - return getConstraintMessageFromResourceBundle(constraint); - } catch (MissingResourceException e) { - return getDefaultConstraintMessage(constraint); - } - } - - private static String getConstraintMessageFromResourceBundle( - Constraint constraint - ) throws MissingResourceException { - String key = ((String) constraint.getConfiguration().get("message")) - .replace("{", "") - .replace("}", ""); - return resourceBundle.getString(key); - } - - private static String getDefaultConstraintMessage(Constraint constraint) { - return descriptionResolver.resolveDescription(constraint); - } -} diff --git a/src/test/java/es/princip/getp/api/docs/PageResponseDescriptor.java b/src/test/java/es/princip/getp/api/docs/PageResponseDescriptor.java deleted file mode 100644 index a288b751..00000000 --- a/src/test/java/es/princip/getp/api/docs/PageResponseDescriptor.java +++ /dev/null @@ -1,23 +0,0 @@ -package es.princip.getp.api.docs; - -import org.springframework.restdocs.payload.FieldDescriptor; - -import static es.princip.getp.api.docs.FieldDescriptorHelper.getDescriptor; - -public class PageResponseDescriptor { - - public static FieldDescriptor[] pageResponseFieldDescriptors() { - return new FieldDescriptor[] { - getDescriptor("pageInfo.totalPages", "전체 페이지 수"), - getDescriptor("pageInfo.totalElements", "전체 요소 수"), - getDescriptor("pageInfo.size", "페이지 크기"), - getDescriptor("pageInfo.number", "현재 페이지 번호"), - getDescriptor("pageInfo.numberOfElements", "현재 페이지 요소 수"), - getDescriptor("pageInfo.first", "첫 페이지 여부"), - getDescriptor("pageInfo.last", "마지막 페이지 여부"), - getDescriptor("pageInfo.empty", "비어있는 페이지 여부"), - getDescriptor("pageInfo.sort.property", "정렬 속성"), - getDescriptor("pageInfo.sort.direction", "정렬 방향") - }; - } -} diff --git a/src/test/java/es/princip/getp/api/docs/PaginationDescription.java b/src/test/java/es/princip/getp/api/docs/PaginationDescription.java deleted file mode 100644 index f65e0859..00000000 --- a/src/test/java/es/princip/getp/api/docs/PaginationDescription.java +++ /dev/null @@ -1,17 +0,0 @@ -package es.princip.getp.api.docs; - - -import org.springframework.restdocs.request.ParameterDescriptor; - -import static es.princip.getp.api.docs.ParameterDescriptorHelper.getDescriptor; - -public class PaginationDescription { - - public static ParameterDescriptor[] description(final int page, final int size, final String sort) { - return new ParameterDescriptor[] { - getDescriptor("page", "페이지 번호", "default", String.valueOf(page)), - getDescriptor("size", "페이지 크기", "default", String.valueOf(size)), - getDescriptor("sort", "정렬 방식", "default", sort) - }; - } -} diff --git a/src/test/java/es/princip/getp/api/docs/PayloadDocumentationHelper.java b/src/test/java/es/princip/getp/api/docs/PayloadDocumentationHelper.java deleted file mode 100644 index 0cc2e82a..00000000 --- a/src/test/java/es/princip/getp/api/docs/PayloadDocumentationHelper.java +++ /dev/null @@ -1,15 +0,0 @@ -package es.princip.getp.api.docs; - -import org.springframework.restdocs.payload.FieldDescriptor; -import org.springframework.restdocs.payload.ResponseFieldsSnippet; - -import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath; - -public class PayloadDocumentationHelper { - - public static ResponseFieldsSnippet responseFields(FieldDescriptor... descriptors) { - return org.springframework.restdocs.payload.PayloadDocumentation.responseFields( - beneathPath("data").withSubsectionId("data") - ).and(descriptors); - } -} diff --git a/src/test/java/es/princip/getp/api/docs/SpringRestDocsConfig.java b/src/test/java/es/princip/getp/api/docs/SpringRestDocsConfig.java deleted file mode 100644 index dad5056e..00000000 --- a/src/test/java/es/princip/getp/api/docs/SpringRestDocsConfig.java +++ /dev/null @@ -1,20 +0,0 @@ -package es.princip.getp.api.docs; - -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; -import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; -import org.springframework.restdocs.operation.preprocess.Preprocessors; - -@TestConfiguration -public class SpringRestDocsConfig { - - @Bean - public RestDocumentationResultHandler write() { - return MockMvcRestDocumentation.document( - "{class-name}/{method-name}", - Preprocessors.preprocessRequest(Preprocessors.prettyPrint()), - Preprocessors.preprocessResponse(Preprocessors.prettyPrint()) - ); - } -} diff --git a/src/test/java/es/princip/getp/fixture/client/ClientFixture.java b/src/test/java/es/princip/getp/fixture/client/ClientFixture.java deleted file mode 100644 index a01129b9..00000000 --- a/src/test/java/es/princip/getp/fixture/client/ClientFixture.java +++ /dev/null @@ -1,26 +0,0 @@ -package es.princip.getp.fixture.client; - -import es.princip.getp.domain.client.model.Client; - -import java.util.List; -import java.util.stream.LongStream; - -import static es.princip.getp.fixture.common.EmailFixture.email; - -public class ClientFixture { - - public static Client client(final Long memberId) { - return Client.builder() - .memberId(memberId) - .email(email()) - .address(AddressFixture.address()) - .bankAccount(BankAccountFixture.bankAccount()) - .build(); - } - - public static List clientList(final int size, final Long memberIdBias) { - return LongStream.range(0, size) - .mapToObj(i -> client(memberIdBias + i)) - .toList(); - } -} diff --git a/src/test/java/es/princip/getp/fixture/common/HashtagFixture.java b/src/test/java/es/princip/getp/fixture/common/HashtagFixture.java deleted file mode 100644 index 99cbf4be..00000000 --- a/src/test/java/es/princip/getp/fixture/common/HashtagFixture.java +++ /dev/null @@ -1,32 +0,0 @@ -package es.princip.getp.fixture.common; - -import es.princip.getp.api.controller.common.dto.HashtagsResponse; -import es.princip.getp.domain.common.model.Hashtag; - -import java.util.List; - -public class HashtagFixture { - - public static List hashtags() { - return List.of( - Hashtag.from("#해시태그1"), - Hashtag.from("#해시태그2") - ); - } - - public static List hashtagsRequest() { - return List.of( - "#해시태그1", - "#해시태그2" - ); - } - - public static HashtagsResponse hashtagsResponse() { - return HashtagsResponse.from( - List.of( - Hashtag.from("#해시태그1"), - Hashtag.from("#해시태그2") - ) - ); - } -} diff --git a/src/test/java/es/princip/getp/fixture/member/MemberFixture.java b/src/test/java/es/princip/getp/fixture/member/MemberFixture.java deleted file mode 100644 index 08f8eaf9..00000000 --- a/src/test/java/es/princip/getp/fixture/member/MemberFixture.java +++ /dev/null @@ -1,23 +0,0 @@ -package es.princip.getp.fixture.member; - -import es.princip.getp.domain.common.model.Email; -import es.princip.getp.domain.member.model.Member; -import es.princip.getp.domain.member.model.MemberType; - -import java.util.List; -import java.util.stream.IntStream; - -import static es.princip.getp.fixture.common.EmailFixture.email; - -public class MemberFixture { - - public static Member member(final MemberType memberType) { - return Member.of(email(), PasswordFixture.password(), memberType); - } - - public static List memberList(final int size, final int bias, final MemberType memberType) { - return IntStream.range(bias, bias + size) - .mapToObj(i -> Member.of(Email.from("test" + i + "@example.com"), PasswordFixture.password(), memberType)) - .toList(); - } -} diff --git a/src/test/java/es/princip/getp/fixture/project/ProjectApplicationFixture.java b/src/test/java/es/princip/getp/fixture/project/ProjectApplicationFixture.java deleted file mode 100644 index c1135c0b..00000000 --- a/src/test/java/es/princip/getp/fixture/project/ProjectApplicationFixture.java +++ /dev/null @@ -1,28 +0,0 @@ -package es.princip.getp.fixture.project; - -import es.princip.getp.domain.common.model.Duration; -import es.princip.getp.domain.project.apply.model.ProjectApplication; - -import java.time.LocalDate; - -import static es.princip.getp.domain.project.apply.model.ProjectApplicationStatus.APPLICATION_ACCEPTED; -import static es.princip.getp.fixture.project.AttachmentFileFixture.attachmentFiles; - -public class ProjectApplicationFixture { - - public static final String DESCRIPTION = "프로젝트 지원 내용"; - - public static ProjectApplication projectApplication(final Long peopleId, final Long projectId) { - return ProjectApplication.builder() - .applicantId(peopleId) - .projectId(projectId) - .expectedDuration(Duration.of( - LocalDate.of(2024, 7, 1), - LocalDate.of(2024, 7, 31) - )) - .applicationStatus(APPLICATION_ACCEPTED) - .description(DESCRIPTION) - .attachmentFiles(attachmentFiles()) - .build(); - } -} diff --git a/src/test/java/es/princip/getp/fixture/project/ProjectFixture.java b/src/test/java/es/princip/getp/fixture/project/ProjectFixture.java deleted file mode 100644 index fe2d2bea..00000000 --- a/src/test/java/es/princip/getp/fixture/project/ProjectFixture.java +++ /dev/null @@ -1,49 +0,0 @@ -package es.princip.getp.fixture.project; - -import es.princip.getp.domain.common.model.Duration; -import es.princip.getp.domain.project.commission.model.MeetingType; -import es.princip.getp.domain.project.commission.model.Project; -import es.princip.getp.domain.project.commission.model.ProjectCategory; -import es.princip.getp.domain.project.commission.model.ProjectStatus; - -import java.time.LocalDate; - -import static es.princip.getp.fixture.common.HashtagFixture.hashtags; -import static es.princip.getp.fixture.project.AttachmentFileFixture.attachmentFiles; - -public class ProjectFixture { - - public static final Long PAYMENT = 1_000_000L; - public static final String TITLE = "프로젝트 제목"; - public static final String DESCRIPTION = "프로젝트 설명"; - - private static final Project.ProjectBuilder builder = Project.builder() - .category(ProjectCategory.BACKEND) - .attachmentFiles(attachmentFiles()) - .payment(PAYMENT) - .title(TITLE) - .description(DESCRIPTION) - .meetingType(MeetingType.IN_PERSON) - .estimatedDuration(Duration.of( - LocalDate.of(2024, 8, 1), - LocalDate.of(2024, 8, 31) - )) - .hashtags(hashtags()); - - public static Project project(final Long clientId, final ProjectStatus status) { - return builder.clientId(clientId) - .status(status) - .applicationDuration(Duration.of( - LocalDate.of(2024, 7, 1), - LocalDate.of(2024, 7, 31) - )) - .build(); - } - - public static Project project(final Long clientId, final ProjectStatus status, final Duration applicationDuration) { - return builder.clientId(clientId) - .status(status) - .applicationDuration(applicationDuration) - .build(); - } -} diff --git a/src/test/java/es/princip/getp/persistence/adapter/people/PeopleDataLoader.java b/src/test/java/es/princip/getp/persistence/adapter/people/PeopleDataLoader.java deleted file mode 100644 index faf6e9ea..00000000 --- a/src/test/java/es/princip/getp/persistence/adapter/people/PeopleDataLoader.java +++ /dev/null @@ -1,62 +0,0 @@ -package es.princip.getp.persistence.adapter.people; - -import es.princip.getp.domain.people.model.PeopleType; -import es.princip.getp.persistence.support.DataLoader; -import es.princip.getp.persistence.adapter.member.MemberJpaEntity; -import es.princip.getp.persistence.adapter.people.mapper.PeoplePersistenceMapper; -import es.princip.getp.persistence.adapter.people.model.PeopleJpaEntity; -import jakarta.persistence.EntityManager; -import lombok.RequiredArgsConstructor; - -import java.util.List; -import java.util.stream.LongStream; - -import static es.princip.getp.domain.member.model.MemberType.ROLE_PEOPLE; -import static es.princip.getp.fixture.member.PasswordFixture.PASSWORD; -import static es.princip.getp.fixture.people.PeopleFixture.peopleList; - -@RequiredArgsConstructor -public class PeopleDataLoader implements DataLoader { - - private final PeoplePersistenceMapper mapper; - private final EntityManager entityManager; - - @Override - public void load(final int size) { - final List memberList = LongStream.range(1, 1 + size) - .mapToObj(i -> MemberJpaEntity.builder() - .email("test" + i + "@example.com") - .password(PASSWORD) - .memberType(ROLE_PEOPLE) - .build()) - .toList(); - memberList.forEach(entityManager::persist); - - final Long memberIdBias = memberList.stream() - .findFirst() - .map(MemberJpaEntity::getMemberId) - .orElse(1L); - final int individualSize = size / 2; - final int teamSize = (size % 2) == 0 ? size / 2 : (size / 2) + 1; - - final List individualList = peopleList(individualSize, memberIdBias, PeopleType.INDIVIDUAL) - .stream() - .map(mapper::mapToJpa) - .toList(); - final List teamList = peopleList(teamSize, size + memberIdBias, PeopleType.TEAM) - .stream() - .map(mapper::mapToJpa) - .toList(); - - individualList.forEach(entityManager::persist); - teamList.forEach(entityManager::persist); - } - - @Override - public void teardown() { - entityManager.createQuery("DELETE FROM PeopleJpaEntity").executeUpdate(); - entityManager.createQuery("DELETE FROM MemberJpaEntity").executeUpdate(); - entityManager.createNativeQuery("ALTER TABLE member AUTO_INCREMENT = 1").executeUpdate(); - entityManager.createNativeQuery("ALTER TABLE people AUTO_INCREMENT = 1").executeUpdate(); - } -} diff --git a/src/test/java/es/princip/getp/persistence/adapter/people/PeopleQueryAdapterTest.java b/src/test/java/es/princip/getp/persistence/adapter/people/PeopleQueryAdapterTest.java deleted file mode 100644 index c806fcd0..00000000 --- a/src/test/java/es/princip/getp/persistence/adapter/people/PeopleQueryAdapterTest.java +++ /dev/null @@ -1,85 +0,0 @@ -package es.princip.getp.persistence.adapter.people; - -import es.princip.getp.api.controller.people.query.dto.people.CardPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.people.DetailPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.people.MyPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.people.PublicDetailPeopleResponse; -import es.princip.getp.api.controller.people.query.dto.peopleProfile.DetailPeopleProfileResponse; -import es.princip.getp.persistence.support.DataLoader; -import es.princip.getp.persistence.support.PersistenceAdapterTest; -import es.princip.getp.persistence.adapter.people.mapper.PeoplePersistenceMapper; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -public class PeopleQueryAdapterTest extends PersistenceAdapterTest { - - private static final int TEST_SIZE = 20; - private static final int PAGE_SIZE = 10; - - @PersistenceContext private EntityManager entityManager; - @Autowired private PeoplePersistenceMapper mapper; - @Autowired private PeopleQueryAdapter adapter; - - private List dataLoaders; - - @BeforeEach - void setUp() { - dataLoaders = List.of( - new PeopleDataLoader(mapper, entityManager) - ); - dataLoaders.forEach(dataLoader -> dataLoader.load(TEST_SIZE)); - } - - @AfterEach - void teardown() { - dataLoaders.forEach(DataLoader::teardown); - } - - @Test - void 피플_카드_목록_페이지를_조회한다() { - final Pageable pageable = PageRequest.of(0, PAGE_SIZE); - final Page response = adapter.findCardBy(pageable); - - assertThat(response.getNumberOfElements()).isEqualTo(PAGE_SIZE); - assertThat(response.getTotalElements()).isEqualTo(TEST_SIZE); - } - - @Test - void 피플_ID로_피플_상세_정보를_조회한다() { - final DetailPeopleResponse response = adapter.findDetailBy(1L); - - assertThat(response).isNotNull(); - } - - @Test - void 피플_ID로_피플_공개_상세_정보를_조회한다() { - final PublicDetailPeopleResponse response = adapter.findPublicDetailBy(1L); - - assertThat(response).isNotNull(); - } - - @Test - void 멤버_ID로_피플_정보를_조회한다() { - final MyPeopleResponse response = adapter.findBy(1L); - - assertThat(response).isNotNull(); - } - - @Test - void 멤버_ID로_피플_상세_프로필을_조회한다() { - final DetailPeopleProfileResponse response = adapter.findDetailProfileBy(1L); - - assertThat(response).isNotNull(); - } -} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/persistence/adapter/project/apply/FindAppliedProjectAdapterTest.java b/src/test/java/es/princip/getp/persistence/adapter/project/apply/FindAppliedProjectAdapterTest.java deleted file mode 100644 index aeb5e93d..00000000 --- a/src/test/java/es/princip/getp/persistence/adapter/project/apply/FindAppliedProjectAdapterTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package es.princip.getp.persistence.adapter.project.apply; - -import es.princip.getp.api.controller.project.query.dto.AppliedProjectCardResponse; -import es.princip.getp.persistence.support.DataLoader; -import es.princip.getp.persistence.support.PersistenceAdapterTest; -import es.princip.getp.persistence.adapter.people.PeopleDataLoader; -import es.princip.getp.persistence.adapter.people.mapper.PeoplePersistenceMapper; -import es.princip.getp.persistence.adapter.project.ProjectPersistenceMapper; -import es.princip.getp.persistence.adapter.project.commission.ProjectDataLoader; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -public class FindAppliedProjectAdapterTest extends PersistenceAdapterTest { - - private static final int TEST_SIZE = 20; - private static final int PAGE_SIZE = 10; - - @PersistenceContext private EntityManager entityManager; - @Autowired private FindAppliedProjectAdapter adapter; - @Autowired private ProjectApplicationPersistenceMapper applicationMapper; - @Autowired private PeoplePersistenceMapper peopleMapper; - @Autowired private ProjectPersistenceMapper projectMapper; - - private List dataLoaders; - - @BeforeEach - void setUp() { - dataLoaders = List.of( - new PeopleDataLoader(peopleMapper, entityManager), - new ProjectDataLoader(projectMapper, entityManager), - new ProjectApplicationDataLoader(applicationMapper, entityManager) - ); - dataLoaders.forEach(dataLoader -> dataLoader.load(TEST_SIZE)); - } - - @AfterEach - void teardown() { - dataLoaders.forEach(DataLoader::teardown); - } - - - final Pageable pageable = PageRequest.of(0, PAGE_SIZE); - - @Test - void 지원한_프로젝트_목록을_조회한다() { - final Page response = adapter.findBy(1L, pageable); - - assertThat(response.getContent()).allSatisfy(content -> - assertThat(content).usingRecursiveComparison().isNotNull() - ); - assertThat(response.getNumberOfElements()).isGreaterThan(0); - } -} diff --git a/src/test/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationDataLoader.java b/src/test/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationDataLoader.java deleted file mode 100644 index b25d8d3f..00000000 --- a/src/test/java/es/princip/getp/persistence/adapter/project/apply/ProjectApplicationDataLoader.java +++ /dev/null @@ -1,36 +0,0 @@ -package es.princip.getp.persistence.adapter.project.apply; - -import es.princip.getp.persistence.support.DataLoader; -import jakarta.persistence.EntityManager; -import lombok.RequiredArgsConstructor; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.LongStream; - -import static es.princip.getp.fixture.project.ProjectApplicationFixture.projectApplication; - -@RequiredArgsConstructor -public class ProjectApplicationDataLoader implements DataLoader { - - private final ProjectApplicationPersistenceMapper mapper; - private final EntityManager entityManager; - - @Override - public void load(final int size) { - final List projectApplicationList = new ArrayList<>(); - LongStream.rangeClosed(1, size).forEach(projectId -> - LongStream.rangeClosed(1, size).forEach(peopleId -> - projectApplicationList.add(mapper.mapToJpa(projectApplication(peopleId, projectId))) - ) - ); - projectApplicationList.forEach(entityManager::persist); - } - - @Override - public void teardown() { - entityManager.createQuery("DELETE FROM ProjectApplicationJpaEntity").executeUpdate(); - entityManager.createNativeQuery("ALTER TABLE project_application AUTO_INCREMENT = 1") - .executeUpdate(); - } -} diff --git a/src/test/java/es/princip/getp/persistence/adapter/project/commission/FindCommissionedProjectAdapterTest.java b/src/test/java/es/princip/getp/persistence/adapter/project/commission/FindCommissionedProjectAdapterTest.java deleted file mode 100644 index 78781ed2..00000000 --- a/src/test/java/es/princip/getp/persistence/adapter/project/commission/FindCommissionedProjectAdapterTest.java +++ /dev/null @@ -1,81 +0,0 @@ -package es.princip.getp.persistence.adapter.project.commission; - -import es.princip.getp.api.controller.project.query.dto.CommissionedProjectCardResponse; -import es.princip.getp.domain.project.commission.model.ProjectStatus; -import es.princip.getp.persistence.support.DataLoader; -import es.princip.getp.persistence.support.PersistenceAdapterTest; -import es.princip.getp.persistence.adapter.client.ClientDataLoader; -import es.princip.getp.persistence.adapter.project.ProjectPersistenceMapper; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -class FindCommissionedProjectAdapterTest extends PersistenceAdapterTest { - - private static final int PAGE_SIZE = 10; - private static final int TEST_SIZE = 20; - - @PersistenceContext private EntityManager entityManager; - @Autowired private FindCommissionedProjectAdapter adapter; - @Autowired private ProjectPersistenceMapper mapper; - - private List dataLoaders; - - @BeforeEach - void setUp() { - dataLoaders = List.of( - new ClientDataLoader(entityManager), - new ProjectDataLoader(mapper, entityManager) - ); - dataLoaders.forEach(dataLoader -> dataLoader.load(TEST_SIZE)); - } - - @AfterEach - void teardown() { - dataLoaders.forEach(DataLoader::teardown); - } - - @Nested - class 의뢰한_프로젝트_목록_페이지를_조회한다 { - - final Pageable pageable = PageRequest.of(0, PAGE_SIZE); - - @Test - void 만료된_프로젝트도_보는_경우() { - final Page response = adapter.findBy( - 1L, - true, - pageable - ); - - assertThat(response.getContent()).allSatisfy(content -> { - assertThat(content).usingRecursiveComparison().isNotNull(); - }); - } - - @Test - void 만료된_프로젝트는_보지_않는_경우() { - final Page response = adapter.findBy( - 1L, - false, - pageable - ); - - assertThat(response.getContent()).allSatisfy(content -> { - assertThat(content).usingRecursiveComparison().isNotNull(); - assertThat(content.status()).isNotEqualTo(ProjectStatus.CANCELLED); - }); - } - } -} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/persistence/adapter/project/commission/FindProjectAdapterTest.java b/src/test/java/es/princip/getp/persistence/adapter/project/commission/FindProjectAdapterTest.java deleted file mode 100644 index d2eed567..00000000 --- a/src/test/java/es/princip/getp/persistence/adapter/project/commission/FindProjectAdapterTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package es.princip.getp.persistence.adapter.project.commission; - -import es.princip.getp.api.controller.project.query.dto.ProjectCardResponse; -import es.princip.getp.api.controller.project.query.dto.ProjectDetailResponse; -import es.princip.getp.persistence.support.DataLoader; -import es.princip.getp.persistence.support.PersistenceAdapterTest; -import es.princip.getp.persistence.adapter.client.ClientDataLoader; -import es.princip.getp.persistence.adapter.project.ProjectPersistenceMapper; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; -import lombok.extern.slf4j.Slf4j; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -@Slf4j -class FindProjectAdapterTest extends PersistenceAdapterTest { - - private static final int TEST_SIZE = 20; - - @PersistenceContext private EntityManager entityManager; - @Autowired private FindProjectAdapter adapter; - @Autowired private ProjectPersistenceMapper mapper; - - private List dataLoaders; - - @BeforeEach - void setUp() { - dataLoaders = List.of( - new ClientDataLoader(entityManager), - new ProjectDataLoader(mapper, entityManager) - ); - dataLoaders.forEach(dataLoader -> dataLoader.load(TEST_SIZE)); - } - - @AfterEach - void teardown() { - dataLoaders.forEach(DataLoader::teardown); - } - - @Test - void 프로젝트_목록을_조회한다() { - final int pageSize = 10; - final Pageable pageable = PageRequest.of(0, pageSize); - final Page response = adapter.findBy(pageable); - - assertThat(response.getContent()).isNotEmpty(); - assertThat(response.getTotalElements()).isGreaterThan(pageSize); - assertThat(response.getNumberOfElements()).isEqualTo(pageSize); - } - - @Test - void 프로젝트_상세_정보를_조회한다() { - final ProjectDetailResponse response = adapter.findBy(1L); - - assertThat(response).isNotNull(); - } -} \ No newline at end of file diff --git a/src/test/java/es/princip/getp/persistence/adapter/project/commission/ProjectDataLoader.java b/src/test/java/es/princip/getp/persistence/adapter/project/commission/ProjectDataLoader.java deleted file mode 100644 index d43c9665..00000000 --- a/src/test/java/es/princip/getp/persistence/adapter/project/commission/ProjectDataLoader.java +++ /dev/null @@ -1,38 +0,0 @@ -package es.princip.getp.persistence.adapter.project.commission; - -import es.princip.getp.domain.project.commission.model.ProjectStatus; -import es.princip.getp.persistence.support.DataLoader; -import es.princip.getp.persistence.adapter.project.ProjectPersistenceMapper; -import jakarta.persistence.EntityManager; -import lombok.RequiredArgsConstructor; - -import java.util.Arrays; -import java.util.List; -import java.util.stream.LongStream; - -import static es.princip.getp.fixture.project.ProjectFixture.project; - -@RequiredArgsConstructor -public class ProjectDataLoader implements DataLoader { - - private final ProjectPersistenceMapper mapper; - private final EntityManager entityManager; - - @Override - public void load(final int size) { - final List projectList = LongStream.rangeClosed(1, size) - .boxed() - .flatMap(clientId -> Arrays.stream(ProjectStatus.values()) - .map(status -> mapper.mapToJpa(project(clientId, status))) - ) - .toList(); - projectList.forEach(entityManager::persist); - } - - @Override - public void teardown() { - entityManager.createQuery("DELETE FROM ProjectJpaEntity").executeUpdate(); - entityManager.createNativeQuery("ALTER TABLE project AUTO_INCREMENT = 1") - .executeUpdate(); - } -} diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml deleted file mode 100644 index e1dd00a9..00000000 --- a/src/test/resources/application-test.yml +++ /dev/null @@ -1,68 +0,0 @@ -server: - servlet: - context-path: ${BASE_PATH} - -spring: - storage: - local: - path: ${STORAGE_PATH} - base-uri: ${STORAGE_BASE_URI} - - datasource: - url: ${DB_TEST_URL} - driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver - - jpa: - hibernate: - ddl-auto: create - properties: - hibernate: - show_sql: true - format_sql: true - highlight_sql: true - use_sql_comments: true - jdbc: - time_zone: Asia/Seoul - batch_size: 100 - default_batch_fetch_size: 20 - dialect: org.hibernate.dialect.MySQLDialect - - mail: - host: smtp.gmail.com - port: ${GMAIL_PORT} - username: ${GMAIL_USERNAME} - password: ${GMAIL_PASSWORD} - properties: - mail: - smtp: - auth: true - starttls: - enable: true - required: true - connectiontimeout: 5000 - timeout: 5000 - writetimeout: 5000 - - verification-code: - length: ${VERIFICATION_CODE_LENGTH} - expire-time: ${VERIFICATION_CODE_EXPIRE_TIME} - - jwt: - secret: ${JWT_SECRET} - access-token: - expire-time: ${JWT_ACCESS_TOKEN_EXPIRE_TIME} - refresh-token: - expire-time: ${JWT_REFRESH_TOKEN_EXPIRE_TIME} - - messages: - basename: messages/validation, messages/error - -logging: - level: - org: - springframework: - security: DEBUG - type: - descriptor: - sql: - BasicBinder: TRACE \ No newline at end of file diff --git a/src/test/resources/org/springframework/restdocs/templates/asciidoctor/error-code-fields.snippet b/src/test/resources/org/springframework/restdocs/templates/asciidoctor/error-code-fields.snippet deleted file mode 100644 index b68b5e14..00000000 --- a/src/test/resources/org/springframework/restdocs/templates/asciidoctor/error-code-fields.snippet +++ /dev/null @@ -1,10 +0,0 @@ -|=== -|Error Code|Status|Message - -{{#fields}} -|{{#tableCellContent}}{{code}}{{/tableCellContent}} -|{{#tableCellContent}}{{status}}{{/tableCellContent}} -|{{#tableCellContent}}{{message}}{{/tableCellContent}} - -{{/fields}} -|=== \ No newline at end of file diff --git a/src/test/resources/org/springframework/restdocs/templates/asciidoctor/query-parameters.snippet b/src/test/resources/org/springframework/restdocs/templates/asciidoctor/query-parameters.snippet deleted file mode 100644 index 89f5dca0..00000000 --- a/src/test/resources/org/springframework/restdocs/templates/asciidoctor/query-parameters.snippet +++ /dev/null @@ -1,11 +0,0 @@ -|=== -|Parameter|Description|Optional|Default - -{{#parameters}} -|{{#tableCellContent}}`+{{name}}+`{{/tableCellContent}} -|{{#tableCellContent}}{{description}}{{/tableCellContent}} -|{{#tableCellContent}}{{#optional}}Yes{{/optional}}{{^optional}}No{{/optional}}{{/tableCellContent}} -|{{#tableCellContent}}{{default}}{{/tableCellContent}} - -{{/parameters}} -|=== \ No newline at end of file diff --git a/src/test/resources/org/springframework/restdocs/templates/asciidoctor/request-fields.snippet b/src/test/resources/org/springframework/restdocs/templates/asciidoctor/request-fields.snippet deleted file mode 100644 index 0112299b..00000000 --- a/src/test/resources/org/springframework/restdocs/templates/asciidoctor/request-fields.snippet +++ /dev/null @@ -1,12 +0,0 @@ -|=== -|Path|Type|Description|Format|Constraints - -{{#fields}} -|{{#tableCellContent}}`+{{path}}+`{{/tableCellContent}} -|{{#tableCellContent}}`+{{type}}+`{{/tableCellContent}} -|{{#tableCellContent}}{{description}}{{/tableCellContent}} -|{{#tableCellContent}}{{#format}}{{.}}{{/format}}{{/tableCellContent}} -|{{#tableCellContent}}{{constraints}}{{/tableCellContent}} - -{{/fields}} -|=== \ No newline at end of file