diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 0000000..1a0708c --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,120 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle + +name: Java CI with Gradle + +on: + push: + branches: + - main + - develop + pull_request: + branches: [ "main" ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + + + - uses: actions/checkout@v3 + - name: Set up JDK 19 + uses: actions/setup-java@v3 + with: + java-version: '19' + distribution: 'temurin' + ## gradle caching + - name: Gradle Caching + uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + ## create application-dev.properties + - name: make application-dev.properties + if: contains(github.ref, 'develop') + run: | + cd ./src/main/resources + touch ./application-dev.properties + echo "${{ secrets.PROPERTIES_DEV }}" > ./application-dev.properties + shell: bash + + ## create application-prod.properties + - name: make application-prod.properties + if: contains(github.ref, 'main') # branch가 main 일 때, 나머지는 위와 동일 + run: | + cd ./src/main/resources + touch ./application-prod.properties + echo "${{ secrets.PROPERTIES_PROD }}" > ./application-prod.properties + shell: bash + + ## gradle build + - name: Build with Gradle + run: ./gradlew build -x test + + ## docker build & push to production + - name: Docker build & push to prod + if: contains(github.ref, 'main') + run: | + docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} + docker build -f Dockerfile-prod -t ${{ secrets.DOCKER_USERNAME }}/dub-prod . + docker push ${{ secrets.DOCKER_USERNAME }}/dub-prod + + ## docker build & push to develop + - name: Docker build & push to dev + if: contains(github.ref, 'develop') + run: | + docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} + docker build -f Dockerfile-dev -t ${{ secrets.DOCKER_USERNAME }}/dub-dev . + docker push ${{ secrets.DOCKER_USERNAME }}/dub-dev + + + ## deploy to production + - name: Deploy to prod + uses: appleboy/ssh-action@master + id: deploy-prod + if: contains(github.ref, 'main') + with: + host: ${{ secrets.HOST_PROD }} + username: ubuntu + key: ${{ secrets.SSH_KEY }} + envs: GITHUB_SHA + script: | + sudo docker stop dub + sudo docker rm dub + sudo docker pull ${{ secrets.DOCKER_USERNAME }}/dub-prod + sudo docker run -d -p 8080:8080 --name dub ${{ secrets.DOCKER_USERNAME }}/dub-prod + sudo docker image prune -f + + ## deploy to develop + - name: Deploy to dev + uses: appleboy/ssh-action@master + id: deploy-dev + if: contains(github.ref, 'develop') + with: + host: ${{ secrets.HOST_DEV }} + username: ec2-user + key: ${{ secrets.SSH_KEY }} + port: 22 + #key: ${{ secrets.PRIVATE_KEY }} + script: | + sudo docker stop dub-dev + sudo docker rm -fv dub-dev + sudo docker pull ${{ secrets.DOCKER_USERNAME }}/dub-dev + sudo docker run -d -p 8080:8080 ${{ secrets.DOCKER_USERNAME }}/dub-dev + sudo docker image prune -f + + diff --git a/.gitignore b/.gitignore index cbd5291..e4b746f 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,6 @@ out/ ##application.properties## -application.properties \ No newline at end of file +application.properties +application-dev.properties +application-prod.properties diff --git a/Dockerfile-dev b/Dockerfile-dev new file mode 100644 index 0000000..5da3e54 --- /dev/null +++ b/Dockerfile-dev @@ -0,0 +1,6 @@ +## Dockerfile-dev +FROM openjdk:19-alpine +EXPOSE 8080 +ARG JAR_FILE=/build/libs/dub-0.0.1-SNAPSHOT.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["java","-jar","-Dspring.profiles.active=dev","/app.jar"] \ No newline at end of file diff --git a/Dockerfile-prod b/Dockerfile-prod new file mode 100644 index 0000000..dba27ef --- /dev/null +++ b/Dockerfile-prod @@ -0,0 +1,6 @@ +## Dockerfile-prod +FROM openjdk:19-alpine +EXPOSE 8080 +ARG JAR_FILE=/build/libs/dub-0.0.1-SNAPSHOT.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["java","-jar","-Dspring.profiles.active=prod","/app.jar"] \ No newline at end of file diff --git a/README.md b/README.md index ecafca9..33d791d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,61 @@ +--- + + # dub_club_wanted_BE -동아리 버전 원티드 + +## dub +동아리 홍보와, 동아리 지원을 할수있는 플랫폼 + + +--- + +# 핵심기능 + +#### 1. 회원 가입 + +```일반 회원과 동아리 회원으로 나누어 가입이 가능합니다.``` + +#### 2. 동아리 등록 + +```동아리 회원은 동아리의 프로필 이미지, 태그, 소개글 등을 등록할 수 있습니다.``` + +#### 3. 동아리 공고 및 지원서 양식 등록 + +```동아리 회원은 동아리 내에서 공고를 올릴 수 있으며, 지원자들에게 지원서 양식을 제공할 수 있습니다.``` + +#### 4. 태그별 동아리 조회 + +```사용자는 태그별로 동아리를 검색하고 조회할 수 있습니다.``` + +#### 5. 동아리 지원서 다운로드 및 제출 + +```일반 회원은 동아리의 지원서 양식을 다운로드하여 작성한 후, 해당 동아리에 제출할 수 있습니다.``` + +#### 6. 동아리 회원별 지원자 조회 + +```동아리 회원은 자신의 동아리에 지원한 회원들을 조회할 수 있습니다.``` + +#### 7. 합격/불합격 통보 보내기 + +```동아리 회원은 지원자들에게 합격 또는 불합격 통보를 보낼 수 있습니다.``` + +--- + +# API 엔드포인트 목록 및 사용법 + +--- + +# CI/CD Flow + +![CIcd](https://github.com/s2hoon/dub_club_wanted_BE/assets/82464990/120399f7-7d09-4996-a8ac-631c4024a4fe) + +1. main branch 에 Push 또는 Merge +2. Github 에 작성해둔 workflow file 로 Github Actions 수행 +3. build, docker image build, docker image push 수행 +4. EC2 인스턴스에서 docker image pull 후, run + +--- + +# ERD + +![erd](https://github.com/s2hoon/dub_club_wanted_BE/assets/82464990/d333d553-4d3a-45b5-a492-7f9ee254873e) diff --git a/build.gradle b/build.gradle index a6b39e0..c012ec3 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ plugins { - id 'java' - id 'org.springframework.boot' version '3.0.4' - id 'io.spring.dependency-management' version '1.1.0' + id 'java' + id 'org.springframework.boot' version '3.0.4' + id 'io.spring.dependency-management' version '1.1.0' } group = 'com.likelion' @@ -9,40 +9,49 @@ version = '0.0.1-SNAPSHOT' sourceCompatibility = '19' configurations { - compileOnly { - extendsFrom annotationProcessor - } + compileOnly { + extendsFrom annotationProcessor + } } repositories { - mavenCentral() + mavenCentral() } dependencies { - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' - implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'mysql:mysql-connector-java:8.0.26' - implementation 'org.springframework.boot:spring-boot-starter-security' - compileOnly 'org.projectlombok:lombok' - developmentOnly 'org.springframework.boot:spring-boot-devtools' - annotationProcessor 'org.projectlombok:lombok' - runtimeOnly 'com.mysql:mysql-connector-j' - testImplementation 'org.springframework.boot:spring-boot-starter-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' - implementation 'commons-io:commons-io:2.6' - - - //swagger - implementation 'io.springfox:springfox-boot-starter:3.0.0' - implementation group: 'io.springfox', name: 'springfox-swagger-ui', version: '3.0.0' - + // json 요청 + implementation group: 'org.json', name: 'json', version: '20210307' + // spring + implementation 'org.springframework:spring-web' + // spring boot + developmentOnly 'org.springframework.boot:spring-boot-devtools' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-security' + // mysql + implementation 'mysql:mysql-connector-java:8.0.26' + runtimeOnly 'com.mysql:mysql-connector-j' + // lombok + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + // 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 'commons-io:commons-io:2.6' + + // s3 + implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + // test + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.security:spring-security-test' + + //thymeleaf + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.0.1' } tasks.named('test') { - useJUnitPlatform() + useJUnitPlatform() } diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/images/20230330/691422510048300.png b/images/20230330/691422510048300.png deleted file mode 100644 index 3bccc11..0000000 Binary files a/images/20230330/691422510048300.png and /dev/null differ diff --git a/images/20230330/82016473748200.png b/images/20230330/82016473748200.png deleted file mode 100644 index 47bf137..0000000 Binary files a/images/20230330/82016473748200.png and /dev/null differ diff --git a/src/main/java/com/likelion/dub/DubApplication.java b/src/main/java/com/likelion/dub/DubApplication.java index b2dfed9..dbdc11b 100644 --- a/src/main/java/com/likelion/dub/DubApplication.java +++ b/src/main/java/com/likelion/dub/DubApplication.java @@ -2,12 +2,15 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +@EnableJpaAuditing @SpringBootApplication public class DubApplication { - public static void main(String[] args) { - SpringApplication.run(DubApplication.class, args); - } - + public static void main(String[] args) { + SpringApplication.run(DubApplication.class, args); + } + } diff --git a/src/main/java/com/likelion/dub/common/BaseException.java b/src/main/java/com/likelion/dub/baseResponse/BaseException.java similarity index 84% rename from src/main/java/com/likelion/dub/common/BaseException.java rename to src/main/java/com/likelion/dub/baseResponse/BaseException.java index 5c74c07..49d3557 100644 --- a/src/main/java/com/likelion/dub/common/BaseException.java +++ b/src/main/java/com/likelion/dub/baseResponse/BaseException.java @@ -1,4 +1,4 @@ -package com.likelion.dub.common; +package com.likelion.dub.baseResponse; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/src/main/java/com/likelion/dub/common/BaseResponse.java b/src/main/java/com/likelion/dub/baseResponse/BaseResponse.java similarity index 59% rename from src/main/java/com/likelion/dub/common/BaseResponse.java rename to src/main/java/com/likelion/dub/baseResponse/BaseResponse.java index efc5549..38e64dc 100644 --- a/src/main/java/com/likelion/dub/common/BaseResponse.java +++ b/src/main/java/com/likelion/dub/baseResponse/BaseResponse.java @@ -1,12 +1,10 @@ -package com.likelion.dub.common; +package com.likelion.dub.baseResponse; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import lombok.Getter; -import static com.likelion.dub.common.BaseResponseStatus.SUCCESS; - @Getter @JsonPropertyOrder({"isSuccess", "code", "message", "result"}) public class BaseResponse { @@ -23,24 +21,15 @@ public class BaseResponse { private T result; // 요청 성공 - public BaseResponse(T result) { - this.isSuccess = SUCCESS.isSuccess(); - this.code = SUCCESS.getCode(); - this.message = SUCCESS.getMessage(); + public BaseResponse(BaseResponseStatus status, T result) { + this(status); this.result = result; } - // 요청에 실패한 경우 + // 요청 실패 public BaseResponse(BaseResponseStatus status) { this.isSuccess = status.isSuccess(); this.code = status.getCode(); this.message = status.getMessage(); } - - public BaseResponse(T result, BaseResponseStatus existsLike) { - this.isSuccess = existsLike.isSuccess(); - this.code = existsLike.getCode(); - this.message = existsLike.getMessage(); - this.result = result; - } } \ No newline at end of file diff --git a/src/main/java/com/likelion/dub/baseResponse/BaseResponseStatus.java b/src/main/java/com/likelion/dub/baseResponse/BaseResponseStatus.java new file mode 100644 index 0000000..cebff42 --- /dev/null +++ b/src/main/java/com/likelion/dub/baseResponse/BaseResponseStatus.java @@ -0,0 +1,61 @@ +package com.likelion.dub.baseResponse; + +import lombok.Getter; + +@Getter +public enum BaseResponseStatus { + + /** + * 1000 : Success + */ + SUCCESS(true, 1000, "요청에 성공했습니다."), + + /** + * 2XXX : Common + */ + REQUEST_ERROR(false, 2000, "입력값을 확인해주세요."), + FILE_SAVE_ERROR(false, 2001, "파일 저장에 실패하였습니다."), + FILE_DELETE_ERROR(false, 2002, "파일 삭제에 실패하였습니다."), + + /** + * 3XXX : Member + */ + INVALID_MEMBER_JWT(false, 3000, "권한이 없는 회원의 접근입니다."), + EMPTY_PROFILE_IMAGE(false, 3001, "프로필 이미지를 입력해주세요."), + + JWT_TOKEN_ERROR(false, 3002, "jwt 토큰을 확인해주세요"), + + USERS_EMPTY_USER_ID(false, 3003, "유저 아이디 값을 확인해주세요."), + + EMAIL_ALREADY_EXIST(false, 3004, "이미 가입된 이메일 주소입니다."), + WRONG_EMAIL(false, 3005, "잘못된 이메일 주소입니다."), + WRONG_PASSWORD(false, 3006, "잘못된 비밀번호입니다."), + STU_NUM_ALREADY_EXIST(false, 3007, "이미 가입된 학번 입니다."), + NO_SUCH_MEMBER_EXIST(false, 3008, "존재하지 않는 회원입니다."), + NO_SUCH_CLUB_EXIST(false, 3009, "존재하지 않는 동아리입니다."), + + /** + * 4XXX : Post + */ + NOT_EXISTS_POST(false, 4000, "게시물이 존재하지 않습니다."), + FAILED_GET_POST(false, 4001, "게시물 조회에 실패하였습니다."), + NOT_EXISTS_TAG_NAME_POST(true, 4002, "해당 태그를 가진 게시물이 없습니다."), + DELETE_FAIL_POST(false, 4003, "게시물 삭제에 실패하였습니다."); + + + /** + * 5xxx: Mypage + */ + + + private final boolean isSuccess; + private final int code; + private final String message; + + + BaseResponseStatus(boolean isSuccess, int code, String message) { + this.isSuccess = isSuccess; + this.code = code; + this.message = message; + } +} \ No newline at end of file diff --git a/src/main/java/com/likelion/dub/common/BaseResponseStatus.java b/src/main/java/com/likelion/dub/common/BaseResponseStatus.java deleted file mode 100644 index b687326..0000000 --- a/src/main/java/com/likelion/dub/common/BaseResponseStatus.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.likelion.dub.common; - -import lombok.Getter; - -@Getter -public enum BaseResponseStatus { - - /** - * 1000 : Successed - */ - SUCCESS(true, 1000, "요청에 성공했습니다."), - EXISTS_LIKE_AND_EXISTS_SCRAP(true,1010,"좋아요와 스크랩 한 게시물 입니다."), - NOT_EXISTS_LIKE_NOT_EXISTS_SCRAP(true,1011,"좋아요와 스크랩 하지 않은 게시물입니다."), - EXISTS_LIKE_AND_NOT_EXISTS_SCRAP(true,1012,"좋아요 한 게시물이지만 스크랩은 하지 않은 게시물 입니다."), - NOT_EXISTS_LIKE_EXISTS_SCRAP(true,1013,"좋아요 하지 않은 게시물이지만 스크랩은 한 게시물입니다."), - - /** - * 2XXX : Request 내용 오류 - */ - // Common - REQUEST_ERROR(false, 2000, "입력값을 확인해주세요."), - FILE_SAVE_ERROR(false, 2001, "파일 저장에 실패하였습니다."), - FILE_DELETE_ERROR(false, 2002, "파일 삭제에 실패하였습니다."), - - // album - NO_ACCESS_TO_VIBE(false, 2100, "해당 바이브에 대한 접근 권한이 없습니다."), - - // comment - POST_COMMENT_INVALID_BODY(false, 2200,"댓글의 글자수를 확인해주세요."), - - // member - - // mypage - INVALID_MEMBER_JWT(false,2300,"권한이 없는 회원의 접근입니다."), - EMPTY_PROFILE_IMAGE(false, 2301, "프로필 이미지를 입력해주세요."), - - JWT_TOKEN_ERROR(false, 2302, "jwt 토큰을 확인해주세요"), - - // post - USERS_EMPTY_USER_ID(false, 2010, "유저 아이디 값을 확인해주세요."), - POST_POSTS_INVALID_TITLE(false, 2011, "제목의 글자수를 확인해주세요."), - POST_POSTS_INVALID_BODY(false, 2012, "내용의 글자수를 확인해주세요."), - - // vibe - - - /** - * 3XXX : 내부 오류 - */ - // Common - RESPONSE_ERROR(false, 3000, "값을 불러오는 데 실패하였습니다."), - - // album - - // comment - NOT_FOUND_COMMENT(false, 3100, "해당 댓글이 존재하지 않습니다."), - NOT_FOUND_SUB_COMMENT(false, 3101, "해당 대댓글이 존재하지 않습니다."), - - - // member - - EMAIL_ALREADY_EXIST(false, 3200, "이미 가입된 이메일 주소입니다."), - WRONG_EMAIL(false, 3201, "잘못된 이메일 주소입니다."), - WRONG_PASSWORD(false, 3202, "잘못된 비밀번호입니다."), - DISABLED_MEMBER(false, 3203, "탈퇴한 회원입니다."), - STU_NUM_ALREADY_EXIST(false, 3204, "이미 가입된 학번 입니다."), - NO_SUCH_MEMBER_EXIST(false, 3303, "존재하지 않는 회원입니다."), - - // mypage - - // post - DELETE_FAIL_POST(false, 3010, "게시물 삭제에 실패하였습니다."), - FAILED_GET_POST(false,3011,"게시물 조회에 실패하였습니다."), - NOT_EXISTS_TAG_NAME_POST(true,3011,"해당 태그를 가진 게시물이 없습니다."), - NOT_EXISTS_POST(false,3012,"게시물이 존재하지 않습니다."), - - // vibe - SAVE_TEMPORARY_FILE_FAILED(false, 3500, "이미지 파일 전달 실패. 요청을 다시 전송해주세요."), - EMPTY_IMAGE(false, 3501, "이미지를 보내 주세요"), - EXTERNAL_API_FAILED(false, 3502,"외부 API 호출 실패"), - NO_PROPER_VIDEO(false, 3503, "적절한 음악이 없습니다. (주어진 정보가 너무 복잡한 경우 발생) "), - - /** - * 4XXX : DB, server 오류 - */ - DBCONN_ERROR(false, 4000, "데이터베이스 연결 오류"), - SERVER_ERROR(false, 4001, "서버와의 연결에 실패하였습니다."), - - // notice - NO_SUCH_NOTICE(false, 5000, "존재하진 않는 알림"); - private final boolean isSuccess; - private final int code; - private final String message; - - - BaseResponseStatus(boolean isSuccess, int code, String message) { - this.isSuccess = isSuccess; - this.code = code; - this.message = message; - } -} \ No newline at end of file diff --git a/src/main/java/com/likelion/dub/configuration/AwareAuditor.java b/src/main/java/com/likelion/dub/configuration/AwareAuditor.java new file mode 100644 index 0000000..4f09eba --- /dev/null +++ b/src/main/java/com/likelion/dub/configuration/AwareAuditor.java @@ -0,0 +1,16 @@ +package com.likelion.dub.configuration; + +import java.util.Optional; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.domain.AuditorAware; +import org.springframework.security.core.context.SecurityContextHolder; + +@Configuration +public class AwareAuditor implements AuditorAware { + @Override + public Optional getCurrentAuditor() { + String email = SecurityContextHolder.getContext().getAuthentication().getName(); + return Optional.of(email); + } + +} diff --git a/src/main/java/com/likelion/dub/configuration/ClientConfig.java b/src/main/java/com/likelion/dub/configuration/ClientConfig.java new file mode 100644 index 0000000..a57788d --- /dev/null +++ b/src/main/java/com/likelion/dub/configuration/ClientConfig.java @@ -0,0 +1,14 @@ +package com.likelion.dub.configuration; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class ClientConfig { + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} \ No newline at end of file diff --git a/src/main/java/com/likelion/dub/configuration/EncoderConfig.java b/src/main/java/com/likelion/dub/configuration/EncoderConfig.java index d4fd441..3bd4a84 100644 --- a/src/main/java/com/likelion/dub/configuration/EncoderConfig.java +++ b/src/main/java/com/likelion/dub/configuration/EncoderConfig.java @@ -8,7 +8,7 @@ @Configuration public class EncoderConfig { @Bean - public BCryptPasswordEncoder encoder(){ + public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } } diff --git a/src/main/java/com/likelion/dub/configuration/JwtTokenFilter.java b/src/main/java/com/likelion/dub/configuration/JwtTokenFilter.java index 4e16ed1..52aeb6f 100644 --- a/src/main/java/com/likelion/dub/configuration/JwtTokenFilter.java +++ b/src/main/java/com/likelion/dub/configuration/JwtTokenFilter.java @@ -1,100 +1,90 @@ package com.likelion.dub.configuration; -import com.likelion.dub.domain.Member; -import com.likelion.dub.service.MemberService; -import com.likelion.dub.utils.JwtTokenUtil; -import io.jsonwebtoken.Jwt; -import jakarta.servlet.Filter; +import com.likelion.dub.util.JwtTokenUtil; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; -import java.io.IOException; -import java.security.SignatureException; -import java.util.List; - /** - * JwtTokenFilter는 Spring Security에서 인증된 사용자의 요청에 대해 실행됩니다. - * doFilterInternal() 메소드를 통해 요청에 대한 필터링이 이루어지며, 인증 토큰을 추출하여 이를 이용해 인증된 사용자의 권한을 설정합니다 - * 이후 요청이 컨트롤러로 전달됩니다. 따라서 JwtTokenFilter는 인증된 사용자가 보호된 자원에 접근할 때마다 실행됩니다. + * JwtTokenFilter는 Spring Security에서 인증된 사용자의 요청에 대해 실행됩니다. doFilterInternal() 메소드를 통해 요청에 대한 필터링이 이루어지며, 인증 토큰을 추출하여 이를 + * 이용해 인증된 사용자의 권한을 설정합니다 이후 요청이 컨트롤러로 전달됩니다. 따라서 JwtTokenFilter는 인증된 사용자가 보호된 자원에 접근할 때마다 실행됩니다. */ @Slf4j +@Component @RequiredArgsConstructor public class JwtTokenFilter extends OncePerRequestFilter { - private final MemberService memberService; - private final String secretKey; + private final Map roleMapping; + @Value("${jwt.token.secret}") + private String secretKey; @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - final String authorization = request.getHeader(HttpHeaders.AUTHORIZATION); - log.info("authorization:{}", authorization); - - - //token 안보내면 Block - if (authorization == null || !authorization.startsWith("Bearer ")) { - - log.error("No Token"); - filterChain.doFilter(request, response); + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + final String authorization = request.getHeader(HttpHeaders.AUTHORIZATION); //요청 헤더에서 Authorization 헤더를 읽음 + log.info("authorization:{}", authorization); + // 토큰이 있는지 확인 + if (checkHaveToken(request, response, filterChain, authorization)) { return; } //token 꺼내기 String token = authorization.split(" ")[1]; - //token expired 여부 - if (JwtTokenUtil.isExpired(token, secretKey)) { - log.error("token Expired"); - filterChain.doFilter(request, response); + //token expired 면 종료 + if (checkIsExpired(request, response, filterChain, token)) { return; - } - - //email Token 에서 꺼내기 String email = JwtTokenUtil.getEmail(token, secretKey); - log.info("email:{}", email); - - //role Token 에서 꺼내기 String role = JwtTokenUtil.getRole(token, secretKey); + log.info("email:{}", email); log.info("role:{}", role); - - if ( "USER".equals(role)){ - //권한 부여 + String authority = roleMapping.get(role); + log.info("authority ={} ", authority); + if (authority != null) { + // 권한 부여 UsernamePasswordAuthenticationToken authenticationToken = - new UsernamePasswordAuthenticationToken(email, null, List.of(new SimpleGrantedAuthority("ROLE_USER"))); - //detail 을 넣어줍니다 + new UsernamePasswordAuthenticationToken(email, null, + List.of(new SimpleGrantedAuthority(authority))); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authenticationToken); - filterChain.doFilter(request, response); } - else if ("CLUB".equals(role)) { - //권한 부여 - UsernamePasswordAuthenticationToken authenticationToken = - new UsernamePasswordAuthenticationToken(email, null, List.of(new SimpleGrantedAuthority("ROLE_CLUB"))); - //detail 을 넣어줍니다 - authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); - SecurityContextHolder.getContext().setAuthentication(authenticationToken); + filterChain.doFilter(request, response); + } + + private boolean checkIsExpired(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain, + String token) throws IOException, ServletException { + if (JwtTokenUtil.isExpired(token, secretKey)) { + log.error("token Expired"); filterChain.doFilter(request, response); + return true; } - else if ("ADMIN".equals(role)) { - //권한 부여 - UsernamePasswordAuthenticationToken authenticationToken = - new UsernamePasswordAuthenticationToken(email, null, List.of(new SimpleGrantedAuthority("ROLE_ADMIN"))); - //detail 을 넣어줍니다 - authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); - SecurityContextHolder.getContext().setAuthentication(authenticationToken); + return false; + } + + private boolean checkHaveToken(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain, + String authorization) throws IOException, ServletException { + //토큰이 없거나, Bearer token 이 아니면 종료 + if (authorization == null || !authorization.startsWith("Bearer ")) { + log.error("No Token or not Bearer token"); filterChain.doFilter(request, response); + return true; } - - + return false; } + } diff --git a/src/main/java/com/likelion/dub/configuration/RoleMappingConfig.java b/src/main/java/com/likelion/dub/configuration/RoleMappingConfig.java new file mode 100644 index 0000000..13d332a --- /dev/null +++ b/src/main/java/com/likelion/dub/configuration/RoleMappingConfig.java @@ -0,0 +1,20 @@ +package com.likelion.dub.configuration; + + +import java.util.HashMap; +import java.util.Map; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class RoleMappingConfig { + @Bean + public Map roleMapping() { + Map roleMapping = new HashMap<>(); + roleMapping.put("USER", "ROLE_USER"); + roleMapping.put("CLUB", "ROLE_CLUB"); + roleMapping.put("ADMIN", "ROLE_ADMIN"); + return roleMapping; + } + +} diff --git a/src/main/java/com/likelion/dub/configuration/S3config.java b/src/main/java/com/likelion/dub/configuration/S3config.java new file mode 100644 index 0000000..bf92051 --- /dev/null +++ b/src/main/java/com/likelion/dub/configuration/S3config.java @@ -0,0 +1,30 @@ +package com.likelion.dub.configuration; + + +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class S3config { + + @Value("${cloud.aws.credentials.access-key}") + private String accessKey; + @Value("${cloud.aws.credentials.secret-key}") + private String secretKey; + @Value("${cloud.aws.region.static}") + private String region; + + @Bean + public AmazonS3Client amazonS3Client() { + BasicAWSCredentials awsCredentials= new BasicAWSCredentials(accessKey, secretKey); + return (AmazonS3Client) AmazonS3ClientBuilder.standard() + .withRegion(region) + .withCredentials(new AWSStaticCredentialsProvider(awsCredentials)) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/likelion/dub/configuration/SecurityConfig.java b/src/main/java/com/likelion/dub/configuration/SecurityConfig.java index d7ca3e9..a359c5c 100644 --- a/src/main/java/com/likelion/dub/configuration/SecurityConfig.java +++ b/src/main/java/com/likelion/dub/configuration/SecurityConfig.java @@ -1,13 +1,9 @@ package com.likelion.dub.configuration; -import com.likelion.dub.domain.Role; -import com.likelion.dub.service.MemberService; import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; @@ -19,45 +15,26 @@ @RequiredArgsConstructor public class SecurityConfig { - private final MemberService memberService; - private static final String[] Swagger_url = { - /* swagger v2 */ - "/v2/api-docs", - "/swagger-resources", - "/swagger-resources/**", - "/configuration/ui", - "/configuration/security", - "/swagger-ui.html", - "/webjars/**", - /* swagger v3 */ - "/v3/api-docs/**", - "/swagger-ui/**" - }; - @Value("${jwt.token.secret}") - private String secretKey; - + private final JwtTokenFilter jwtTokenFilter; @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http .httpBasic().disable() .csrf().disable() - //.cors().and() + .cors().disable() .authorizeRequests() - .requestMatchers("/app/member/sign-up", "/app/member/sign-in", "/app/member/email/{email}", "/app/member/stunum/{stunum}","/app/post/getAll").permitAll() //누구나 접근 가능 - .requestMatchers(Swagger_url).permitAll() - .requestMatchers(HttpMethod.POST, "/app/member/test").hasRole("ADMIN") //admin 권한 필요 - .requestMatchers(HttpMethod.POST, "/app/post/write-post").hasRole("CLUB") //CLUB 권한 필요 - .anyRequest().authenticated() + .requestMatchers("/app/post/write-post", "/app/post/delete-post", "/app/post/rewrite-post") + .hasRole("CLUB") // Post 작성 Club 권한 필요 + .requestMatchers("/app/club/**").hasRole("CLUB") // Club 에 관한거 CLUB 권한 필요 + .anyRequest().permitAll() // 나머지는 누구나 접근가능 .and() .sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 세션 사용 비활성화 -> restful api 기반 토큰 이므로 세션 필요x .and() - .addFilterBefore(new JwtTokenFilter(memberService, secretKey), UsernamePasswordAuthenticationFilter.class) + .addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class) .build(); - - - + } } diff --git a/src/main/java/com/likelion/dub/controller/ClubController.java b/src/main/java/com/likelion/dub/controller/ClubController.java new file mode 100644 index 0000000..b4d4799 --- /dev/null +++ b/src/main/java/com/likelion/dub/controller/ClubController.java @@ -0,0 +1,72 @@ +package com.likelion.dub.controller; + + +import com.likelion.dub.baseResponse.BaseException; +import com.likelion.dub.baseResponse.BaseResponse; +import com.likelion.dub.baseResponse.BaseResponseStatus; +import com.likelion.dub.dto.Club.UpdateTagRequest; +import com.likelion.dub.service.ClubService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +@RestController +@RequestMapping("/app/club") +@RequiredArgsConstructor +//@CrossOrigin(origins = "*", allowedHeaders = "*") //Cors 제거 +public class ClubController { + + + private final ClubService clubService; + + + @PostMapping("/uploadForm") + public BaseResponse uploadForm(@RequestBody String url) { + try { + clubService.uploadForm(url); + String result = "동아리 지원서 양식 등록 완료"; + return new BaseResponse<>(BaseResponseStatus.SUCCESS, result); + } catch (BaseException e) { + return new BaseResponse<>(e.getStatus()); + } + + } + + @PostMapping("/writeIntro") + public BaseResponse writeInfo(@RequestBody String introduction) { + try { + clubService.updateIntroduce(introduction); + String result = "동아리 소개글 작성 완료"; + return new BaseResponse<>(BaseResponseStatus.SUCCESS, result); + } catch (BaseException e) { + return new BaseResponse<>(e.getStatus()); + } + } + + @PostMapping("/uploadClubImage") + public BaseResponse uploadClubImage(@ModelAttribute MultipartFile image) { + try { + clubService.updateClubImage(image); + String result = "동아리 사진 등록 완료"; + return new BaseResponse<>(BaseResponseStatus.SUCCESS, result); + } catch (BaseException e) { + return new BaseResponse<>(e.getStatus()); + } + } + + @PostMapping("/updateTag") + public BaseResponse updateTag(@RequestBody UpdateTagRequest updateTagRequest) { + try { + clubService.updateTag(updateTagRequest.getGroupName(), updateTagRequest.getCategory()); + String result = "동아리 태그 등록 완료"; + return new BaseResponse<>(BaseResponseStatus.SUCCESS, result); + } catch (BaseException e) { + return new BaseResponse<>(e.getStatus()); + } + + } +} diff --git a/src/main/java/com/likelion/dub/controller/JspController.java b/src/main/java/com/likelion/dub/controller/JspController.java new file mode 100644 index 0000000..1875b32 --- /dev/null +++ b/src/main/java/com/likelion/dub/controller/JspController.java @@ -0,0 +1,48 @@ +package com.likelion.dub.controller; + + +import com.likelion.dub.dto.Member.MemberJoinRequest; +import com.likelion.dub.service.MemberService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +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.client.RestTemplate; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +@Controller +@RequiredArgsConstructor +//@CrossOrigin(origins = "*", allowedHeaders = "*") //Cors 제거 +@RequestMapping("/app/jsp") +public class JspController { + + private final MemberService memberService; + private final RestTemplate restTemplate; + + + @RequestMapping("/login") + public String loginView() { + return "loginView"; + } + + @GetMapping("/redirect") + public String kakaoCallback(@RequestParam("code") String code, + RedirectAttributes redirectAttributes) { + // code 값을 main 페이지로 전달 + redirectAttributes.addFlashAttribute("authorizationCode", code); + return "redirect:/mainView"; // mainView 페이지로 리다이렉트 + } + + @GetMapping("/mainView") + public String mainView() { + return "mainView"; + } + + @GetMapping("/join") + public String joinView(Model model) { + model.addAttribute("memberJoinRequest", new MemberJoinRequest()); + return "joinView"; + } +} diff --git a/src/main/java/com/likelion/dub/controller/MemberController.java b/src/main/java/com/likelion/dub/controller/MemberController.java index 1eaedac..c80766a 100644 --- a/src/main/java/com/likelion/dub/controller/MemberController.java +++ b/src/main/java/com/likelion/dub/controller/MemberController.java @@ -1,107 +1,125 @@ package com.likelion.dub.controller; -import com.fasterxml.jackson.databind.ser.Serializers; -import com.likelion.dub.common.BaseException; -import com.likelion.dub.common.BaseResponse; -import com.likelion.dub.common.BaseResponseStatus; -import com.likelion.dub.domain.Club; -import com.likelion.dub.domain.dto.MemberJoinRequest; -import com.likelion.dub.domain.dto.MemberLoginRequest; -import com.likelion.dub.exception.AppException; -import com.likelion.dub.exception.Errorcode; +import com.likelion.dub.baseResponse.BaseException; +import com.likelion.dub.baseResponse.BaseResponse; +import com.likelion.dub.baseResponse.BaseResponseStatus; +import com.likelion.dub.dto.Member.ChangePwdRequest; +import com.likelion.dub.dto.Member.GetMemberInfoResponse; +import com.likelion.dub.dto.Member.MemberJoinRequest; +import com.likelion.dub.dto.Member.MemberLoginRequest; +import com.likelion.dub.dto.Member.ToClubRequest; +import com.likelion.dub.dto.OAuth.KakaoLoginParams; import com.likelion.dub.service.MemberService; import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; -import static com.likelion.dub.common.BaseResponseStatus.WRONG_EMAIL; @RestController @RequestMapping("/app/member") @RequiredArgsConstructor -@CrossOrigin(origins = "*", allowedHeaders = "*") //Cors 제거 +@Slf4j public class MemberController { private final MemberService memberService; - /** - * 이메일 중복체크 - * @param email - * @return - */ + // 이메일 중복체크 @GetMapping("/email/{email}") public BaseResponse checkEmail(@PathVariable String email) { - + try { boolean isEmailAvailable = memberService.checkEmail(email); - if (isEmailAvailable) { - String result = "이메일 사용 가능"; - return new BaseResponse<>(result); - } else { - return new BaseResponse(BaseResponseStatus.EMAIL_ALREADY_EXIST); - } - + return new BaseResponse<>(BaseResponseStatus.SUCCESS, "이메일 사용 가능"); + } catch (BaseException e) { + return new BaseResponse(e.getStatus()); + } } - /** - * 학번 중복체크 - * @param stunum - * @return - */ - @GetMapping("/stunum/{stunum}") - public BaseResponse checkStunum(@PathVariable Long stunum) { - boolean isStunumAvailable = memberService.checkStunum(stunum); - if(isStunumAvailable) { - String result = "학번 사용 가능"; - return new BaseResponse<>(result); - } else { - return new BaseResponse(BaseResponseStatus.STU_NUM_ALREADY_EXIST); + // 일반 회원가입 + @PostMapping("/sign-up") + public BaseResponse join(@RequestBody MemberJoinRequest memberJoinRequest) { + try { + memberService.join(memberJoinRequest); + return new BaseResponse<>(BaseResponseStatus.SUCCESS, "(일반) 회원 가입 완료"); + } catch (BaseException e) { + return new BaseResponse(e.getStatus()); } } - - /** - * 회원가입 - * @param dto - * @return - */ - @PostMapping("/sign-up") - public BaseResponse join(@RequestBody MemberJoinRequest dto ) throws BaseException{ - try { - memberService.join(dto.getEmail(), dto.getName(), dto.getPassword(), dto.getStunum(), dto.getRole()); - String result = "회원 가입 완료"; - return new BaseResponse<>(result); - } - catch(BaseException e){ - return new BaseResponse(e.getStatus()); - } + // 동아리 회원으로 전환 + @PostMapping("/toClub") + public BaseResponse toClub(@RequestBody ToClubRequest toClubRequest) { + try { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String email = authentication.getName(); + memberService.transferToClub(email, toClubRequest); + return new BaseResponse<>(BaseResponseStatus.SUCCESS, "동아리 회원으로 전환 완료"); + } catch (BaseException e) { + return new BaseResponse<>(e.getStatus()); + } } - /** - * 로그인 - * @param dto - * @return - */ + // 일반 로그인 @PostMapping("/sign-in") public BaseResponse login(@RequestBody MemberLoginRequest dto) { try { String token = memberService.login(dto.getEmail(), dto.getPassword()); - return new BaseResponse<>("Bearer "+token); + return new BaseResponse<>(BaseResponseStatus.SUCCESS, "Bearer " + token); + + } catch (BaseException e) { + return new BaseResponse(e.getStatus()); + } + + } + + // 카카오 로그인 + @PostMapping("/loginKakao") + public BaseResponse loginKakao(@RequestBody KakaoLoginParams params) { + try { + log.info("params = {}", params.getAuthorizationCode()); + String token = memberService.loginKakao(params); + log.info("JWT token = {}", token); + return new BaseResponse<>(BaseResponseStatus.SUCCESS, "Bearer " + token); } catch (BaseException e) { - BaseResponseStatus result = e.getStatus(); - return new BaseResponse(result); + return new BaseResponse(e.getStatus()); } + } + // 회원 정보 조회 + @GetMapping("/getInfo") + public BaseResponse getInfo() { + try { + GetMemberInfoResponse getMemberInfoResponse = memberService.getInfo(); + return new BaseResponse<>(BaseResponseStatus.SUCCESS, getMemberInfoResponse); + } catch (BaseException e) { + return new BaseResponse(e.getStatus()); + } } - /** - * test api - * @param dto - * @return - */ - @PostMapping("/test") - public ResponseEntity test(@RequestBody MemberJoinRequest dto) { - return ResponseEntity.ok().body("test 성공"); + + // 비밀번호 수정 + @PutMapping("/changePwd") + public BaseResponse changePwd(@RequestBody ChangePwdRequest changePwdRequest) { + try { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String email = authentication.getName(); + memberService.changePassword(email, changePwdRequest.getCurrentPassword(), + changePwdRequest.getNewPassword()); + String result = "비밀번호 수정 완료"; + return new BaseResponse<>(BaseResponseStatus.SUCCESS, result); + } catch (BaseException e) { + return new BaseResponse(e.getStatus()); + } + + } } diff --git a/src/main/java/com/likelion/dub/controller/MypageController.java b/src/main/java/com/likelion/dub/controller/MypageController.java deleted file mode 100644 index 5d20cf5..0000000 --- a/src/main/java/com/likelion/dub/controller/MypageController.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.likelion.dub.controller; - -import com.fasterxml.jackson.databind.ser.Serializers; -import com.likelion.dub.common.BaseException; -import com.likelion.dub.common.BaseResponse; -import com.likelion.dub.common.BaseResponseStatus; -import com.likelion.dub.domain.Member; -import com.likelion.dub.domain.dto.MyPageResponse; -import com.likelion.dub.domain.dto.PasswordRequest; -import com.likelion.dub.service.MemberService; -import com.likelion.dub.service.MypageService; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.crypto.bcrypt.BCrypt; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.web.bind.annotation.*; - -@RestController -@RequestMapping("/app/mypage") -@RequiredArgsConstructor -@Slf4j -@CrossOrigin(origins = "*", allowedHeaders = "*") //Cors 제거 -public class MypageController { - private final MypageService mypageService; - - - /** - * 마이페이지 정보조회 - * @return - */ - @GetMapping("/getInfo") - public BaseResponse getMyPage(){ - try { - //Spring Security Context 에서 인증 정보를 가져옴 - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication == null || !authentication.isAuthenticated()) { - return new BaseResponse(BaseResponseStatus.INVALID_MEMBER_JWT); - } - //사용자 정보를 추출하여 마이페이지 정보 반환 - String email = authentication.getName(); - Member member = mypageService.loadMemberByEmail(email); - - log.info(email.toString()); - log.info(member.toString()); - - MyPageResponse myPageResponse = new MyPageResponse(member.getEmail(), member.getName(), member.getStunum(), member.getRole()); - return new BaseResponse<>(myPageResponse); - } catch (BaseException e) { - return new BaseResponse(BaseResponseStatus.JWT_TOKEN_ERROR); - } - - } - - - /** - * 비밀번호 수정 - * @param passwordRequest - * @return - */ - @PutMapping("/password") - public BaseResponse changePassword(@RequestBody PasswordRequest passwordRequest) { - String currentPassword = passwordRequest.getCurrentPassword(); - String newPassword = passwordRequest.getNewPassword(); - - - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - //jwt token 오류 - if (authentication == null || !authentication.isAuthenticated()) { - return new BaseResponse(BaseResponseStatus.JWT_TOKEN_ERROR); - } - String email = authentication.getName(); - Member member = mypageService.loadMemberByEmail(email); - //current password wrong - if (!BCrypt.checkpw(passwordRequest.getCurrentPassword(), member.getPassword())) { - return new BaseResponse(BaseResponseStatus.WRONG_PASSWORD); - } - //새로운 비밀번호 암호화하여 업데이트 - String hashedPassword = BCrypt.hashpw(passwordRequest.getNewPassword(), BCrypt.gensalt()); - member.setPassword(hashedPassword); - mypageService.save(member); - - String result = "비밀번호 수정 완료"; - return new BaseResponse<>(result); - - } - -} diff --git a/src/main/java/com/likelion/dub/controller/PostController.java b/src/main/java/com/likelion/dub/controller/PostController.java index b6ad830..cd832c3 100644 --- a/src/main/java/com/likelion/dub/controller/PostController.java +++ b/src/main/java/com/likelion/dub/controller/PostController.java @@ -1,65 +1,64 @@ package com.likelion.dub.controller; -import com.fasterxml.jackson.databind.ser.Serializers; -import com.likelion.dub.common.BaseException; -import com.likelion.dub.common.BaseResponse; -import com.likelion.dub.common.BaseResponseStatus; -import com.likelion.dub.domain.Club; -import com.likelion.dub.domain.Member; -import com.likelion.dub.common.BaseResponseStatus; -import com.likelion.dub.domain.Post; -import com.likelion.dub.domain.dto.PostEditRequest; -import com.likelion.dub.domain.dto.PostWritingRequest; -import com.likelion.dub.service.MemberService; +import com.likelion.dub.baseResponse.BaseException; +import com.likelion.dub.baseResponse.BaseResponse; +import com.likelion.dub.baseResponse.BaseResponseStatus; +import com.likelion.dub.dto.Post.GetAllPostResponse; +import com.likelion.dub.dto.Post.GetOnePostResponse; +import com.likelion.dub.dto.Post.PostEditRequest; +import com.likelion.dub.dto.Post.WritingRequest; import com.likelion.dub.service.PostService; +import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; -import java.nio.file.Files; -import java.util.List; - @RestController @RequestMapping("/app/post") @RequiredArgsConstructor -@CrossOrigin(origins = "*", allowedHeaders = "*") //Cors 제거 @Slf4j public class PostController { + private final PostService postService; - /** - * 동아리 글 전체 조회 - * - * @param - * @return all post - */ @GetMapping("/getAll") - public BaseResponse> getAllClubs() { - return new BaseResponse<>(postService.getAllClubs()); - + public BaseResponse> getAllPost() { + try { + List getAllPostResponses = postService.getAllPost(); + return new BaseResponse<>(BaseResponseStatus.SUCCESS, getAllPostResponses); + } catch (BaseException e) { + return new BaseResponse<>(e.getStatus()); + } } - /** - * post 작성 - * - * @param dto - * @return - */ - @PostMapping("/write-post") - public BaseResponse writePost(@RequestPart(value = "json") PostWritingRequest dto, @RequestPart(value = "images", required = false) List files) throws BaseException { + @PostMapping(value = "/write-post") + public BaseResponse writePost(@ModelAttribute WritingRequest writingRequest) { try { - postService.writePost(dto.getTitle(), dto.getContent(), dto.getCategory(), files); - return new BaseResponse<>("글 작성 성공"); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String email = authentication.getName(); + String title = writingRequest.getTitle(); + String content = writingRequest.getContent(); + MultipartFile file = writingRequest.getImage(); + postService.writing(email, title, content, file); + return new BaseResponse<>(BaseResponseStatus.SUCCESS, "글 작성 성공"); } catch (BaseException e) { return new BaseResponse<>(e.getStatus()); } + } /** @@ -68,20 +67,29 @@ public BaseResponse writePost(@RequestPart(value = "json") PostWritingRe * @param id * @return */ - @GetMapping("/read-post") - public BaseResponse readPost(@RequestParam(value = "id", required = true) Long id) throws BaseException { - return new BaseResponse<>(postService.readPost(id)); + @GetMapping("/read-post/{id}") + public BaseResponse readPost(@PathVariable Long id) throws BaseException { + + try { + GetOnePostResponse getOnePostResponse = postService.readPost(id); + return new BaseResponse<>(BaseResponseStatus.SUCCESS, getOnePostResponse); + } catch (BaseException e) { + return new BaseResponse<>(e.getStatus()); + } + } @DeleteMapping("delete-post") - public BaseResponse deletePost(@RequestParam(value="id",required = true) Long id) throws BaseException { + public BaseResponse deletePost(@RequestParam(value = "id") Long id) { postService.deletePost(id); String result = "동아리 게시글 삭제 완료"; - return new BaseResponse<>(result); + return new BaseResponse<>(BaseResponseStatus.SUCCESS, result); } + @PutMapping("/edit-post") - public BaseResponse editPost(@RequestPart(value="json") PostEditRequest dto, @RequestPart(value="images", required = false) List images) throws BaseException{ + public BaseResponse editPost(@RequestPart(value = "json") PostEditRequest dto, + @RequestPart(value = "images", required = false) List images) { String newTitle = dto.getTitle(); String newContent = dto.getContent(); int newCategory = dto.getCategory(); @@ -93,8 +101,6 @@ public BaseResponse editPost(@RequestPart(value="json") PostEditRequest } String email = authentication.getName(); - postService.editPost(email, newTitle, newContent, newCategory, newImages); - return new BaseResponse<>(BaseResponseStatus.SUCCESS); } diff --git a/src/main/java/com/likelion/dub/domain/BaseEntity.java b/src/main/java/com/likelion/dub/domain/BaseEntity.java new file mode 100644 index 0000000..61cfab1 --- /dev/null +++ b/src/main/java/com/likelion/dub/domain/BaseEntity.java @@ -0,0 +1,21 @@ +package com.likelion.dub.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.LastModifiedBy; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +@EntityListeners(AuditingEntityListener.class) //이걸 생략하고 orm.xml에 등록하면 전체 적용이가능하다 +@MappedSuperclass +public abstract class BaseEntity extends BaseTimeEntity { + + @CreatedBy + @Column(updatable = false, length = 512) + private String createdBy; + @LastModifiedBy + @Column(length = 512) + private String lastModifiedBy; +} + diff --git a/src/main/java/com/likelion/dub/domain/BaseTimeEntity.java b/src/main/java/com/likelion/dub/domain/BaseTimeEntity.java new file mode 100644 index 0000000..e865ca7 --- /dev/null +++ b/src/main/java/com/likelion/dub/domain/BaseTimeEntity.java @@ -0,0 +1,22 @@ +package com.likelion.dub.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.time.LocalDateTime; +@EntityListeners(AuditingEntityListener.class) //이걸 생략하고 orm.xml에 등록하면 전체 적용이가능하다 +@MappedSuperclass +public abstract class BaseTimeEntity { + @CreatedDate + @Column(updatable = false) + private LocalDateTime createdDate; + @LastModifiedDate + @Column + private LocalDateTime lastModifiedDate; +} + + diff --git a/src/main/java/com/likelion/dub/domain/Club.java b/src/main/java/com/likelion/dub/domain/Club.java index 5eb786e..fc85bb7 100644 --- a/src/main/java/com/likelion/dub/domain/Club.java +++ b/src/main/java/com/likelion/dub/domain/Club.java @@ -1,20 +1,29 @@ package com.likelion.dub.domain; -import jakarta.persistence.*; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Lob; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; -import java.util.ArrayList; -import java.util.List; -@Builder @NoArgsConstructor @Entity @AllArgsConstructor @Getter +@Setter +@Table(name = "club") public class Club { @Id @@ -22,37 +31,33 @@ public class Club { @Column(name = "club_id") private Long id; - @Column - private String image; + @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; - @Column - @Lob - private String introduction; - @Column + @Column(length = 32, nullable = false) private String clubName; - @OneToOne(fetch=FetchType.LAZY) - private Post post; + @Lob + @Column + private String introduction; + @Column(length = 32, nullable = false) + private String groupName; - @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) - @JoinColumn(name = "member_id") - private Member member; - @OneToMany(mappedBy = "club") - private List tags = new ArrayList<>(); + @Column(length = 512, nullable = false) + private String clubImageUrl; - @OneToOne(mappedBy = "club", - cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, - orphanRemoval = true) - private ClubImage clubImage; + @Column(length = 512) + private String question1; + @Column(length = 512) + private String question2; - public void setMember(Member member) { - this.member = member; - member.setClub(this); - } + @Column(length = 512) + private String question3; } diff --git a/src/main/java/com/likelion/dub/domain/ClubApply.java b/src/main/java/com/likelion/dub/domain/ClubApply.java new file mode 100644 index 0000000..294e2f9 --- /dev/null +++ b/src/main/java/com/likelion/dub/domain/ClubApply.java @@ -0,0 +1,49 @@ +package com.likelion.dub.domain; + + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@NoArgsConstructor +@Entity +@AllArgsConstructor +@Getter +@Setter +@Table(name = "club_apply") +public class ClubApply extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + + @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @JoinColumn(name = "club_id") + private Club club; + + @Column(length = 512) + private String answer1; + + @Column(length = 512) + private String answer2; + + @Column(length = 512) + private String answer3; + +} diff --git a/src/main/java/com/likelion/dub/domain/ClubHashTag.java b/src/main/java/com/likelion/dub/domain/ClubHashTag.java new file mode 100644 index 0000000..ee32ad4 --- /dev/null +++ b/src/main/java/com/likelion/dub/domain/ClubHashTag.java @@ -0,0 +1,40 @@ +package com.likelion.dub.domain; + + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Table(name = "club_hashtag") +public class ClubHashTag { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + + @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @JoinColumn(name = "club_id") + private Club club; + + + @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @JoinColumn(name = "hashtag_id") + private HashTag hashTag; + +} diff --git a/src/main/java/com/likelion/dub/domain/ClubImage.java b/src/main/java/com/likelion/dub/domain/ClubImage.java deleted file mode 100644 index ee2c144..0000000 --- a/src/main/java/com/likelion/dub/domain/ClubImage.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.likelion.dub.domain; - - -import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@Entity -@NoArgsConstructor -@AllArgsConstructor -@Builder -@Table(name = "ClubImage") -public class ClubImage { - @Id - @GeneratedValue(strategy= GenerationType.IDENTITY) - @Column - private Long id; - - @OneToOne - @JoinColumn(name = "club_id") - private Club club; - - @Column - private String origFileName; - - @Column - private String filePath; - public ClubImage(String origFileName, String filePath, Long fileSize){ - this.origFileName = origFileName; - this.filePath = filePath; - this.fileSize = fileSize; - } - - - private Long fileSize; - - - -} - - diff --git a/src/main/java/com/likelion/dub/domain/HashTag.java b/src/main/java/com/likelion/dub/domain/HashTag.java new file mode 100644 index 0000000..90a61c4 --- /dev/null +++ b/src/main/java/com/likelion/dub/domain/HashTag.java @@ -0,0 +1,31 @@ +package com.likelion.dub.domain; + + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Table(name = "hashtag") +public class HashTag { + + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(length = 32, nullable = false) + private String tagName; + +} diff --git a/src/main/java/com/likelion/dub/domain/Image.java b/src/main/java/com/likelion/dub/domain/Image.java deleted file mode 100644 index aede6ef..0000000 --- a/src/main/java/com/likelion/dub/domain/Image.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.likelion.dub.domain; - -import jakarta.persistence.*; -import lombok.*; - -@Getter -@Entity -@NoArgsConstructor -@AllArgsConstructor -@Builder -@Table(name = "image") -public class Image { - @Id - @GeneratedValue(strategy= GenerationType.IDENTITY) - @Column - private Long id; - - @ManyToOne - @JoinColumn(name = "post_id") - private Post post; - - @Column - private String origFileName; - - @Column - private String filePath; - public Image(String origFileName, String filePath, Long fileSize){ - this.origFileName = origFileName; - this.filePath = filePath; - this.fileSize = fileSize; - } - - public void setPost(Post post){ - this.post = post; - - if(!post.getImage().contains(this)) - post.getImage().add(this); - } - - private Long fileSize; - -} diff --git a/src/main/java/com/likelion/dub/domain/Member.java b/src/main/java/com/likelion/dub/domain/Member.java index 14cb6d2..ae55f46 100644 --- a/src/main/java/com/likelion/dub/domain/Member.java +++ b/src/main/java/com/likelion/dub/domain/Member.java @@ -1,43 +1,49 @@ package com.likelion.dub.domain; -import jakarta.persistence.*; -import lombok.*; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -@Builder @NoArgsConstructor @Entity @AllArgsConstructor @Getter @Setter +@Table(name = "member") public class Member { + @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) //어플리케이션에서는 기본키 값을 미리 알수 없음, 엔티티를 저장하고 나서야 키 값 확인 기능 + @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "member_id") private Long id; - @Column - private String email; - @Column - private String name; - @Column - private String password; - @Column - private Long stunum; - @Column - private String role; - - @OneToOne(mappedBy = "member") private Club club; - public void setClub(Club club) { - this.club = club; - } + @Column(length = 512, nullable = false) + private String email; + + @Column(length = 32, nullable = false) + private String name; + + @Column(length = 512, nullable = false) + private String password; + + @Column(length = 32, nullable = true) + private String gender; + + @Column(length = 32, nullable = false) + private String role; } diff --git a/src/main/java/com/likelion/dub/domain/Post.java b/src/main/java/com/likelion/dub/domain/Post.java index be1321b..cec4c16 100644 --- a/src/main/java/com/likelion/dub/domain/Post.java +++ b/src/main/java/com/likelion/dub/domain/Post.java @@ -1,65 +1,47 @@ package com.likelion.dub.domain; -import jakarta.persistence.*; -import lombok.*; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Lob; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; -import java.util.ArrayList; -import java.util.List; -@Builder @NoArgsConstructor @Entity @AllArgsConstructor @Getter @Setter -public class Post { +@Table(name = "post") +public class Post extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Column - private String clubName; - @Column - private String title; - - @Column - @Lob - private String content; - - - @OneToOne(mappedBy="post") + @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = "club_id") private Club club; + @Column(length = 32, nullable = false) + private String postTitle; + @Lob + @Column(nullable = false) + private String content; - @OneToMany( - mappedBy = "post", - cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, - orphanRemoval = true - ) - private List image = new ArrayList<>(); - - @Column - private int category; - - - public void addImage(Image image){ - this.image.add(image); - - if(image.getPost() != this) - image.setPost(this); - } - - public static class PostBuilder { - private List image = new ArrayList<>(); - - public PostBuilder addImage(Image image) { - this.image.add(image); - return this; - } + @Column(length = 512) + private String postImage; - } } \ No newline at end of file diff --git a/src/main/java/com/likelion/dub/domain/Tag.java b/src/main/java/com/likelion/dub/domain/Tag.java deleted file mode 100644 index 9ea3ad2..0000000 --- a/src/main/java/com/likelion/dub/domain/Tag.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.likelion.dub.domain; - - -import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -import java.util.List; - -@Builder -@NoArgsConstructor -@Entity -@AllArgsConstructor -@Getter -public class Tag { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "tag_id") - private Long id; - - - @Column - private String tag_name; - - @ManyToOne - @JoinColumn(name = "club_id") - private Club club; - -} diff --git a/src/main/java/com/likelion/dub/domain/dto/ImageDto.java b/src/main/java/com/likelion/dub/domain/dto/ImageDto.java deleted file mode 100644 index 1b7a328..0000000 --- a/src/main/java/com/likelion/dub/domain/dto/ImageDto.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.likelion.dub.domain.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Builder -@Getter -@AllArgsConstructor -@NoArgsConstructor -public class ImageDto { - @JsonProperty - private String origFileName; - - @JsonProperty - private String filePath; - - @JsonProperty - private Long fileSize; - -} diff --git a/src/main/java/com/likelion/dub/domain/dto/MyPageResponse.java b/src/main/java/com/likelion/dub/domain/dto/MyPageResponse.java deleted file mode 100644 index 3885b08..0000000 --- a/src/main/java/com/likelion/dub/domain/dto/MyPageResponse.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.likelion.dub.domain.dto; - - -import com.fasterxml.jackson.annotation.JsonProperty; -import jakarta.persistence.Column; -import jakarta.persistence.JoinColumn; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@AllArgsConstructor -@Getter -public class MyPageResponse { - @JsonProperty - private String email; - @JsonProperty - private String username; - @JsonProperty - private Long stunum; - @JsonProperty - private String role; - - -} diff --git a/src/main/java/com/likelion/dub/domain/dto/PostGetRequest.java b/src/main/java/com/likelion/dub/dto/Club/UpdateTagRequest.java similarity index 58% rename from src/main/java/com/likelion/dub/domain/dto/PostGetRequest.java rename to src/main/java/com/likelion/dub/dto/Club/UpdateTagRequest.java index 4c591c6..abcd366 100644 --- a/src/main/java/com/likelion/dub/domain/dto/PostGetRequest.java +++ b/src/main/java/com/likelion/dub/dto/Club/UpdateTagRequest.java @@ -1,4 +1,5 @@ -package com.likelion.dub.domain.dto; +package com.likelion.dub.dto.Club; + import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; @@ -6,12 +7,13 @@ @Getter @AllArgsConstructor -public class PostGetRequest { +public class UpdateTagRequest { @JsonProperty - private String clubName; + private String groupName; + @JsonProperty - private String title; + private String category; } diff --git a/src/main/java/com/likelion/dub/domain/dto/PasswordRequest.java b/src/main/java/com/likelion/dub/dto/Member/ChangePwdRequest.java similarity index 78% rename from src/main/java/com/likelion/dub/domain/dto/PasswordRequest.java rename to src/main/java/com/likelion/dub/dto/Member/ChangePwdRequest.java index 736c0ab..b4a2ea7 100644 --- a/src/main/java/com/likelion/dub/domain/dto/PasswordRequest.java +++ b/src/main/java/com/likelion/dub/dto/Member/ChangePwdRequest.java @@ -1,4 +1,4 @@ -package com.likelion.dub.domain.dto; +package com.likelion.dub.dto.Member; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; @@ -6,7 +6,7 @@ @AllArgsConstructor @Getter -public class PasswordRequest { +public class ChangePwdRequest { @JsonProperty @@ -15,5 +15,4 @@ public class PasswordRequest { private String newPassword; - } diff --git a/src/main/java/com/likelion/dub/dto/Member/ClubMemberJoinRequest.java b/src/main/java/com/likelion/dub/dto/Member/ClubMemberJoinRequest.java new file mode 100644 index 0000000..51fee6c --- /dev/null +++ b/src/main/java/com/likelion/dub/dto/Member/ClubMemberJoinRequest.java @@ -0,0 +1,37 @@ +package com.likelion.dub.dto.Member; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.persistence.Lob; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + + +@Getter +@Setter +@NoArgsConstructor +public class ClubMemberJoinRequest { + + @JsonProperty + private String email; + @JsonProperty + private String name; + @JsonProperty + private String password; + + @JsonProperty + private String role; + + @JsonProperty + private String group; + + @JsonProperty + private String category; + + @JsonProperty + @Lob + private String introduction; + + +} diff --git a/src/main/java/com/likelion/dub/dto/Member/GetMemberInfoResponse.java b/src/main/java/com/likelion/dub/dto/Member/GetMemberInfoResponse.java new file mode 100644 index 0000000..89b2c63 --- /dev/null +++ b/src/main/java/com/likelion/dub/dto/Member/GetMemberInfoResponse.java @@ -0,0 +1,25 @@ +package com.likelion.dub.dto.Member; + + +import com.likelion.dub.domain.Club; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@Getter +@Setter +@NoArgsConstructor +public class GetMemberInfoResponse { + + private String email; + private String name; + + private String gender; + private String role; + + private Club club; + + +} diff --git a/src/main/java/com/likelion/dub/domain/dto/MemberJoinRequest.java b/src/main/java/com/likelion/dub/dto/Member/MemberJoinRequest.java similarity index 65% rename from src/main/java/com/likelion/dub/domain/dto/MemberJoinRequest.java rename to src/main/java/com/likelion/dub/dto/Member/MemberJoinRequest.java index 891ab47..d0ad1f2 100644 --- a/src/main/java/com/likelion/dub/domain/dto/MemberJoinRequest.java +++ b/src/main/java/com/likelion/dub/dto/Member/MemberJoinRequest.java @@ -1,13 +1,19 @@ -package com.likelion.dub.domain.dto; +package com.likelion.dub.dto.Member; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.annotation.Nullable; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; @AllArgsConstructor @Getter +@NoArgsConstructor +@Setter public class MemberJoinRequest { + @JsonProperty private String email; @JsonProperty @@ -15,11 +21,10 @@ public class MemberJoinRequest { @JsonProperty private String password; @JsonProperty - private Long stunum; + @Nullable + private String gender; @JsonProperty private String role; - - } diff --git a/src/main/java/com/likelion/dub/domain/dto/MemberLoginRequest.java b/src/main/java/com/likelion/dub/dto/Member/MemberLoginRequest.java similarity index 70% rename from src/main/java/com/likelion/dub/domain/dto/MemberLoginRequest.java rename to src/main/java/com/likelion/dub/dto/Member/MemberLoginRequest.java index ec5e55a..7f374b2 100644 --- a/src/main/java/com/likelion/dub/domain/dto/MemberLoginRequest.java +++ b/src/main/java/com/likelion/dub/dto/Member/MemberLoginRequest.java @@ -1,11 +1,12 @@ -package com.likelion.dub.domain.dto; +package com.likelion.dub.dto.Member; import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; -@AllArgsConstructor + +@NoArgsConstructor @Getter public class MemberLoginRequest { diff --git a/src/main/java/com/likelion/dub/dto/Member/ToClubRequest.java b/src/main/java/com/likelion/dub/dto/Member/ToClubRequest.java new file mode 100644 index 0000000..1a281b3 --- /dev/null +++ b/src/main/java/com/likelion/dub/dto/Member/ToClubRequest.java @@ -0,0 +1,40 @@ +package com.likelion.dub.dto.Member; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.persistence.Lob; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class ToClubRequest { + + @JsonProperty + private String clubName; + + @Lob + @JsonProperty + private String introduction; + + @JsonProperty + private String group; + + @JsonProperty + private String clubImageUrl; + + @JsonProperty + private String question1; + + @JsonProperty + private String question2; + + @JsonProperty + private String question3; + + +} diff --git a/src/main/java/com/likelion/dub/dto/OAuth/KakaoApiClient.java b/src/main/java/com/likelion/dub/dto/OAuth/KakaoApiClient.java new file mode 100644 index 0000000..9485729 --- /dev/null +++ b/src/main/java/com/likelion/dub/dto/OAuth/KakaoApiClient.java @@ -0,0 +1,69 @@ +package com.likelion.dub.dto.OAuth; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +@Component +@RequiredArgsConstructor +public class KakaoApiClient implements OAuthApiClient { + + private static final String GRANT_TYPE = "authorization_code"; + + @Value("${oauth.kakao.url.auth}") + private String authUrl; + + @Value("${oauth.kakao.url.api}") + private String apiUrl; + + @Value("${oauth.kakao.client-id}") + private String clientId; + + private final RestTemplate restTemplate; + + @Override + public OAuthProvider oAuthProvider() { + return OAuthProvider.KAKAO; + } + + @Override + public String requestAccessToken(OAuthLoginParams params) { + String url = authUrl + "/oauth/token"; + + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + MultiValueMap body = params.makeBody(); + body.add("grant_type", GRANT_TYPE); + body.add("client_id", clientId); + + HttpEntity request = new HttpEntity<>(body, httpHeaders); + + KakaoTokens response = restTemplate.postForObject(url, request, KakaoTokens.class); + + assert response != null; + return response.getAccessToken(); + } + + @Override + public OAuthInfoResponse requestOauthInfo(String accessToken) { + String url = apiUrl + "/v2/user/me"; + + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + httpHeaders.set("Authorization", "Bearer " + accessToken); + + MultiValueMap body = new LinkedMultiValueMap<>(); + body.add("property_keys", "[\"kakao_account.email\", \"kakao_account.profile\"]"); + + HttpEntity request = new HttpEntity<>(body, httpHeaders); + + return restTemplate.postForObject(url, request, KakaoInfoResponse.class); + } +} \ No newline at end of file diff --git a/src/main/java/com/likelion/dub/dto/OAuth/KakaoInfoResponse.java b/src/main/java/com/likelion/dub/dto/OAuth/KakaoInfoResponse.java new file mode 100644 index 0000000..a3ec807 --- /dev/null +++ b/src/main/java/com/likelion/dub/dto/OAuth/KakaoInfoResponse.java @@ -0,0 +1,43 @@ +package com.likelion.dub.dto.OAuth; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; + +@Getter +@JsonIgnoreProperties(ignoreUnknown = true) +public class KakaoInfoResponse implements OAuthInfoResponse { + + @JsonProperty("kakao_account") + private KakaoAccount kakaoAccount; + + @Getter + @JsonIgnoreProperties(ignoreUnknown = true) + static class KakaoAccount { + + private KakaoProfile profile; + private String email; + } + + @Getter + @JsonIgnoreProperties(ignoreUnknown = true) + static class KakaoProfile { + + private String nickname; + } + + @Override + public String getEmail() { + return kakaoAccount.email; + } + + @Override + public String getNickname() { + return kakaoAccount.profile.nickname; + } + + @Override + public OAuthProvider getOAuthProvider() { + return OAuthProvider.KAKAO; + } +} \ No newline at end of file diff --git a/src/main/java/com/likelion/dub/dto/OAuth/KakaoLoginParams.java b/src/main/java/com/likelion/dub/dto/OAuth/KakaoLoginParams.java new file mode 100644 index 0000000..01119cf --- /dev/null +++ b/src/main/java/com/likelion/dub/dto/OAuth/KakaoLoginParams.java @@ -0,0 +1,32 @@ +package com.likelion.dub.dto.OAuth; + + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class KakaoLoginParams implements OAuthLoginParams { + + + private String authorizationCode; + + @Override + public OAuthProvider oAuthProvider() { + return OAuthProvider.KAKAO; + } + + @Override + public MultiValueMap makeBody() { + MultiValueMap body = new LinkedMultiValueMap<>(); + body.add("code", authorizationCode); + return body; + } + +} diff --git a/src/main/java/com/likelion/dub/dto/OAuth/KakaoTokens.java b/src/main/java/com/likelion/dub/dto/OAuth/KakaoTokens.java new file mode 100644 index 0000000..6356b21 --- /dev/null +++ b/src/main/java/com/likelion/dub/dto/OAuth/KakaoTokens.java @@ -0,0 +1,28 @@ +package com.likelion.dub.dto.OAuth; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class KakaoTokens { + + @JsonProperty("access_token") + private String accessToken; + + @JsonProperty("token_type") + private String tokenType; + + @JsonProperty("refresh_token") + private String refreshToken; + + @JsonProperty("expires_in") + private String expiresIn; + + @JsonProperty("refresh_token_expires_in") + private String refreshTokenExpiresIn; + + @JsonProperty("scope") + private String scope; +} \ No newline at end of file diff --git a/src/main/java/com/likelion/dub/dto/OAuth/OAuthApiClient.java b/src/main/java/com/likelion/dub/dto/OAuth/OAuthApiClient.java new file mode 100644 index 0000000..c19ab52 --- /dev/null +++ b/src/main/java/com/likelion/dub/dto/OAuth/OAuthApiClient.java @@ -0,0 +1,10 @@ +package com.likelion.dub.dto.OAuth; + +public interface OAuthApiClient { + + OAuthProvider oAuthProvider(); + + String requestAccessToken(OAuthLoginParams params); + + OAuthInfoResponse requestOauthInfo(String accessToken); +} \ No newline at end of file diff --git a/src/main/java/com/likelion/dub/dto/OAuth/OAuthInfoResponse.java b/src/main/java/com/likelion/dub/dto/OAuth/OAuthInfoResponse.java new file mode 100644 index 0000000..c205276 --- /dev/null +++ b/src/main/java/com/likelion/dub/dto/OAuth/OAuthInfoResponse.java @@ -0,0 +1,10 @@ +package com.likelion.dub.dto.OAuth; + +public interface OAuthInfoResponse { + + String getEmail(); + + String getNickname(); + + OAuthProvider getOAuthProvider(); +} \ No newline at end of file diff --git a/src/main/java/com/likelion/dub/dto/OAuth/OAuthLoginParams.java b/src/main/java/com/likelion/dub/dto/OAuth/OAuthLoginParams.java new file mode 100644 index 0000000..ed8ebb9 --- /dev/null +++ b/src/main/java/com/likelion/dub/dto/OAuth/OAuthLoginParams.java @@ -0,0 +1,10 @@ +package com.likelion.dub.dto.OAuth; + +import org.springframework.util.MultiValueMap; + +public interface OAuthLoginParams { + + OAuthProvider oAuthProvider(); + + MultiValueMap makeBody(); +} diff --git a/src/main/java/com/likelion/dub/dto/OAuth/OAuthProvider.java b/src/main/java/com/likelion/dub/dto/OAuth/OAuthProvider.java new file mode 100644 index 0000000..892ea00 --- /dev/null +++ b/src/main/java/com/likelion/dub/dto/OAuth/OAuthProvider.java @@ -0,0 +1,6 @@ +package com.likelion.dub.dto.OAuth; + +public enum OAuthProvider { + KAKAO, NAVER +} + diff --git a/src/main/java/com/likelion/dub/dto/Post/GetAllPostResponse.java b/src/main/java/com/likelion/dub/dto/Post/GetAllPostResponse.java new file mode 100644 index 0000000..340643d --- /dev/null +++ b/src/main/java/com/likelion/dub/dto/Post/GetAllPostResponse.java @@ -0,0 +1,22 @@ +package com.likelion.dub.dto.Post; + + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@Getter +@Setter +@NoArgsConstructor +public class GetAllPostResponse { + + private Long id; + private String title; + private String clubName; + + private String clubImage; + + +} diff --git a/src/main/java/com/likelion/dub/dto/Post/GetOnePostResponse.java b/src/main/java/com/likelion/dub/dto/Post/GetOnePostResponse.java new file mode 100644 index 0000000..fc40a4d --- /dev/null +++ b/src/main/java/com/likelion/dub/dto/Post/GetOnePostResponse.java @@ -0,0 +1,28 @@ +package com.likelion.dub.dto.Post; + +import jakarta.persistence.Lob; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@Getter +@Setter +@NoArgsConstructor +public class GetOnePostResponse { + + + private String title; + private String clubName; + @Lob + private String content; + + private List comments; + + private String postImage; + + private String form; + +} diff --git a/src/main/java/com/likelion/dub/domain/dto/PostEditRequest.java b/src/main/java/com/likelion/dub/dto/Post/PostEditRequest.java similarity index 89% rename from src/main/java/com/likelion/dub/domain/dto/PostEditRequest.java rename to src/main/java/com/likelion/dub/dto/Post/PostEditRequest.java index 393b639..5ba0771 100644 --- a/src/main/java/com/likelion/dub/domain/dto/PostEditRequest.java +++ b/src/main/java/com/likelion/dub/dto/Post/PostEditRequest.java @@ -1,4 +1,4 @@ -package com.likelion.dub.domain.dto; +package com.likelion.dub.dto.Post; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; @@ -7,6 +7,7 @@ @Getter @AllArgsConstructor public class PostEditRequest { + @JsonProperty private String title; @JsonProperty diff --git a/src/main/java/com/likelion/dub/domain/dto/PostWritingRequest.java b/src/main/java/com/likelion/dub/dto/Post/PostWritingRequest.java similarity index 62% rename from src/main/java/com/likelion/dub/domain/dto/PostWritingRequest.java rename to src/main/java/com/likelion/dub/dto/Post/PostWritingRequest.java index 3822d20..8a1297d 100644 --- a/src/main/java/com/likelion/dub/domain/dto/PostWritingRequest.java +++ b/src/main/java/com/likelion/dub/dto/Post/PostWritingRequest.java @@ -1,24 +1,21 @@ -package com.likelion.dub.domain.dto; +package com.likelion.dub.dto.Post; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.persistence.Lob; import lombok.AllArgsConstructor; import lombok.Getter; -import org.springframework.web.multipart.MultipartFile; - -import java.util.List; @Getter @AllArgsConstructor public class PostWritingRequest { + @JsonProperty private String title; + + @Lob @JsonProperty private String content; - @JsonProperty - private int category; - - } diff --git a/src/main/java/com/likelion/dub/dto/Post/WritingRequest.java b/src/main/java/com/likelion/dub/dto/Post/WritingRequest.java new file mode 100644 index 0000000..cdb529e --- /dev/null +++ b/src/main/java/com/likelion/dub/dto/Post/WritingRequest.java @@ -0,0 +1,24 @@ +package com.likelion.dub.dto.Post; + + +import jakarta.annotation.Nullable; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.web.multipart.MultipartFile; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class WritingRequest { + + private String title; + private String content; + + @Nullable + private MultipartFile image; + + +} diff --git a/src/main/java/com/likelion/dub/exception/AppException.java b/src/main/java/com/likelion/dub/exception/AppException.java deleted file mode 100644 index 0e63314..0000000 --- a/src/main/java/com/likelion/dub/exception/AppException.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.likelion.dub.exception; - - -import lombok.AllArgsConstructor; -import lombok.Getter; - -@AllArgsConstructor -@Getter -public class AppException extends RuntimeException{ - private Errorcode errorCode; - - - - -} diff --git a/src/main/java/com/likelion/dub/exception/Errorcode.java b/src/main/java/com/likelion/dub/exception/Errorcode.java deleted file mode 100644 index 93306df..0000000 --- a/src/main/java/com/likelion/dub/exception/Errorcode.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.likelion.dub.exception; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.springframework.http.HttpStatus; - -@AllArgsConstructor -@Getter -public enum Errorcode { - - //Member - //이메일 중복 체크 - EMAIL_CHECK_COMPLETE(HttpStatus.OK,1000,"이메일 사용 가능"), - EMAIL_DUPLICATED(HttpStatus.CONFLICT, 2000, "중복된 이메일이 있습니다"), - - //학번 중복 체크 - STU_NUM_CHECK_COMPLETE(HttpStatus.OK,1000,"학번 사용 가능"), - STU_NUM_DUPLICATED(HttpStatus.CONFLICT, 2000, "중복된 학번이 있습니다"), - - //회원가입 - JOIN_COMPLETE(HttpStatus.OK, 1000, "회원 가입 완료"), - JOIN_FAILED(HttpStatus.OK, 2000, "회원 가입 실패"), - - //로그인 - LOGIN_COMPLETE(HttpStatus.OK, 1000, "로그인 완료"), - LOGIN_FAILED(HttpStatus.OK, 2000, "로그인 실패"), - - - - //Post - - - //Club - - //Admin - - SUCCESS(HttpStatus.OK, 1000, "OK"), - USERNAME_DUPLICATED(HttpStatus.CONFLICT,2000, "중복된 이름이 있습니다."), - INVALID_PASSWORD(HttpStatus.UNAUTHORIZED, 2001,"패스워드가 맞지 않습니다."), - CLUB_EXIST(HttpStatus.CONFLICT, 3000,"이미 작성한 글이 있습니다." ), - ID_DOES_NOT_EXIST(HttpStatus.CONFLICT, 3001,"글이 존재하지 않습니다.") - ; - - private HttpStatus httpStatus; - private Integer code; - private String message; - - -} diff --git a/src/main/java/com/likelion/dub/exception/ExceptionManager.java b/src/main/java/com/likelion/dub/exception/ExceptionManager.java deleted file mode 100644 index cb7d77b..0000000 --- a/src/main/java/com/likelion/dub/exception/ExceptionManager.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.likelion.dub.exception; - -import com.fasterxml.jackson.databind.ser.Serializers; -import com.likelion.dub.common.BaseException; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RestControllerAdvice; - -@RestControllerAdvice -public class ExceptionManager { - @ExceptionHandler(BaseException.class) - public ResponseEntity baseExceptionHandler(BaseException e) { - return ResponseEntity.status(HttpStatus.OK) - .body(e.getStatus() + " "+e.getMessage()); - - } - -} diff --git a/src/main/java/com/likelion/dub/repository/ImageRepository.java b/src/main/java/com/likelion/dub/repository/ImageRepository.java deleted file mode 100644 index 8fa6479..0000000 --- a/src/main/java/com/likelion/dub/repository/ImageRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.likelion.dub.repository; - -import com.likelion.dub.domain.Image; -import jakarta.persistence.EntityManager; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface ImageRepository extends JpaRepository { - -} diff --git a/src/main/java/com/likelion/dub/repository/MemberRepository.java b/src/main/java/com/likelion/dub/repository/MemberRepository.java index 51a2ba2..a6705e4 100644 --- a/src/main/java/com/likelion/dub/repository/MemberRepository.java +++ b/src/main/java/com/likelion/dub/repository/MemberRepository.java @@ -1,16 +1,11 @@ package com.likelion.dub.repository; import com.likelion.dub.domain.Member; -import org.springframework.data.jpa.repository.JpaRepository; - import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; public interface MemberRepository extends JpaRepository { Optional findByEmail(String email); - Optional findByStunum(Long stunum); - - - } diff --git a/src/main/java/com/likelion/dub/repository/MypageRepository.java b/src/main/java/com/likelion/dub/repository/MypageRepository.java deleted file mode 100644 index dfb07fa..0000000 --- a/src/main/java/com/likelion/dub/repository/MypageRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.likelion.dub.repository; - -import org.springframework.data.jpa.repository.JpaRepository; - -public interface MypageRepository { -} diff --git a/src/main/java/com/likelion/dub/repository/PostRepository.java b/src/main/java/com/likelion/dub/repository/PostRepository.java index 288614c..847d4b9 100644 --- a/src/main/java/com/likelion/dub/repository/PostRepository.java +++ b/src/main/java/com/likelion/dub/repository/PostRepository.java @@ -1,20 +1,19 @@ package com.likelion.dub.repository; -import com.likelion.dub.domain.Member; import com.likelion.dub.domain.Post; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - import java.util.List; import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + -@Repository public interface PostRepository extends JpaRepository { + + @Query("select p from Post p join fetch p.club") List findAll(); - Optional findByClubName(String clubName); + Optional findById(Long id); - ; - // Optional findByEmail(String email); + } diff --git a/src/main/java/com/likelion/dub/service/ClubService.java b/src/main/java/com/likelion/dub/service/ClubService.java new file mode 100644 index 0000000..b48f846 --- /dev/null +++ b/src/main/java/com/likelion/dub/service/ClubService.java @@ -0,0 +1,94 @@ +package com.likelion.dub.service; + + +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.likelion.dub.baseResponse.BaseException; +import com.likelion.dub.baseResponse.BaseResponseStatus; +import com.likelion.dub.domain.Club; +import com.likelion.dub.domain.Member; +import com.likelion.dub.repository.ClubRepository; +import com.likelion.dub.repository.MemberRepository; +import java.io.IOException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + + +@Service +@Transactional +@RequiredArgsConstructor +@Slf4j +public class ClubService { + + private final ClubRepository clubRepository; + private final MemberRepository memberRepository; + private final AmazonS3Client amazonS3Client; + + @Value("${cloud.aws.s3.bucket}") + private String bucket; + + + public void uploadForm(String url) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String email = authentication.getName(); + Member member = memberRepository.findByEmail(email) + .orElseThrow(() -> new BaseException(BaseResponseStatus.WRONG_EMAIL)); + String clubName = member.getClub().getClubName(); + Club club = clubRepository.findByClubName(clubName) + .orElseThrow(() -> new BaseException(BaseResponseStatus.NO_SUCH_CLUB_EXIST)); + + clubRepository.save(club); + + } + + public void updateIntroduce(String introduction) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String email = authentication.getName(); + Member member = memberRepository.findByEmail(email) + .orElseThrow(() -> new BaseException(BaseResponseStatus.WRONG_EMAIL)); + String clubName = member.getClub().getClubName(); + Club club = clubRepository.findByClubName(clubName) + .orElseThrow(() -> new BaseException(BaseResponseStatus.NO_SUCH_CLUB_EXIST)); + club.setIntroduction(introduction); + clubRepository.save(club); + } + + + public void updateClubImage(MultipartFile file) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String email = authentication.getName(); + Member member = memberRepository.findByEmail(email) + .orElseThrow(() -> new BaseException(BaseResponseStatus.WRONG_EMAIL)); + Club club = member.getClub(); + String clubName = club.getClubName(); + String fileName = clubName + "_" + "ClubImage"; + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentType(file.getContentType()); + metadata.setContentLength(file.getSize()); + try { + amazonS3Client.putObject(bucket, fileName, file.getInputStream(), metadata); + club.setClubImageUrl("https://dubs3.s3.ap-northeast-2.amazonaws.com/" + fileName); + } catch (IOException e) { + throw new BaseException(BaseResponseStatus.FILE_SAVE_ERROR); + } + clubRepository.save(club); + + } + + public void updateTag(String groupName, String category) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String email = authentication.getName(); + Member member = memberRepository.findByEmail(email) + .orElseThrow(() -> new BaseException(BaseResponseStatus.WRONG_EMAIL)); + Club club = member.getClub(); + club.setGroupName(groupName); + clubRepository.save(club); + } + +} diff --git a/src/main/java/com/likelion/dub/service/FileHandler.java b/src/main/java/com/likelion/dub/service/FileHandler.java deleted file mode 100644 index edc25ad..0000000 --- a/src/main/java/com/likelion/dub/service/FileHandler.java +++ /dev/null @@ -1,115 +0,0 @@ -package com.likelion.dub.service; - -import com.likelion.dub.domain.Image; -import com.likelion.dub.domain.Post; -import com.likelion.dub.domain.dto.ImageDto; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.springframework.util.CollectionUtils; -import org.springframework.util.ObjectUtils; -import org.springframework.web.multipart.MultipartFile; - -import java.io.File; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.List; - -@Component -@RequiredArgsConstructor -@Slf4j -public class FileHandler { - private final ImageService imageService; - public List parseFileInfo(List multipartFiles) throws RuntimeException{ - List filelist = new ArrayList<>(); - if(!CollectionUtils.isEmpty(multipartFiles)) { - // 파일명을 업로드 한 날짜로 변환하여 저장 - LocalDateTime now = LocalDateTime.now(); - DateTimeFormatter dateTimeFormatter = - DateTimeFormatter.ofPattern("yyyyMMdd"); - String current_date = now.format(dateTimeFormatter); - - // 프로젝트 디렉터리 내의 저장을 위한 절대 경로 설정 - // 경로 구분자 File.separator 사용 - String absolutePath = new File("").getAbsolutePath() + File.separator + File.separator; - - // 파일을 저장할 세부 경로 지정 - - String path = "images" + File.separator + current_date; - File file = new File(path); - log.info(file.toString()); - - // 디렉터리가 존재하지 않을 경우 - if(!file.exists()) { - boolean wasSuccessful = file.mkdirs(); - - // 디렉터리 생성에 실패했을 경우 - if(!wasSuccessful) - System.out.println("file: was not successful"); - } - - // 다중 파일 처리 - for(MultipartFile multipartFile : multipartFiles) { - - // 파일의 확장자 추출 - String originalFileExtension; - String contentType = multipartFile.getContentType(); - log.info(contentType); - - // 확장자명이 존재하지 않을 경우 처리 x - if(ObjectUtils.isEmpty(contentType)) { - break; - } - else { // 확장자가 jpeg, png인 파일들만 받아서 처리 - if(contentType.contains("image/jpeg")) - originalFileExtension = ".jpg"; - else if(contentType.contains("image/png")) - originalFileExtension = ".png"; - else // 다른 확장자일 경우 처리 x - break; - } - - // 파일명 중복 피하고자 나노초까지 얻어와 지정 - String new_file_name = System.nanoTime() + originalFileExtension; - - // 파일 DTO 생성 - ImageDto imageDto = ImageDto.builder() - .origFileName(multipartFile.getOriginalFilename()) - .filePath(path + File.separator + new_file_name) - .fileSize(multipartFile.getSize()) - .build(); - log.info(imageDto.toString()); - // 파일 DTO 이용하여 Photo 엔티티 생성 - Image photo = new Image( - imageDto.getOrigFileName(), - imageDto.getFilePath(), - imageDto.getFileSize() - ); - - - // 생성 후 리스트에 추가 - filelist.add(photo); - - // 업로드 한 파일 데이터를 지정한 파일에 저장 - file = new File(absolutePath + path + File.separator + new_file_name); - try{ - multipartFile.transferTo(file); - } - catch(IOException e){ - throw new UncheckedIOException("not yet", e); - } - log.info(filelist.toString()); - // 파일 권한 설정(쓰기, 읽기) - file.setWritable(true); - file.setReadable(true); - } - } - - return filelist; - } - - -} diff --git a/src/main/java/com/likelion/dub/service/ImageService.java b/src/main/java/com/likelion/dub/service/ImageService.java deleted file mode 100644 index 9975601..0000000 --- a/src/main/java/com/likelion/dub/service/ImageService.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.likelion.dub.service; - -import lombok.Builder; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Builder -@Service -@RequiredArgsConstructor -public class ImageService { - -} diff --git a/src/main/java/com/likelion/dub/service/MemberService.java b/src/main/java/com/likelion/dub/service/MemberService.java index 73b62ac..2655acf 100644 --- a/src/main/java/com/likelion/dub/service/MemberService.java +++ b/src/main/java/com/likelion/dub/service/MemberService.java @@ -1,127 +1,159 @@ package com.likelion.dub.service; -import com.likelion.dub.common.BaseException; -import com.likelion.dub.common.BaseResponseStatus; +import com.amazonaws.services.s3.AmazonS3Client; +import com.likelion.dub.baseResponse.BaseException; +import com.likelion.dub.baseResponse.BaseResponseStatus; import com.likelion.dub.domain.Club; import com.likelion.dub.domain.Member; -import com.likelion.dub.domain.Post; -import com.likelion.dub.domain.Role; -import com.likelion.dub.exception.AppException; -import com.likelion.dub.exception.Errorcode; +import com.likelion.dub.dto.Member.GetMemberInfoResponse; +import com.likelion.dub.dto.Member.MemberJoinRequest; +import com.likelion.dub.dto.Member.ToClubRequest; +import com.likelion.dub.dto.OAuth.OAuthInfoResponse; +import com.likelion.dub.dto.OAuth.OAuthLoginParams; import com.likelion.dub.repository.ClubRepository; import com.likelion.dub.repository.MemberRepository; -import com.likelion.dub.utils.JwtTokenUtil; -import jakarta.persistence.EntityNotFoundException; +import com.likelion.dub.util.JwtTokenUtil; +import java.util.Optional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; -import java.util.Optional; - -import static java.lang.Boolean.FALSE; -import static java.lang.Boolean.TRUE; - -@RequiredArgsConstructor @Service +@Transactional +@RequiredArgsConstructor @Slf4j public class MemberService { + private final MemberRepository memberRepository; private final ClubRepository clubRepository; + private final BCryptPasswordEncoder bCryptPasswordEncoder; + private final AmazonS3Client amazonS3Client; + private final RequestOAuthInfoService requestOAuthInfoService; - - private final BCryptPasswordEncoder encoder; - - - + @Value("${cloud.aws.s3.bucket}") + private String bucket; @Value("${jwt.token.secret}") private String key; private Long expireTimeMs = 1000 * 60 * 60L; //1시간 - public boolean checkEmail(String email) { Optional member = memberRepository.findByEmail(email); - return !member.isPresent(); - } - - - public boolean checkStunum(Long stunum) { - Optional member = memberRepository.findByStunum(stunum); - return !member.isPresent(); + if (member.isPresent()) { + throw new BaseException(BaseResponseStatus.EMAIL_ALREADY_EXIST); + } + return true; } - - public void join(String email, String name, String password, Long stunum, String role) { + public String join(MemberJoinRequest memberJoinRequest) { // 중복 이메일 검사 - Optional existingMember = memberRepository.findByEmail(email); + Optional existingMember = memberRepository.findByEmail(memberJoinRequest.getEmail()); if (existingMember.isPresent()) { throw new BaseException(BaseResponseStatus.EMAIL_ALREADY_EXIST); } + Member member = new Member(); + member.setEmail(memberJoinRequest.getEmail()); + member.setName(memberJoinRequest.getName()); + String hashedPassword = bCryptPasswordEncoder.encode(memberJoinRequest.getPassword()); + member.setPassword(hashedPassword); + member.setGender(memberJoinRequest.getGender()); + member.setRole("USER"); + memberRepository.save(member); + return member.getName(); + } - //저장 - if (role.equals("CLUB")) { - - Club club = Club.builder() - .clubName(name) - .build(); - clubRepository.save(club); - Member member = Member.builder() - .email(email) - .password(encoder.encode(password)) - .stunum(stunum) - .name(name) - .role(role) - .club(club) - .build(); - club.setMember(member); - memberRepository.save(member); - - - } else if (role.equals("USER")) { - Member member = Member.builder() - .email(email) - .password(encoder.encode(password)) - .stunum(stunum) - .role(role) - .name(name) - .build(); - memberRepository.save(member); - } else if (role.equals("ADMIN")) { - //admin 나중에 구현 - } - - + public String transferToClub(String email, ToClubRequest toClubRequest) { + Member member = memberRepository.findByEmail(email) + .orElseThrow(() -> new BaseException(BaseResponseStatus.NO_SUCH_MEMBER_EXIST)); + Club club = new Club(); + club.setClubName(toClubRequest.getClubName()); + club.setIntroduction(toClubRequest.getIntroduction()); + club.setGroupName(toClubRequest.getGroup()); + club.setClubImageUrl(toClubRequest.getClubImageUrl()); + club.setQuestion1("지원동기??"); + club.setMember(member); + member.setClub(club); //변경감지 + member.setRole("CLUB"); //변경감지 + clubRepository.save(club); + return club.getClubName(); } public String login(String email, String password) throws BaseException { - //email 없음 - Member selectedUser = memberRepository.findByEmail(email) + //email 중복확인 + Member member = memberRepository.findByEmail(email) .orElseThrow(() -> new BaseException(BaseResponseStatus.WRONG_EMAIL)); //비밀번호 틀림 - if (!encoder.matches(password, selectedUser.getPassword())) { + if (!bCryptPasswordEncoder.matches(password, member.getPassword())) { throw new BaseException(BaseResponseStatus.WRONG_PASSWORD); } - String token = JwtTokenUtil.createToken(selectedUser.getEmail(),selectedUser.getRole(), key, expireTimeMs); + String token = JwtTokenUtil.createToken(member.getEmail(), member.getRole(), + member.getName(), key, expireTimeMs); return token; } - public void changePassword(Long id, String password) { + public String loginKakao(OAuthLoginParams params) { + OAuthInfoResponse oAuthInfoResponse = requestOAuthInfoService.request(params); + Long memberId = findOrCreateMember(oAuthInfoResponse); + Member member = memberRepository.findById(memberId).orElseThrow(); + return JwtTokenUtil.createToken(member.getEmail(), member.getRole(), member.getName(), key, + expireTimeMs); + } - Member member = memberRepository.findById(id) - .orElseThrow(() -> new EntityNotFoundException("Member not found with id " + id)); - member.setPassword(password); - memberRepository.save(member); + private Long findOrCreateMember(OAuthInfoResponse oAuthInfoResponse) { + return memberRepository.findByEmail(oAuthInfoResponse.getEmail()) + .map(Member::getId) + .orElseGet(() -> newMember(oAuthInfoResponse)); + } + + private Long newMember(OAuthInfoResponse oAuthInfoResponse) { + Member member = new Member(); + member.setEmail(oAuthInfoResponse.getEmail()); + member.setName(oAuthInfoResponse.getNickname()); + member.setRole("USER"); + return memberRepository.save(member).getId(); } + + public GetMemberInfoResponse getInfo() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String email = authentication.getName(); + Member member = memberRepository.findByEmail(email) + .orElseThrow(() -> new BaseException(BaseResponseStatus.NO_SUCH_MEMBER_EXIST)); + GetMemberInfoResponse getMemberInfoResponse = new GetMemberInfoResponse(); + getMemberInfoResponse.setName(member.getName()); + getMemberInfoResponse.setGender(member.getGender()); + getMemberInfoResponse.setRole(member.getRole()); + getMemberInfoResponse.setEmail(member.getEmail()); + return getMemberInfoResponse; + } + + + public void changePassword(String email, String currentPassword, String newPassword) { + Member member = memberRepository.findByEmail(email) + .orElseThrow(() -> new BaseException(BaseResponseStatus.NO_SUCH_MEMBER_EXIST)); + if (bCryptPasswordEncoder.matches(currentPassword, member.getPassword())) { + // 비밀번호가 일치할 때 처리 + String hashedPassword = bCryptPasswordEncoder.encode(newPassword); + member.setPassword(hashedPassword); + //service 단 @Transactional 로 인해 save 호출이 필요없음. 즉, flush() 가 자동으로됨 + //memberRepository.save(member); + } else { + throw new BaseException(BaseResponseStatus.WRONG_PASSWORD); + } + } + + } diff --git a/src/main/java/com/likelion/dub/service/MypageService.java b/src/main/java/com/likelion/dub/service/MypageService.java deleted file mode 100644 index e270f3e..0000000 --- a/src/main/java/com/likelion/dub/service/MypageService.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.likelion.dub.service; - - -import com.likelion.dub.common.BaseException; -import com.likelion.dub.common.BaseResponseStatus; -import com.likelion.dub.domain.Member; -import com.likelion.dub.repository.MemberRepository; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.stereotype.Service; - -import java.util.Optional; - -@RequiredArgsConstructor -@Service -@Slf4j -public class MypageService { - private final MemberRepository memberRepository; - private final BCryptPasswordEncoder encoder; - - - public Member loadMemberByEmail(String email) throws BaseException { - Member selectedUser = memberRepository.findByEmail(email) - .orElseThrow(() -> new BaseException(BaseResponseStatus.WRONG_EMAIL)); - - return selectedUser; - - } - - public Member save(Member member) { - memberRepository.save(member); - return member; - - } - -} diff --git a/src/main/java/com/likelion/dub/service/PostService.java b/src/main/java/com/likelion/dub/service/PostService.java index 39f3f87..14c279d 100644 --- a/src/main/java/com/likelion/dub/service/PostService.java +++ b/src/main/java/com/likelion/dub/service/PostService.java @@ -1,96 +1,106 @@ package com.likelion.dub.service; -import com.fasterxml.jackson.databind.ser.Serializers; -import com.likelion.dub.common.BaseException; -import com.likelion.dub.common.BaseResponse; -import com.likelion.dub.common.BaseResponseStatus; + +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.likelion.dub.baseResponse.BaseException; +import com.likelion.dub.baseResponse.BaseResponseStatus; import com.likelion.dub.domain.Club; -import com.likelion.dub.domain.Image; import com.likelion.dub.domain.Member; import com.likelion.dub.domain.Post; -import com.likelion.dub.domain.dto.PostEditRequest; -import com.likelion.dub.exception.AppException; -import com.likelion.dub.exception.Errorcode; -import com.likelion.dub.repository.ImageRepository; +import com.likelion.dub.dto.Post.GetAllPostResponse; +import com.likelion.dub.dto.Post.GetOnePostResponse; import com.likelion.dub.repository.MemberRepository; import com.likelion.dub.repository.PostRepository; +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; -import java.util.List; -import java.util.Optional; - @Service +@Transactional @RequiredArgsConstructor +@Slf4j public class PostService { + private final PostRepository postRepository; - private final ImageRepository imageRepository; private final MemberRepository memberRepository; - private final FileHandler fileHandler; + private final AmazonS3Client amazonS3Client; + @Value("${cloud.aws.s3.bucket}") + private String bucket; - public List getAllClubs() { - return this.postRepository.findAll(); - } - public BaseResponse writePost(String title, String content,int category, List files) throws BaseException { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - //jwt token 오류 - if (authentication == null || !authentication.isAuthenticated()) { - return new BaseResponse(BaseResponseStatus.JWT_TOKEN_ERROR); - } - String email = authentication.getName(); - Member member = memberRepository.findByEmail(email).orElseThrow(); - Club club = member.getClub(); - String clubName = club.getClubName(); - - //이 club 글이 있으면 작성 불가 - postRepository.findByClubName(clubName) - .ifPresent(post -> { - throw new BaseException(BaseResponseStatus.USERS_EMPTY_USER_ID); - }); - - List imageList = fileHandler.parseFileInfo(files); - Post.PostBuilder postBuilder = Post.builder() - .clubName(clubName) - .title(title) - .content(content) - .category(category); - //Post post = Post.builder() - // .clubName(clubName) - // .image(imageList) - for (Image image : imageList) { - postBuilder.addImage(image); - } + public List getAllPost() { + + List allPosts = postRepository.findAll(); + // stream 사용 + List getAllPostResponses = allPosts.stream() + .map(post -> new GetAllPostResponse(post.getId(), post.getPostTitle(), post.getClub().getClubName(), + post.getClub().getClubImageUrl())) + .collect(Collectors.toList()); + return getAllPostResponses; + } - Post post = postBuilder.build(); + public void writing(String email, String title, String content, MultipartFile image) throws BaseException { - //파일이 존재할 때만 처리 - if(!imageList.isEmpty()){ - for(Image image : imageList){ - imageRepository.save(image); - post.addImage(image); - } + Member member = memberRepository.findByEmail(email) + .orElseThrow(() -> new BaseException(BaseResponseStatus.NO_SUCH_MEMBER_EXIST)); + Club club = member.getClub(); + if (club == null) { + throw new BaseException(BaseResponseStatus.NO_SUCH_CLUB_EXIST); + } + String clubName = club.getClubName(); + // post 객체 생성 및 db 에 저장 + Post post = new Post(); + post.setClub(club); + post.setPostTitle(title); + post.setContent(content); + try { + if (image != null) { + String fileName = clubName + "_" + "PostImage"; + // 포스터 사진 S3에 저장 + uploadPostImageToS3(fileName, image); + post.setPostImage("https://dubs3.s3.ap-northeast-2.amazonaws.com/" + fileName); } postRepository.save(post); - return new BaseResponse<>("글 작성 성공"); + + } catch (IOException e) { + throw new BaseException(BaseResponseStatus.FILE_SAVE_ERROR); + } + } - public Post readPost(Long id) throws BaseException{ - return postRepository.findById(id) - .orElseThrow(() -> - new BaseException(BaseResponseStatus.NOT_EXISTS_POST) - ); + private void uploadPostImageToS3(String fileName, MultipartFile file) throws IOException { + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentType(file.getContentType()); + metadata.setContentLength(file.getSize()); + amazonS3Client.putObject(bucket, fileName, file.getInputStream(), metadata); + } + + public GetOnePostResponse readPost(Long id) throws BaseException { + Post post = postRepository.findById(id) + .orElseThrow(() -> new BaseException(BaseResponseStatus.NOT_EXISTS_POST)); + GetOnePostResponse getOnePostResponse = new GetOnePostResponse(); + getOnePostResponse.setClubName(post.getClub().getClubName()); + getOnePostResponse.setTitle(post.getPostTitle()); + getOnePostResponse.setContent(post.getContent()); + getOnePostResponse.setPostImage(post.getPostImage()); + return getOnePostResponse; } - public BaseResponse deletePost(Long id) throws BaseException { + public void deletePost(Long id) throws BaseException { // 저장 되어있는 post 가져오기 Post post = postRepository.findById(id).orElseThrow(() -> new BaseException(BaseResponseStatus.NOT_EXISTS_POST)); @@ -99,7 +109,6 @@ public BaseResponse deletePost(Long id) throws BaseException { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); //jwt token 오류 if (authentication == null || !authentication.isAuthenticated()) { - return new BaseResponse(BaseResponseStatus.JWT_TOKEN_ERROR); } // 로그인 된 이메일 String email = authentication.getName(); @@ -107,43 +116,13 @@ public BaseResponse deletePost(Long id) throws BaseException { Member member = memberRepository.findByEmail(email).orElseThrow(); // 로그인 된 클럽 String clubName = member.getClub().getClubName(); - // 로그인 된 post - Post member_post = postRepository.findByClubName(clubName).orElseThrow(); - - //post id 가 같은지 검사, 같으면 삭제, 다르면 오류출력 - if (member_post.getId() == id) { - postRepository.deleteById(id); - return new BaseResponse<>("삭제 완료"); - } else { - return new BaseResponse(BaseResponseStatus.INVALID_MEMBER_JWT); - } - } - public void editPost(String email, String newTitle, String newContent, int newCategory, List newfiles) throws BaseException { - Member member = memberRepository.findByEmail(email) - .orElseThrow(() -> new BaseException(BaseResponseStatus.NO_SUCH_MEMBER_EXIST)); - String clubName = member.getClub().getClubName(); - Post post = postRepository.findByClubName(clubName) - .orElseThrow(()-> new BaseException(BaseResponseStatus.FAILED_GET_POST)); - List imageList = fileHandler.parseFileInfo(newfiles); - post.setTitle(newTitle); - post.setContent(newContent); - post.setCategory(newCategory); - post.setImage(imageList); - postRepository.save(post); } -// public Member loadMemberByEmail(String email) throws BaseException { -// Member selectedUser = postRepository.findByEmail(email) -// .orElseThrow(() -> new BaseException(BaseResponseStatus.WRONG_EMAIL)); -// -// return selectedUser; -// -// } +} - } diff --git a/src/main/java/com/likelion/dub/service/RequestOAuthInfoService.java b/src/main/java/com/likelion/dub/service/RequestOAuthInfoService.java new file mode 100644 index 0000000..65a08ea --- /dev/null +++ b/src/main/java/com/likelion/dub/service/RequestOAuthInfoService.java @@ -0,0 +1,29 @@ +package com.likelion.dub.service; + +import com.likelion.dub.dto.OAuth.OAuthApiClient; +import com.likelion.dub.dto.OAuth.OAuthInfoResponse; +import com.likelion.dub.dto.OAuth.OAuthLoginParams; +import com.likelion.dub.dto.OAuth.OAuthProvider; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import org.springframework.stereotype.Component; + +@Component +public class RequestOAuthInfoService { + + private final Map clients; + + public RequestOAuthInfoService(List clients) { + this.clients = clients.stream().collect( + Collectors.toUnmodifiableMap(OAuthApiClient::oAuthProvider, Function.identity()) + ); + } + + public OAuthInfoResponse request(OAuthLoginParams params) { + OAuthApiClient client = clients.get(params.oAuthProvider()); + String accessToken = client.requestAccessToken(params); + return client.requestOauthInfo(accessToken); + } +} \ No newline at end of file diff --git a/src/main/java/com/likelion/dub/utils/JwtTokenUtil.java b/src/main/java/com/likelion/dub/util/JwtTokenUtil.java similarity index 75% rename from src/main/java/com/likelion/dub/utils/JwtTokenUtil.java rename to src/main/java/com/likelion/dub/util/JwtTokenUtil.java index 4688b21..edbbb89 100644 --- a/src/main/java/com/likelion/dub/utils/JwtTokenUtil.java +++ b/src/main/java/com/likelion/dub/util/JwtTokenUtil.java @@ -1,10 +1,8 @@ -package com.likelion.dub.utils; +package com.likelion.dub.util; import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jwt; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; - import java.util.Date; public class JwtTokenUtil { @@ -19,20 +17,23 @@ public static String getEmail(String token, String secretKey) { .getBody().get("email", String.class); } + public static String getName(String token, String secretKey) { + return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token) + .getBody().get("name", String.class); + } - public static boolean isExpired(String token, String secretKey){ + + public static boolean isExpired(String token, String secretKey) { return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token) .getBody().getExpiration().before(new Date()); } - /** - * - JWT에서 claims는 사용자 정보를 담은 JSON 객체입니다. claims에는 고유한 ID, 발행자, 만료 시간, 사용자 이름 등이 포함될 수 있습니다. - */ - public static String createToken(String email,String role, String key, Long expireTimeMs) { + + public static String createToken(String email, String role, String name, String key, Long expireTimeMs) { Claims claims = Jwts.claims(); claims.put("email", email); claims.put("role", role); + claims.put("name", name); return Jwts.builder() .setClaims(claims) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 1396110..f4c01cf 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,13 +1,30 @@ -# MySQL ?? ?? -spring.datasource.url=jdbc:mysql://localhost:3306/new_schema +# MySQL +spring.datasource.url=jdbc:mysql://localhost:3306/tttt spring.datasource.username=root -spring.datasource.password=(120938yhok!) - spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver - -# Hibernate ?? +spring.datasource.password=whqkr44## +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +# Hibernate spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=create spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect - #jwt jwt.token.secret=VlwEyVBsYt9V7zq57TejMnVUyzblYcfPQye08f7MGVA9XkHa +#multipart +spring.servlet.multipart.enabled=true +spring.servlet.multipart.max-file-size=100MB +spring.servlet.multipart.max-request-size=100MB +#s3 +cloud.aws.s3.bucket=dubs3 +cloud.aws.stack.auto=false +cloud.aws.region.static=ap-northeast-2 +cloud.aws.credentials.accessKey=AKIAVNQICB3FCRQNW4PO +cloud.aws.credentials.secretKey=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 +# Kakao OAuth ?? +oauth.kakao.client-id=7f3162e5e7ea72180f4d8a7494e6f05c +oauth.kakao.url.auth=https://kauth.kakao.com +oauth.kakao.url.api=https://kapi.kakao.com +# Naver OAuth ?? +oauth.naver.secret=QmzgfY82j9 +oauth.naver.client-id=UCJ4dA_B8TDcKXUT3FJv +oauth.naver.url.auth=https://nid.naver.com +oauth.naver.url.api=https://openapi.naver.com \ No newline at end of file diff --git a/src/main/resources/static/image/kakao_login_medium_narrow.png b/src/main/resources/static/image/kakao_login_medium_narrow.png new file mode 100644 index 0000000..09bb358 Binary files /dev/null and b/src/main/resources/static/image/kakao_login_medium_narrow.png differ diff --git a/src/main/resources/templates/JoinView.html b/src/main/resources/templates/JoinView.html new file mode 100644 index 0000000..78e7a53 --- /dev/null +++ b/src/main/resources/templates/JoinView.html @@ -0,0 +1,34 @@ + + + + + 회원 가입 + + +
+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + + diff --git a/src/main/resources/templates/loginView.html b/src/main/resources/templates/loginView.html new file mode 100644 index 0000000..9fea1b5 --- /dev/null +++ b/src/main/resources/templates/loginView.html @@ -0,0 +1,14 @@ + + + + + My Spring-Thymeleaf Project + + + + + + + + + diff --git a/src/main/resources/templates/mainView.html b/src/main/resources/templates/mainView.html new file mode 100644 index 0000000..af07e40 --- /dev/null +++ b/src/main/resources/templates/mainView.html @@ -0,0 +1,40 @@ + + + + + + Title + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/com/likelion/dub/DubApplicationTests.java b/src/test/java/com/likelion/dub/DubApplicationTests.java index 89d5ba9..d1732ff 100644 --- a/src/test/java/com/likelion/dub/DubApplicationTests.java +++ b/src/test/java/com/likelion/dub/DubApplicationTests.java @@ -3,10 +3,10 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; -//@SpringBootTest +@SpringBootTest class DubApplicationTests { - //@Test + @Test void contextLoads() { } diff --git a/src/test/java/com/likelion/dub/controller/MemberControllerTest.java b/src/test/java/com/likelion/dub/controller/MemberControllerTest.java new file mode 100644 index 0000000..5a247b2 --- /dev/null +++ b/src/test/java/com/likelion/dub/controller/MemberControllerTest.java @@ -0,0 +1,53 @@ +package com.likelion.dub.controller; + + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.likelion.dub.dto.OAuth.KakaoLoginParams; +import jakarta.transaction.Transactional; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + + +@SpringBootTest +@AutoConfigureMockMvc +@Transactional +class MemberControllerTest { + + + @Autowired + protected MockMvc mvc; + @Autowired + protected ObjectMapper objectMapper; + + private static final String authorizationCode = "4lKRW_1z0BdjGGEQrYSoAZY_hUZt1BmoxebFeR44jqjww27vcmVgKZpCvik3odo2dmxWgwopyWAAAAGK19gfBQ"; + + @Test + public void 카카오_로그인_회원가입() throws Exception { + //given + KakaoLoginParams params = new KakaoLoginParams(authorizationCode); + //when + ResultActions resultActions = mvc.perform(post("/app/member/loginKakao") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(params)) + .accept(MediaType.APPLICATION_JSON)) + .andDo(print()); + + //then + resultActions.andExpect(status().isOk()).andExpect(jsonPath("$.is_Success").value(true)) + .andExpect(jsonPath("$.code").value(1000)); + + + } + + +} \ No newline at end of file diff --git a/src/test/java/com/likelion/dub/repository/ClubRepositoryTest.java b/src/test/java/com/likelion/dub/repository/ClubRepositoryTest.java new file mode 100644 index 0000000..ebd4099 --- /dev/null +++ b/src/test/java/com/likelion/dub/repository/ClubRepositoryTest.java @@ -0,0 +1,50 @@ +package com.likelion.dub.repository; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import com.likelion.dub.domain.Club; +import com.likelion.dub.domain.Member; +import java.util.Optional; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.jdbc.Sql.ExecutionPhase; +import org.springframework.test.context.jdbc.SqlGroup; + +@DataJpaTest(showSql = true) +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +@TestPropertySource("classpath:test-application.properties") +@SqlGroup({ + @Sql(value = "/sql/club-repository-test-data.sql", executionPhase = ExecutionPhase.BEFORE_TEST_METHOD) +}) +public class ClubRepositoryTest { + + @Autowired + private ClubRepository clubRepository; + + + @Test + public void findByClubName_으로_동아리를_가져올수있다() { + //given + //when + Optional club = clubRepository.findByClubName("멋쟁이사지"); + + //then + assertThat(club.isPresent()).isTrue(); + } + + @Test + public void findByClubName_으로_동아리장을_가져올수_있다() { + //given + //when + Optional club = clubRepository.findByClubName("멋쟁이사지"); + Optional member = Optional.ofNullable(club.get().getMember()); + //then + assertThat(member.isPresent()).isTrue(); + } + + +} \ No newline at end of file diff --git a/src/test/java/com/likelion/dub/repository/MemberRepositoryTest.java b/src/test/java/com/likelion/dub/repository/MemberRepositoryTest.java new file mode 100644 index 0000000..87425d7 --- /dev/null +++ b/src/test/java/com/likelion/dub/repository/MemberRepositoryTest.java @@ -0,0 +1,68 @@ +package com.likelion.dub.repository; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import com.likelion.dub.domain.Member; +import java.util.Optional; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.jdbc.Sql; + + +@DataJpaTest(showSql = true) +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +@TestPropertySource("classpath:test-application.properties") +@Sql("/sql/member-repository-test-data.sql") +public class MemberRepositoryTest { + + + @Autowired + private MemberRepository memberRepository; + + + @Test + void findById_로_멤버_찾아오기() { + //given + //when + Optional member = memberRepository.findById(1L); + + //then + assertThat(member.isPresent()).isTrue(); + + } + + @Test + void findById_는_데이터가_없으면_Optional_empty_를_반환한다() { + //given + //when + Optional member = memberRepository.findById(3L); + //then + assertThat(member.isEmpty()).isTrue(); + + } + + @Test + void findByEmail_로_멤버_데이터를_찾아올_수_있다() { + // given + // when + Optional member = memberRepository.findByEmail("suhoon@naver.com"); + + // then + assertThat(member.isPresent()).isTrue(); + } + + @Test + void findByEmail는_데이터가_없으면_Optional_empty_를_내려준다() { + // given + // when + Optional member = memberRepository.findByEmail("fafafafa@naver.com"); + + // then + assertThat(member.isEmpty()).isTrue(); + } + + +} \ No newline at end of file diff --git a/src/test/java/com/likelion/dub/service/MemberServiceTest.java b/src/test/java/com/likelion/dub/service/MemberServiceTest.java new file mode 100644 index 0000000..ef6dd06 --- /dev/null +++ b/src/test/java/com/likelion/dub/service/MemberServiceTest.java @@ -0,0 +1,122 @@ +package com.likelion.dub.service; + +import static org.mockito.ArgumentMatchers.any; + +import com.likelion.dub.dto.Member.MemberJoinRequest; +import com.likelion.dub.dto.Member.ToClubRequest; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.BDDMockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.jdbc.Sql.ExecutionPhase; +import org.springframework.test.context.jdbc.SqlGroup; +import org.springframework.transaction.annotation.Transactional; + + +@SpringBootTest +@Transactional +@TestPropertySource("classpath:test-application.properties") +@SqlGroup({ + @Sql(value = "/sql/member-service-test-data.sql", executionPhase = ExecutionPhase.BEFORE_TEST_METHOD), +}) +public class MemberServiceTest { + + @Autowired + private MemberService memberService; + @MockBean + private BCryptPasswordEncoder bCryptPasswordEncoder; + + + @Test + void checkEmail_로_이메일_중복체크를할수있다() { + //given + String email = "new_email"; + //when + boolean checkEmail = memberService.checkEmail(email); + //then + Assertions.assertThat(checkEmail).isTrue(); + } + + + @Test + void join_으로_회원가입을할수있다() { + //given + String email = "suhoon@naver.com"; + String name = "조수훈"; + String password = "124"; + String gender = "남자"; + String role = "CLUB"; + MemberJoinRequest memberJoinRequest = new MemberJoinRequest(email, name, password, gender, role); + //when + String memberName = memberService.join(memberJoinRequest); + //then + Assertions.assertThat(memberName).isEqualTo("조수훈"); + } + + @Test + void transferToClub_으로동아리전환을할수있다() { + //given + String email = "suhoon@naver.com"; + String club_name = "UMC"; + String introduction = "안녕하세요UMC입니다"; + String group_name = "코딩동아리"; + String club_image_url = "naver.com"; + String question1 = "지원동기??"; + ToClubRequest toClubRequest = new ToClubRequest(); + toClubRequest.setClubName(club_name); + toClubRequest.setIntroduction(introduction); + toClubRequest.setGroup(group_name); + toClubRequest.setClubImageUrl(club_image_url); + toClubRequest.setQuestion1(question1); + //when + String clubName = memberService.transferToClub(email, toClubRequest); + //then + Assertions.assertThat(clubName).isEqualTo("UMC"); + + } + + @Test + void login_으로_로그인할수가있다() { + //given + String email = "suhoon@naver.com"; + String password = "1234"; + BDDMockito.given(bCryptPasswordEncoder.matches(any(), any())).willReturn(true); + //when + String token = memberService.login(email, password); + + //then + Assertions.assertThat(token).isNotEmpty(); + } + + @Test + void loginKakao() { + //given + + //when + + //then + } + + @Test + void getInfo() { + //given + + //when + + //then + } + + @Test + void changePassword() { + //given + + //when + + //then + } +} \ No newline at end of file diff --git a/src/test/resources/sql/club-repository-test-data.sql b/src/test/resources/sql/club-repository-test-data.sql new file mode 100644 index 0000000..8d61f7a --- /dev/null +++ b/src/test/resources/sql/club-repository-test-data.sql @@ -0,0 +1,13 @@ +-- Inserting data into the "member" table +INSERT INTO `member` (`member_id`, `email`, `gender`, `name`, `password`, `role`) +VALUES (1, 'suhoon@naver.com', '남자', '조수훈', '1234', 'USER'); + +INSERT INTO `member` (`member_id`, `email`, `gender`, `name`, `password`, `role`) +VALUES (2, 'sohoon@naver.com', '여자', '조소훈', '1234', 'USER'); + +-- Inserting data into the "club" table +INSERT INTO `club` (`club_id`, `club_image_url`, `club_name`, `group_name`, `introduction`, `question1`, `member_id`) +VALUES (1, 'naver.com', '멋쟁이사지', '코딩동아리', '안녕하세요!!멋재이사자입니다.', '지원동기??', 1); + +INSERT INTO `club` (`club_id`, `club_image_url`, `club_name`, `group_name`, `introduction`, `question1`, `member_id`) +VALUES (2, 'naver.com', 'UMC','코딩동아리2', '안녕하세요!!UMC입니다.', '지원동기??', 2); diff --git a/src/test/resources/sql/member-repository-test-data.sql b/src/test/resources/sql/member-repository-test-data.sql new file mode 100644 index 0000000..72bffe9 --- /dev/null +++ b/src/test/resources/sql/member-repository-test-data.sql @@ -0,0 +1,4 @@ +insert into `member` (`member_id`, `email`, `gender`, `name`, `password`, `role`) +values (1, 'suhoon@naver.com','남자','조수훈','1234','USER'); +insert into `member` (`member_id`, `email`, `gender`, `name`, `password`, `role`) +values (2, 'sohoon@naver.com','여자','조소훈','1234','USER'); \ No newline at end of file diff --git a/src/test/resources/sql/member-service-test-data.sql b/src/test/resources/sql/member-service-test-data.sql new file mode 100644 index 0000000..72bffe9 --- /dev/null +++ b/src/test/resources/sql/member-service-test-data.sql @@ -0,0 +1,4 @@ +insert into `member` (`member_id`, `email`, `gender`, `name`, `password`, `role`) +values (1, 'suhoon@naver.com','남자','조수훈','1234','USER'); +insert into `member` (`member_id`, `email`, `gender`, `name`, `password`, `role`) +values (2, 'sohoon@naver.com','여자','조소훈','1234','USER'); \ No newline at end of file diff --git a/src/test/resources/test-application.properties b/src/test/resources/test-application.properties new file mode 100644 index 0000000..f4c01cf --- /dev/null +++ b/src/test/resources/test-application.properties @@ -0,0 +1,30 @@ +# MySQL +spring.datasource.url=jdbc:mysql://localhost:3306/tttt +spring.datasource.username=root +spring.datasource.password=whqkr44## +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +# Hibernate +spring.jpa.show-sql=true +spring.jpa.hibernate.ddl-auto=create +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect +#jwt +jwt.token.secret=VlwEyVBsYt9V7zq57TejMnVUyzblYcfPQye08f7MGVA9XkHa +#multipart +spring.servlet.multipart.enabled=true +spring.servlet.multipart.max-file-size=100MB +spring.servlet.multipart.max-request-size=100MB +#s3 +cloud.aws.s3.bucket=dubs3 +cloud.aws.stack.auto=false +cloud.aws.region.static=ap-northeast-2 +cloud.aws.credentials.accessKey=AKIAVNQICB3FCRQNW4PO +cloud.aws.credentials.secretKey=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 +# Kakao OAuth ?? +oauth.kakao.client-id=7f3162e5e7ea72180f4d8a7494e6f05c +oauth.kakao.url.auth=https://kauth.kakao.com +oauth.kakao.url.api=https://kapi.kakao.com +# Naver OAuth ?? +oauth.naver.secret=QmzgfY82j9 +oauth.naver.client-id=UCJ4dA_B8TDcKXUT3FJv +oauth.naver.url.auth=https://nid.naver.com +oauth.naver.url.api=https://openapi.naver.com \ No newline at end of file