diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..59affc0 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,148 @@ +name: CI/CD Pipeline + +on: + push: + branches: [ week6 ] + pull_request: + branches: [ week6 ] + +env: + LIGHTSAIL_USERNAME: ubuntu + AWS_REGION: ap-northeast-2 + +jobs: + build: + runs-on: ubuntu-22.04 + + steps: + # 브런치로 체크아웃 + - name: Checkout code + uses: actions/checkout@v3 + + # jdk 설치 + - name: Set up JDK 21 + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '21' + + # application-oauth.yml 생성 + - name: Create application.yml + run: | + mkdir -p src/main/resources + cat < src/main/resources/application-oauth.yml + spring: + datasource: + url: ${{ + secrets.SPRING_DATASOURCE_URL + }} + username: ${{ + secrets.SPRING_DATASOURCE_USERNAME + }} + password: ${{ + secrets.SPRING_DATASOURCE_PASSWORD + }} + security: + oauth2: + client: + registration: + google: + client-id: ${{ + secrets.GOOGLE_CLIENT_ID + }} + client-secret: ${{ + secrets.GOOGLE_CLIENT_SECRET + }} + redirect-uri: "https://${{ + secrets.LIGHT_SAIL_IP + }}/login/oauth2/code/google" + scope: + - email + - profile + provider: + google: + authorization-uri: https://accounts.google.com/o/oauth2/auth + token-uri: https://oauth2.googleapis.com/token + user-info-uri: https://www.googleapis.com/oauth2/v3/userinfo + server: + port: 443 + ssl: + enabled: true + key-store: /etc/letsencrypt/live/seamlessup.com/keystore.p12 + key-store-password: ${{ + secrets.KEYSTORE_PASSWORD + }} + key-store-type: PKCS12 + key-alias: tomcat + EOF + + # Gradle 캐시 설정 + - name: Cache Gradle packages + uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + # gradlew 실행 권한 부여 + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + # gradle로 빌드 + - name: Build with Gradle + run: ./gradlew bootJar + + # AWS 인증 + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + aws-region: ${{ env.AWS_REGION }} + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + + # AWS 인증 테스트 + - name: Verify AWS Credentials + run: aws configure list + + # 배포 자동화 + - name: Upload files to Lightsail + uses: appleboy/scp-action@master + with: + host: ${{ secrets.LIGHTSAIL_HOST }} + username: ${{ env.LIGHTSAIL_USERNAME }} + port: 22 + key: ${{ secrets.LIGHTSAIL_SSH_KEY }} + source: 'build/libs/*.jar' + target: '/home/ubuntu/seamless/dist' + use_insecure_cipher: true + + - name: Restart Spring Boot process + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.LIGHTSAIL_HOST }} + username: ${{env.LIGHTSAIL_USERNAME}} + key: ${{ secrets.LIGHTSAIL_SSH_KEY }} + script: | + BUILD_PATH=$(ls /home/ubuntu/seamless/dist/build/libs/*.jar) + JAR_NAME=$(basename $BUILD_PATH) + + CURRENT_PID=$(pgrep -f $JAR_NAME) + + if [ -z $CURRENT_PID ] + then + sleep 1 + else + kill -15 $CURRENT_PID + sleep 5 + fi + + DEPLOY_PATH=/home/ubuntu/seamless/deploy/ + mkdir $DEPLOY_PATH + cp $BUILD_PATH $DEPLOY_PATH + cd $DEPLOY_PATH + + DEPLOY_JAR=$DEPLOY_PATH$JAR_NAME + nohup java -jar $DEPLOY_JAR > /dev/null 2> /dev/null < /dev/null & diff --git a/README.md b/README.md index 4460173..63be885 100644 --- a/README.md +++ b/README.md @@ -63,12 +63,34 @@ - 다른 방법 찾는중(w5) - 프로젝트(김도헌) - 프로젝트 리스트 조회 + - 토큰 내 유저의 email 정보를 통해 본인이 관리하는 프로젝트들을 페이지네이션을 통해 보여줌 - 프로젝트 기간 리스트 조회 + - 토큰 내 유저의 email 정보를 통해 본인이 관리하는 프로젝트들의 일정 정보를 페이지네이션을 통해 보여줌 - 프로젝트 조회 + - 프로젝트 Id를 통한 단일 조회 - 프로젝트 멤버 조회 + - 프로젝트 Id를 통해 해당 프로젝트의 멤버들의 정보를 조회 + - 현재 객체를 통해 정보를 받아 오는 것으로 되어있지만, 추후 fetch join을 활용한 방법으로 수정 예정 - 프로젝트 생성 + - 프로젝트를 생성함 + - 플로우 : + - email을 통해 유저가 존재하는 지 검증 -> + - DTO에 담긴 optionEntity들의 id 정보들을 통해 OptionEntity조회 -> + - OptionEntity을 ProjectOption으로 매핑 -> + - 해당 정보를 가진 ProjectEntity를 생성 후 Repo에 save -> + - 각 ProjectOption의 ProjectEntity field를 생성한 ProjectEntity로 설정 - 프로젝트 설정 수정 + - 해당하는 Id의 프로젝트를 수정 + - 플로우 : + - 프로젝트가 존재하는지 검증 -> + - 기존의 ProjectOption 리스트 초기화-> + - DTO에 담긴 Option id들을 통해 OptionEntity 조회 -> + - OptionEntity를 ProjectOption으로 매핑 -> + - ProjectOption 리스트에 추가 -> + - 나머지 정보 업데이트 후 저장 - 프로젝트 삭제 + - 해당하는 Id의 프로젝트 삭제 + - 현재는 그냥 삭제를 하지만, 추후 Soft Delete 방식으로 재구현 예정 - 게스트(권순호) - 게스트 생성 - 게스트 수정 @@ -99,9 +121,18 @@ - [3주차 피드백](https://github.com/kakao-tech-campus-2nd-step3/Team1_BE/pull/11) - [4주차 리뷰](https://github.com/kakao-tech-campus-2nd-step3/Team1_BE/issues/17) - [4주차 멘토링](https://quickest-asterisk-75d.notion.site/Back-end_-323b0e20ae2b405189ffe5b7c4242e00) +- [5주차 리뷰](https://github.com/kakao-tech-campus-2nd-step3/Team1_BE/issues/31) --- # Issue - [week4 프로젝트 빌드 실패](https://github.com/kakao-tech-campus-2nd-step3/Team1_BE/issues/27) +- [week6 프로젝트 빌드 실패](https://github.com/kakao-tech-campus-2nd-step3/Team1_BE/issues/39) +- [week6 구글 로그인 실패](https://github.com/kakao-tech-campus-2nd-step3/Team1_BE/issues/41) +- [week6 멤버 생성 실패](https://github.com/kakao-tech-campus-2nd-step3/Team1_BE/issues/42) +- [week6 pathvaliable로 값 파싱 실패](https://github.com/kakao-tech-campus-2nd-step3/Team1_BE/issues/43) +- [week6 task 날짜 validation 오류](https://github.com/kakao-tech-campus-2nd-step3/Team1_BE/issues/44) +- [week6 aws server 구글 로그인 실패](https://github.com/kakao-tech-campus-2nd-step3/Team1_BE/issues/45) +- [week6 멤버 생성 권한 수정](https://github.com/kakao-tech-campus-2nd-step3/Team1_BE/issues/46) +- [week6 task 조회 쿼리 문법 오류](https://github.com/kakao-tech-campus-2nd-step3/Team1_BE/issues/47) --- # 질문사항 - week3 @@ -116,4 +147,7 @@ - 메일 발송 방법 - 코드 스타일 - 동시성 처리 -- week5 \ No newline at end of file +- week5 + - softDelete의 구현 방법 + - 연결괸 객체의 정보를 가져오는 방법 +- week6 \ No newline at end of file diff --git a/build.gradle b/build.gradle index 5c34d9c..90ef958 100644 --- a/build.gradle +++ b/build.gradle @@ -38,6 +38,7 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' // testImplementation 'org.springframework.security:spring-security-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + runtimeOnly 'com.h2database:h2:1.4.200' } tasks.named('test') { diff --git a/src/main/java/team1/BE/seamless/DTO/MemberResponseDTO.java b/src/main/java/team1/BE/seamless/DTO/MemberResponseDTO.java index 2bb798a..4268dd1 100644 --- a/src/main/java/team1/BE/seamless/DTO/MemberResponseDTO.java +++ b/src/main/java/team1/BE/seamless/DTO/MemberResponseDTO.java @@ -4,8 +4,41 @@ public class MemberResponseDTO { private String message; - public MemberResponseDTO(String message) { + private String name; + + private String role; + + private String email; + + public MemberResponseDTO(String message, String name, String role, String email) { this.message = message; + this.name = name; + this.role = role; + this.email = email; + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; } public String getMessage() { diff --git a/src/main/java/team1/BE/seamless/DTO/OptionDTO.java b/src/main/java/team1/BE/seamless/DTO/OptionDTO.java index 4f0b6ac..59a542a 100644 --- a/src/main/java/team1/BE/seamless/DTO/OptionDTO.java +++ b/src/main/java/team1/BE/seamless/DTO/OptionDTO.java @@ -1,28 +1,37 @@ package team1.BE.seamless.DTO; +import team1.BE.seamless.entity.enums.OptionType; + public class OptionDTO { public static class OptionCreate { private String name; - private String eventType; + private String description; + + private String optionType; public OptionCreate() { } - public OptionCreate(String name, String eventType) { + public OptionCreate(String name, String description, String optionType) { this.name = name; - this.eventType = eventType; + this.description = description; + this.optionType = optionType; } public String getName() { return name; } - public String getEventType() { - return eventType; + public String getDescription() { + return description; + } + + public String getOptionType() { + return optionType; } } diff --git a/src/main/java/team1/BE/seamless/DTO/ProjectDTO.java b/src/main/java/team1/BE/seamless/DTO/ProjectDTO.java index 209ec93..179eb4c 100644 --- a/src/main/java/team1/BE/seamless/DTO/ProjectDTO.java +++ b/src/main/java/team1/BE/seamless/DTO/ProjectDTO.java @@ -1,8 +1,14 @@ package team1.BE.seamless.DTO; +import jakarta.validation.Valid; import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.Digits; import jakarta.validation.constraints.FutureOrPresent; +import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.Size; import java.time.Duration; import java.time.LocalDateTime; import java.util.ArrayList; @@ -17,29 +23,30 @@ public static class getList extends PageParam { public static class ProjectCreate { + @NotBlank(message = "이름은 필수 입력 사항입니다.") + @Size(max = 15, message = "이름은 공백 포함 최대 15글자까지 가능합니다.") private String name; - private Long userId; - - private List optionIds; + @NotNull + @Valid + private List<@Positive Long> optionIds = new ArrayList<>(); @NotNull - @FutureOrPresent + @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$") private LocalDateTime startDate; @NotNull + @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$") private LocalDateTime endDate; public ProjectCreate() { - this.optionIds = new ArrayList<>(); } - public ProjectCreate(String name, Long userId, + public ProjectCreate(String name, LocalDateTime startDate, LocalDateTime endDate, List optionIds) { this.name = name; - this.userId = userId; this.startDate = startDate; this.endDate = endDate; this.optionIds = optionIds; @@ -49,10 +56,6 @@ public String getName() { return name; } - public Long getUserId() { - return userId; - } - public List getOptionIds() { return optionIds; } @@ -79,15 +82,18 @@ public boolean isAtLeastOneDayDifference() { public static class ProjectUpdate { + @NotBlank(message = "이름은 필수 입력 사항입니다.") + @Size(max = 15, message = "이름은 공백 포함 최대 15글자까지 가능합니다.") private String name; - private List optionIds; - @NotNull - @FutureOrPresent + @Valid + private List<@Positive Long> optionIds = new ArrayList<>(); + + @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$") private LocalDateTime startDate; - @NotNull + @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$") private LocalDateTime endDate; public ProjectUpdate() { @@ -135,17 +141,15 @@ public static class ProjectPeriod { private String name; - @NotNull - @FutureOrPresent private LocalDateTime startDate; - @NotNull private LocalDateTime endDate; public ProjectPeriod() { } - public ProjectPeriod(String name, LocalDateTime startDate, LocalDateTime endDate) { + public ProjectPeriod(Long id, String name, LocalDateTime startDate, LocalDateTime endDate) { + this.id = id; this.name = name; this.startDate = startDate; this.endDate = endDate; @@ -167,16 +171,52 @@ public LocalDateTime getEndDate() { return endDate; } - @AssertTrue - public boolean isEndDateAfterStartDate() { - return endDate.isAfter(startDate); + } + + public static class ProjectDetail{ + private Long id; + + private String name; + + private LocalDateTime startDate; + + private LocalDateTime endDate; + + private List optionIds; + + public ProjectDetail() { } - @AssertTrue - public boolean isAtLeastOneDayDifference() { - return Duration.between(startDate, endDate).toDays() >= 1; + public ProjectDetail(Long id, String name, + LocalDateTime startDate, + LocalDateTime endDate, + List optionIds) { + this.id = id; + this.name = name; + this.startDate = startDate; + this.endDate = endDate; + this.optionIds = optionIds; } + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public LocalDateTime getStartDate() { + return startDate; + } + + public LocalDateTime getEndDate() { + return endDate; + } + + public List getOptionIds() { + return optionIds; + } } } diff --git a/src/main/java/team1/BE/seamless/DTO/TaskDTO.java b/src/main/java/team1/BE/seamless/DTO/TaskDTO.java index 28a1933..c010de9 100644 --- a/src/main/java/team1/BE/seamless/DTO/TaskDTO.java +++ b/src/main/java/team1/BE/seamless/DTO/TaskDTO.java @@ -30,7 +30,7 @@ public static class Create { public Create(String name, String remark, Long memberId, LocalDateTime startDate, LocalDateTime endDate) { - if (startDate.isBefore(endDate)) { + if (endDate.isBefore(startDate)) { throw new BaseHandler(HttpStatus.FORBIDDEN, "종료시간은 시작시간보다 이전일 수 없습니다."); } this.name = name; @@ -78,6 +78,9 @@ public static class Update { public Update(String name, String remark, Integer progress, Long memberId, LocalDateTime startDate, LocalDateTime endDate) { + if (endDate.isBefore(startDate)) { + throw new BaseHandler(HttpStatus.FORBIDDEN, "종료시간은 시작시간보다 이전일 수 없습니다."); + } this.name = name; this.remark = remark; this.progress = progress; @@ -110,4 +113,59 @@ public LocalDateTime getEndDate() { return endDate; } } + + public static class TaskDetail { + private Long id; + + private String name; + + private String remark; + + private Long memberId; + + private Integer progress; + + private LocalDateTime startDate; + + private LocalDateTime endDate; + + public TaskDetail(Long id, String name, String remark, Long memberId, Integer progress, + LocalDateTime startDate, LocalDateTime endDate) { + this.id = id; + this.name = name; + this.remark = remark; + this.memberId = memberId; + this.progress = progress; + this.startDate = startDate; + this.endDate = endDate; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getRemark() { + return remark; + } + + public Long getMemberId() { + return memberId; + } + + public Integer getProgress() { + return progress; + } + + public LocalDateTime getStartDate() { + return startDate; + } + + public LocalDateTime getEndDate() { + return endDate; + } + } } diff --git a/src/main/java/team1/BE/seamless/controller/AttendUrlController.java b/src/main/java/team1/BE/seamless/controller/AttendUrlController.java index bc4e9a7..0d21292 100644 --- a/src/main/java/team1/BE/seamless/controller/AttendUrlController.java +++ b/src/main/java/team1/BE/seamless/controller/AttendUrlController.java @@ -39,9 +39,10 @@ public AttendUrlController(AttendURLService attendURLService) { * less) */ @Operation(summary = "팀원초대 링크 생성") - @PostMapping("/api/project/{project_id}/invite-link") + @PostMapping("/api/project/{project_id}/invite-link/{user-id}") public SingleResult generateInviteLink(HttpServletRequest req, - @Valid @PathVariable("project_id") Long projectId) { - return new SingleResult<>(attendURLService.generateAttendURL(req, projectId)); + @Valid @PathVariable("project_id") Long projectId, + @Valid @PathVariable("user-id") Long userId) { + return new SingleResult<>(attendURLService.generateAttendURL(req, projectId, userId)); } } \ No newline at end of file diff --git a/src/main/java/team1/BE/seamless/controller/MemberController.java b/src/main/java/team1/BE/seamless/controller/MemberController.java index 1cdf351..a0099ab 100644 --- a/src/main/java/team1/BE/seamless/controller/MemberController.java +++ b/src/main/java/team1/BE/seamless/controller/MemberController.java @@ -2,6 +2,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; @@ -13,12 +14,15 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import team1.BE.seamless.DTO.MemberRequestDTO; +import team1.BE.seamless.DTO.MemberResponseDTO; import team1.BE.seamless.entity.MemberEntity; import team1.BE.seamless.service.MemberService; import team1.BE.seamless.util.page.PageMapper; import team1.BE.seamless.util.page.PageResult; import team1.BE.seamless.util.page.SingleResult; +import java.lang.reflect.Member; + @Tag(name = "팀원 관리") @RequestMapping("/api/project/{project_id}/member") @@ -34,43 +38,48 @@ public class MemberController { @Operation(summary = "팀원 개별 조회") @GetMapping("/{member_id}") - public SingleResult getMember(@Valid @PathVariable("project_id") Long projectId, - @Valid @PathVariable("member_id") Long memberId) { - return new SingleResult<>(memberService.getMember(projectId, memberId)); + public SingleResult getMember(@Valid @PathVariable("project_id") Long projectId, + @Valid @PathVariable("member_id") Long memberId, + HttpServletRequest req) { + return new SingleResult<>(memberService.getMember(projectId, memberId, req)); } @Operation(summary = "팀원 전체 조회") @GetMapping public PageResult getMemberList(@Valid @PathVariable("project_id") Long projectId, - @Valid MemberRequestDTO.getMemberList memberListRequestDTO) { + @Valid MemberRequestDTO.getMemberList memberListRequestDTO, + HttpServletRequest req) { return PageMapper.toPageResult( - memberService.getMemberList(projectId, memberListRequestDTO)); + memberService.getMemberList(projectId, memberListRequestDTO, req)); } @Operation(summary = "새 팀원 추가") @PostMapping - public SingleResult createMember( + public SingleResult createMember( @PathVariable("project_id") Long projectId, - @Valid @RequestBody MemberRequestDTO.CreateMember Create) { - return new SingleResult<>(memberService.createMember(projectId, Create)); + @Valid @RequestBody MemberRequestDTO.CreateMember Create, + HttpServletRequest req) { + return new SingleResult<>(memberService.createMember(projectId, Create, req)); } @Operation(summary = "팀원 정보 수정") @PutMapping("/{member_id}") - public SingleResult updateMember( + public SingleResult updateMember( @PathVariable("project_id") Long projectId, @PathVariable("member_id") Long memberId - , @RequestBody MemberRequestDTO.UpdateMember update) { - return new SingleResult<>(memberService.updateMember(projectId, memberId, update)); + , @RequestBody MemberRequestDTO.UpdateMember update, + HttpServletRequest req) { + return new SingleResult<>(memberService.updateMember(projectId, memberId, update, req)); } @Operation(summary = "팀원 삭제") @DeleteMapping("/{member_id}") - public SingleResult deleteMember( + public SingleResult deleteMember( @PathVariable("project_id") Long projectId, - @PathVariable("member_id") Long memberId) { + @PathVariable("member_id") Long memberId, + HttpServletRequest req) { - return new SingleResult<>(memberService.deleteMember(projectId, memberId)); + return new SingleResult<>(memberService.deleteMember(projectId, memberId, req)); } } diff --git a/src/main/java/team1/BE/seamless/controller/ProjectController.java b/src/main/java/team1/BE/seamless/controller/ProjectController.java index 5964203..214124b 100644 --- a/src/main/java/team1/BE/seamless/controller/ProjectController.java +++ b/src/main/java/team1/BE/seamless/controller/ProjectController.java @@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import team1.BE.seamless.DTO.ProjectDTO; +import team1.BE.seamless.DTO.ProjectDTO.ProjectDetail; import team1.BE.seamless.DTO.ProjectDTO.ProjectPeriod; import team1.BE.seamless.entity.MemberEntity; import team1.BE.seamless.entity.ProjectEntity; @@ -40,15 +41,14 @@ public ProjectController(ProjectService projectService, ParsingPram parsingPram) @Operation(summary = "프로젝트 리스트 조회") @GetMapping - public PageResult getProjectList(@Valid ProjectDTO.getList param, + public PageResult getProjectList(@Valid ProjectDTO.getList param, HttpServletRequest req) { - return PageMapper.toPageResult( - projectService.getProjectList(param, parsingPram.getEmail(req))); + return PageMapper.toPageResult(projectService.getProjectList(param, parsingPram.getEmail(req))); } @Operation(summary = "프로젝트 조회") @GetMapping("/{project-id}") - public SingleResult getProject(@Valid @PathVariable long id) { + public SingleResult getProject(@Valid @PathVariable("project-id") Long id) { return new SingleResult<>(projectService.getProject(id)); } @@ -62,28 +62,28 @@ public PageResult getProjectPeriod(@Valid ProjectDTO.getList para @Operation(summary = "프로젝트 멤버 조회") @GetMapping("/{project-id}/members") - public ListResult getProjectMembers(@Valid @PathVariable long id) { + public ListResult getProjectMembers(@Valid @PathVariable("project-id") Long id) { return new ListResult<>(projectService.getProjectMembers(id)); } @Operation(summary = "프로젝트 생성") @PostMapping - public SingleResult createProject( + public SingleResult createProject( @Valid @RequestBody ProjectDTO.ProjectCreate create, HttpServletRequest req) { return new SingleResult<>(projectService.createProject(create, parsingPram.getEmail(req))); } @Operation(summary = "프로젝트 설정 수정") @PutMapping("/{project-id}") - public SingleResult updateProject( + public SingleResult updateProject( @Valid @RequestBody ProjectDTO.ProjectUpdate update, - @PathVariable long id) { + @PathVariable("project-id") Long id) { return new SingleResult<>(projectService.updateProject(id, update)); } @Operation(summary = "프로젝트 삭제") @DeleteMapping("/{project-id}") - public SingleResult deleteProject(@Valid @PathVariable long id) { + public SingleResult deleteProject(@Valid @PathVariable("project-id") Long id) { return new SingleResult<>(projectService.deleteProject(id)); } diff --git a/src/main/java/team1/BE/seamless/controller/TaskController.java b/src/main/java/team1/BE/seamless/controller/TaskController.java index 409a853..bd80f71 100644 --- a/src/main/java/team1/BE/seamless/controller/TaskController.java +++ b/src/main/java/team1/BE/seamless/controller/TaskController.java @@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import team1.BE.seamless.DTO.TaskDTO; +import team1.BE.seamless.DTO.TaskDTO.TaskDetail; import team1.BE.seamless.entity.TaskEntity; import team1.BE.seamless.service.TaskService; import team1.BE.seamless.util.page.PageMapper; @@ -34,13 +35,13 @@ public TaskController(TaskService taskService) { @Operation(summary = "태스크 단건 조회") @GetMapping("/task/{taskId}") - public SingleResult getTask(@PathVariable Long taskId) { + public SingleResult getTask(@PathVariable Long taskId) { return new SingleResult<>(taskService.getTask(taskId)); } @Operation(summary = "프로젝트 아이디로 태스크 리스트 조회 ") @GetMapping("/{projectId}/task") - public PageResult getTaskList(@PathVariable Long projectId, + public PageResult getTaskList(@PathVariable Long projectId, @Valid TaskDTO.getList param) { return PageMapper.toPageResult(taskService.getTaskList(projectId, param)); } @@ -50,7 +51,7 @@ public PageResult getTaskList(@PathVariable Long projectId, */ @Operation(summary = "태스크 생성") @PostMapping("/{projectId}/task") - public SingleResult createTask(HttpServletRequest req, + public SingleResult createTask(HttpServletRequest req, @Valid @PathVariable Long projectId, @Valid @RequestBody TaskDTO.Create create) { return new SingleResult<>(taskService.createTask(req, projectId, create)); } @@ -60,7 +61,7 @@ public SingleResult createTask(HttpServletRequest req, */ @Operation(summary = "태스크 수정") @PutMapping("/task/{taskId}") - public SingleResult updateTask(HttpServletRequest req, + public SingleResult updateTask(HttpServletRequest req, @Valid @PathVariable Long taskId, @Valid @RequestBody TaskDTO.Update update) { return new SingleResult<>(taskService.updateTask(req, taskId, update)); diff --git a/src/main/java/team1/BE/seamless/controller/TestController.java b/src/main/java/team1/BE/seamless/controller/TestController.java index d28158b..07e0be0 100644 --- a/src/main/java/team1/BE/seamless/controller/TestController.java +++ b/src/main/java/team1/BE/seamless/controller/TestController.java @@ -9,10 +9,13 @@ 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.RequestParam; import org.springframework.web.bind.annotation.RestController; import team1.BE.seamless.DTO.TestDTO; import team1.BE.seamless.entity.TestEntity; +import team1.BE.seamless.mapper.TestMapper; import team1.BE.seamless.service.TestService; +import team1.BE.seamless.util.auth.Token; import team1.BE.seamless.util.page.PageMapper; import team1.BE.seamless.util.page.PageResult; import team1.BE.seamless.util.page.SingleResult; @@ -46,4 +49,10 @@ public SingleResult getTest(@Valid @PathVariable long id) { public SingleResult createTest(@Valid @RequestBody TestDTO.create create) { return new SingleResult<>(testService.createTest(create)); } + + @Operation(summary = "테스트용 토큰 생성", description = "id1번 유저의 토큰 생성(테스트 외 사용금지)") + @PostMapping("/userToken/{userId}") + public SingleResult memberCodeCreate(@Valid @PathVariable long userId) { + return new SingleResult<>(testService.TestTokenCreate(userId)); + } } diff --git a/src/main/java/team1/BE/seamless/entity/OptionEntity.java b/src/main/java/team1/BE/seamless/entity/OptionEntity.java index 3b44126..f822604 100644 --- a/src/main/java/team1/BE/seamless/entity/OptionEntity.java +++ b/src/main/java/team1/BE/seamless/entity/OptionEntity.java @@ -3,11 +3,14 @@ import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.OneToMany; import java.util.List; +import team1.BE.seamless.entity.enums.OptionType; @Entity(name = "option") public class OptionEntity extends BaseEntity { @@ -16,9 +19,10 @@ public OptionEntity() { } - public OptionEntity(String name, String eventType) { + public OptionEntity(String name, String description, OptionType optionType) { this.name = name; - this.eventType = eventType; + this.description = description; + this.optionType = optionType; this.isDeleted = false; } @@ -30,8 +34,12 @@ public OptionEntity(String name, String eventType) { @Column(name = "name") private String name; - @Column(name = "event_type") - private String eventType; + @Column(name = "description") + private String description; + + @Column(name = "option_type") + @Enumerated(EnumType.STRING) + private OptionType optionType; @Column(name = "is_deleted") private boolean isDeleted; @@ -47,8 +55,12 @@ public String getName() { return name; } - public String getEventType() { - return eventType; + public String getDescription() { + return description; + } + + public OptionType getOptionType() { + return optionType; } public boolean getIsDeleted() { diff --git a/src/main/java/team1/BE/seamless/entity/ProjectEntity.java b/src/main/java/team1/BE/seamless/entity/ProjectEntity.java index 9480ac6..c9a9afc 100644 --- a/src/main/java/team1/BE/seamless/entity/ProjectEntity.java +++ b/src/main/java/team1/BE/seamless/entity/ProjectEntity.java @@ -47,7 +47,7 @@ public ProjectEntity(String name, UserEntity userEntity, List pro @Column(name = "is_deleted") private boolean isDeleted; - @ManyToOne(fetch = FetchType.LAZY) + @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "user_id") private UserEntity userEntity; diff --git a/src/main/java/team1/BE/seamless/entity/ProjectOption.java b/src/main/java/team1/BE/seamless/entity/ProjectOption.java index 676fddf..ba45c6c 100644 --- a/src/main/java/team1/BE/seamless/entity/ProjectOption.java +++ b/src/main/java/team1/BE/seamless/entity/ProjectOption.java @@ -16,14 +16,12 @@ public ProjectOption() { } - public ProjectOption(String name, OptionEntity optionEntity) { - this.name = name; + public ProjectOption(OptionEntity optionEntity) { this.isDeleted = false; this.optionEntity = optionEntity; } - public ProjectOption(String name, ProjectEntity projectEntity, OptionEntity optionEntity) { - this.name = name; + public ProjectOption(ProjectEntity projectEntity, OptionEntity optionEntity) { this.isDeleted = false; this.projectEntity = projectEntity; this.optionEntity = optionEntity; @@ -34,9 +32,6 @@ public ProjectOption(String name, ProjectEntity projectEntity, OptionEntity opti @Column(name = "project_option_id") private Long id; - @Column(name = "name") - private String name; - @Column(name = "is_deleted") private boolean isDeleted; @@ -52,15 +47,11 @@ public Long getId() { return id; } - public String getName() { - return name; - } - public boolean getIsDeleted() { return isDeleted; } - public ProjectEntity getProject() { + public ProjectEntity getProjectEntity() { return projectEntity; } diff --git a/src/main/java/team1/BE/seamless/entity/enums/OptionType.java b/src/main/java/team1/BE/seamless/entity/enums/OptionType.java new file mode 100644 index 0000000..16b0ad2 --- /dev/null +++ b/src/main/java/team1/BE/seamless/entity/enums/OptionType.java @@ -0,0 +1,22 @@ +package team1.BE.seamless.entity.enums; + +public enum OptionType { + POSITIVE("POSITIVE_OPTION", "긍정적_옵션"), + NEGATIVE("NEGATIVE_OPTION", "부정적_옵션"); + + private final String key; + private final String title; + + OptionType(String key, String title) { + this.key = key; + this.title = title; + } + + public String getKey() { + return key; + } + + public String getTitle() { + return title; + } +} diff --git a/src/main/java/team1/BE/seamless/init/InitData.java b/src/main/java/team1/BE/seamless/init/InitData.java index 3ac6d89..81ae9bf 100644 --- a/src/main/java/team1/BE/seamless/init/InitData.java +++ b/src/main/java/team1/BE/seamless/init/InitData.java @@ -13,14 +13,16 @@ public class InitData { private final ProjectCreator projectCreator; private final UserCreator userCreator; private final OptionCreator optionCreator; + private final MemberCreator memberCreator; @Autowired public InitData(TestCreator testCreator, ProjectCreator projectCreator, UserCreator userCreator, - OptionCreator optionCreator) { + OptionCreator optionCreator, MemberCreator memberCreator) { this.testCreator = testCreator; this.projectCreator = projectCreator; this.userCreator = userCreator; this.optionCreator = optionCreator; + this.memberCreator = memberCreator; } @PostConstruct @@ -30,5 +32,6 @@ public void init() { userCreator.creator(); optionCreator.creator(); projectCreator.creator(); + memberCreator.creator(); } } diff --git a/src/main/java/team1/BE/seamless/init/MemberCreator.java b/src/main/java/team1/BE/seamless/init/MemberCreator.java new file mode 100644 index 0000000..07da476 --- /dev/null +++ b/src/main/java/team1/BE/seamless/init/MemberCreator.java @@ -0,0 +1,30 @@ +package team1.BE.seamless.init; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import team1.BE.seamless.DTO.MemberRequestDTO; +import team1.BE.seamless.service.MemberService; + +@Component +public class MemberCreator { + + private final MemberService memberService; + + @Autowired + public MemberCreator(MemberService memberService) { + this.memberService = memberService; + } + + public void creator() { + // 테스트용으로 request데이터 생성 + MemberRequestDTO.CreateMember member1 = new MemberRequestDTO.CreateMember("권순호","MEMBER","ex1@gmail.com","exURL1"); + memberService.createMember(1L, member1); // HttpServletRequest는 null 처리가 안돼서 테스트용으로 새로운 create만듦 + + MemberRequestDTO.CreateMember member2 = new MemberRequestDTO.CreateMember("김동혁","USER","ex2@gmail.com","exURL2"); + memberService.createMember(1L, member2); // HttpServletRequest는 null 처리가 안돼서 테스트용으로 새로운 create만듦 + + MemberRequestDTO.CreateMember member3 = new MemberRequestDTO.CreateMember("김도헌","MEMBER","ex3@gmail.com","exURL3"); + memberService.createMember(1L, member3); // HttpServletRequest는 null 처리가 안돼서 테스트용으로 새로운 create만듦 + } + +} diff --git a/src/main/java/team1/BE/seamless/init/OptionCreator.java b/src/main/java/team1/BE/seamless/init/OptionCreator.java index bed6653..22fd0d9 100644 --- a/src/main/java/team1/BE/seamless/init/OptionCreator.java +++ b/src/main/java/team1/BE/seamless/init/OptionCreator.java @@ -3,6 +3,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import team1.BE.seamless.DTO.OptionDTO.OptionCreate; +import team1.BE.seamless.entity.enums.OptionType; import team1.BE.seamless.service.OptionService; @Component @@ -16,8 +17,8 @@ public OptionCreator(OptionService optionService) { } public void creator() { - optionService.createOption(new OptionCreate("옵션1", "타입1")); - optionService.createOption(new OptionCreate("옵션2", "타입1")); - optionService.createOption(new OptionCreate("옵션3", "타입2")); + optionService.createOption(new OptionCreate("옵션1", "긍정적 옵션1", OptionType.POSITIVE.getKey())); + optionService.createOption(new OptionCreate("옵션2", "긍정적 옵션2", OptionType.POSITIVE.getKey())); + optionService.createOption(new OptionCreate("옵션3", "부정적 옵션1", OptionType.NEGATIVE.getKey())); } } diff --git a/src/main/java/team1/BE/seamless/init/ProjectCreator.java b/src/main/java/team1/BE/seamless/init/ProjectCreator.java index e7bb1b3..ec06d54 100644 --- a/src/main/java/team1/BE/seamless/init/ProjectCreator.java +++ b/src/main/java/team1/BE/seamless/init/ProjectCreator.java @@ -18,9 +18,9 @@ public ProjectCreator(ProjectService projectService) { } public void creator() { - projectService.createProject(new ProjectCreate("프로젝트 이름1", 1L, + projectService.createProject(new ProjectCreate("프로젝트 이름1", LocalDateTime.of(2024, 10, 1, 0, 0, 0), - LocalDateTime.of(2024, 10, 3, 0, 0, 0), + LocalDateTime.of(2025, 10, 3, 0, 0, 0), List.of(1L, 2L, 3L)), "user1@google.com" ); diff --git a/src/main/java/team1/BE/seamless/mapper/MemberMapper.java b/src/main/java/team1/BE/seamless/mapper/MemberMapper.java index bd927ce..56f6ce5 100644 --- a/src/main/java/team1/BE/seamless/mapper/MemberMapper.java +++ b/src/main/java/team1/BE/seamless/mapper/MemberMapper.java @@ -3,6 +3,7 @@ import org.springframework.stereotype.Component; import team1.BE.seamless.DTO.MemberRequestDTO.CreateMember; import team1.BE.seamless.DTO.MemberRequestDTO.UpdateMember; +import team1.BE.seamless.DTO.MemberResponseDTO; import team1.BE.seamless.entity.MemberEntity; import team1.BE.seamless.entity.ProjectEntity; @@ -28,4 +29,32 @@ public MemberEntity toUpdate(MemberEntity member, UpdateMember update) { return member; } + public MemberResponseDTO toGetResponseDTO(MemberEntity memberEntity) { + return new MemberResponseDTO("성공적으로 조회되었습니다.", + memberEntity.getName(), + memberEntity.getRole(), + memberEntity.getEmail()); + } + + public MemberResponseDTO toDeleteResponseDTO(MemberEntity memberEntity) { + return new MemberResponseDTO("성공적으로 삭제되었습니다.", + memberEntity.getName(), + memberEntity.getRole(), + memberEntity.getEmail()); + } + + public MemberResponseDTO toCreateResponseDTO(MemberEntity memberEntity) { + return new MemberResponseDTO("성공적으로 생성되었습니다.", + memberEntity.getName(), + memberEntity.getRole(), + memberEntity.getEmail()); + } + + public MemberResponseDTO toPutResponseDTO(MemberEntity memberEntity) { + return new MemberResponseDTO("성공적으로 수정되었습니다.", + memberEntity.getName(), + memberEntity.getRole(), + memberEntity.getEmail()); + } + } diff --git a/src/main/java/team1/BE/seamless/mapper/OptionMapper.java b/src/main/java/team1/BE/seamless/mapper/OptionMapper.java index e9ae056..e00976a 100644 --- a/src/main/java/team1/BE/seamless/mapper/OptionMapper.java +++ b/src/main/java/team1/BE/seamless/mapper/OptionMapper.java @@ -1,8 +1,11 @@ package team1.BE.seamless.mapper; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import team1.BE.seamless.DTO.OptionDTO.OptionCreate; import team1.BE.seamless.entity.OptionEntity; +import team1.BE.seamless.entity.enums.OptionType; +import team1.BE.seamless.util.errorException.BaseHandler; @Component public class OptionMapper { @@ -10,7 +13,17 @@ public class OptionMapper { public OptionEntity toEntity(OptionCreate create) { return new OptionEntity( create.getName(), - create.getEventType() + create.getDescription(), + toOptionType(create.getOptionType()) ); } + + public OptionType toOptionType(String optionType) { + for (OptionType type : OptionType.values()) { + if (type.getKey().equals(optionType)) { + return type; + } + } + throw new BaseHandler(HttpStatus.NOT_FOUND,"잘못된 옵션 타입입니다."); + } } diff --git a/src/main/java/team1/BE/seamless/mapper/ProjectMapper.java b/src/main/java/team1/BE/seamless/mapper/ProjectMapper.java index 6163912..ccc75aa 100644 --- a/src/main/java/team1/BE/seamless/mapper/ProjectMapper.java +++ b/src/main/java/team1/BE/seamless/mapper/ProjectMapper.java @@ -4,6 +4,8 @@ import java.util.List; import org.springframework.stereotype.Component; import team1.BE.seamless.DTO.ProjectDTO; +import team1.BE.seamless.DTO.ProjectDTO.ProjectDetail; +import team1.BE.seamless.DTO.ProjectDTO.ProjectPeriod; import team1.BE.seamless.entity.ProjectEntity; import team1.BE.seamless.entity.ProjectOption; import team1.BE.seamless.entity.UserEntity; @@ -22,4 +24,23 @@ public ProjectEntity toEntity(ProjectDTO.ProjectCreate create, UserEntity userEn ); } + public ProjectDetail toDetail(ProjectEntity projectEntity) { + return new ProjectDTO.ProjectDetail( + projectEntity.getId(), + projectEntity.getName(), + projectEntity.getStartDate(), + projectEntity.getEndDate(), + projectEntity.getProjectOptions().stream().map(ProjectOption::getId).toList() + ); + } + + public ProjectPeriod toPeriod(ProjectEntity projectEntity) { + return new ProjectDTO.ProjectPeriod( + projectEntity.getId(), + projectEntity.getName(), + projectEntity.getStartDate(), + projectEntity.getEndDate() + ); + } + } diff --git a/src/main/java/team1/BE/seamless/mapper/TaskMapper.java b/src/main/java/team1/BE/seamless/mapper/TaskMapper.java index 8e04337..51ab0bf 100644 --- a/src/main/java/team1/BE/seamless/mapper/TaskMapper.java +++ b/src/main/java/team1/BE/seamless/mapper/TaskMapper.java @@ -1,7 +1,9 @@ package team1.BE.seamless.mapper; +import java.time.LocalDateTime; import org.springframework.stereotype.Component; import team1.BE.seamless.DTO.TaskDTO.Create; +import team1.BE.seamless.DTO.TaskDTO.TaskDetail; import team1.BE.seamless.DTO.TaskDTO.Update; import team1.BE.seamless.entity.MemberEntity; import team1.BE.seamless.entity.ProjectEntity; @@ -33,4 +35,9 @@ public TaskEntity toUpdate(TaskEntity task, Update update) { return task; } + + + public TaskDetail toDetail(TaskEntity task) { + return new TaskDetail(task.getId(), task.getName(), task.getRemark(), task.getOwner().getId(), task.getProgress(), task.getStartDate(), task.getEndDate()); + } } diff --git a/src/main/java/team1/BE/seamless/repository/MemberRepository.java b/src/main/java/team1/BE/seamless/repository/MemberRepository.java index d0c86a7..0cac24b 100644 --- a/src/main/java/team1/BE/seamless/repository/MemberRepository.java +++ b/src/main/java/team1/BE/seamless/repository/MemberRepository.java @@ -11,7 +11,10 @@ @Repository public interface MemberRepository extends JpaRepository { - Optional findByIdAndProjectEntityIdAndIsDeleteFalse(Long id, Long projectId); - Page findAllByProjectEntityIdAndIsDeleteFalse(Long projectId, Pageable pageable); + + Optional findByProjectEntityIdAndEmailAndIsDeleteFalse(Long projectId, String email); + + Optional findByProjectEntityIdAndIdAndIsDeleteFalse(Long projectId, Long memberId); + } diff --git a/src/main/java/team1/BE/seamless/repository/ProjectRepository.java b/src/main/java/team1/BE/seamless/repository/ProjectRepository.java index 4674197..67b2786 100644 --- a/src/main/java/team1/BE/seamless/repository/ProjectRepository.java +++ b/src/main/java/team1/BE/seamless/repository/ProjectRepository.java @@ -20,4 +20,7 @@ public interface ProjectRepository extends JpaRepository { Optional findByIdAndIsDeletedFalse(Long id); Page findByUserEntityEmailAndIsDeletedFalse(Pageable pageable, String email); + + Optional findByIdAndUserEntityIdAndIsDeletedFalse(Long projectId, Long userId); + } diff --git a/src/main/java/team1/BE/seamless/repository/TaskRepository.java b/src/main/java/team1/BE/seamless/repository/TaskRepository.java index 093511e..60d68e4 100644 --- a/src/main/java/team1/BE/seamless/repository/TaskRepository.java +++ b/src/main/java/team1/BE/seamless/repository/TaskRepository.java @@ -12,7 +12,7 @@ public interface TaskRepository extends JpaRepository { Optional findByIdAndIsDeletedFalse(Long id); - Page findAllByProjectIdAndIsDeletedFalse(Long projectId, Pageable pageable); + Page findAllByProjectEntityIdAndIsDeletedFalse(Long projectId, Pageable pageable); Optional findByIdAndProjectEntityUserEntityEmail(Long id, String email); } diff --git a/src/main/java/team1/BE/seamless/repository/UserRepository.java b/src/main/java/team1/BE/seamless/repository/UserRepository.java index 6505b88..6f11098 100644 --- a/src/main/java/team1/BE/seamless/repository/UserRepository.java +++ b/src/main/java/team1/BE/seamless/repository/UserRepository.java @@ -11,5 +11,5 @@ public interface UserRepository extends JpaRepository { Optional findByEmail(String email); Optional findByEmailAndIsDeleteFalse(String email); - Optional findByEmailAndIsDeleteFalse(String email, Integer isDelete); + // Optional findByEmailAndIsDeleteFalse(String email, Integer isDelete); } diff --git a/src/main/java/team1/BE/seamless/service/AttendURLService.java b/src/main/java/team1/BE/seamless/service/AttendURLService.java index bc96f20..25744e8 100644 --- a/src/main/java/team1/BE/seamless/service/AttendURLService.java +++ b/src/main/java/team1/BE/seamless/service/AttendURLService.java @@ -9,6 +9,7 @@ import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import team1.BE.seamless.entity.ProjectEntity; +import team1.BE.seamless.entity.enums.Role; import team1.BE.seamless.repository.ProjectRepository; import team1.BE.seamless.util.auth.AesEncrypt; import team1.BE.seamless.util.auth.ParsingPram; @@ -29,11 +30,23 @@ public AttendURLService(ProjectRepository projectRepository, ParsingPram parsing this.aesEncrypt = aesEncrypt; } - public String generateAttendURL(HttpServletRequest req, @Valid Long projectId) { - ProjectEntity project = projectRepository.findByIdAndUserEntityEmail(projectId, - parsingPram.getEmail(req)) + public String generateAttendURL(HttpServletRequest req, @Valid Long projectId, @Valid Long userId) { +// ProjectEntity project = projectRepository.findByIdAndUserEntityEmailAndIsDeletedFalse(projectId, +// parsingPram.getEmail(req)) 토큰에서 이메일 찾는 로직 테스트 안 돼서, 일단 프로젝트 id+유저id 로 찾는 로직으로 해놓음(추후 다시 수정 예정) + ProjectEntity project = projectRepository.findByIdAndUserEntityIdAndIsDeletedFalse(projectId,userId) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "프로젝트가 존재하지 않음")); + // 현재 시간이 프로젝트 종료 기한을 넘어섰는지 체크 +// if (project.getEndDate().isBefore(LocalDateTime.now())) { +// throw new BaseHandler(HttpStatus.BAD_REQUEST, "프로젝트는 종료되었습니다."); +// } 프로젝트 initData에 EndDate 설정이 안되어있어서 지금 테스트하면 오류걸림 그래서 주석처리 해놓음ㅇㅇ + + // 팀장인지 확인(팀원인지 굳이 한번 더 확인하지 않음. 팀장인지만 검증.) + if (parsingPram.getRole(req).equals(Role.USER.toString())) { + throw new BaseHandler(HttpStatus.UNAUTHORIZED,"생성 권한이 없습니다."); + } + + // 코드는 프로젝트id + exp로 구성 // exp는 1일로 가정 String code = aesEncrypt.encrypt( diff --git a/src/main/java/team1/BE/seamless/service/MemberService.java b/src/main/java/team1/BE/seamless/service/MemberService.java index e46727f..4102347 100644 --- a/src/main/java/team1/BE/seamless/service/MemberService.java +++ b/src/main/java/team1/BE/seamless/service/MemberService.java @@ -1,5 +1,6 @@ package team1.BE.seamless.service; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; @@ -9,107 +10,140 @@ import team1.BE.seamless.DTO.MemberRequestDTO.CreateMember; import team1.BE.seamless.DTO.MemberRequestDTO.UpdateMember; import team1.BE.seamless.DTO.MemberRequestDTO.getMemberList; +import team1.BE.seamless.DTO.MemberResponseDTO; import team1.BE.seamless.entity.MemberEntity; import team1.BE.seamless.entity.ProjectEntity; +import team1.BE.seamless.entity.enums.Role; import team1.BE.seamless.mapper.MemberMapper; import team1.BE.seamless.repository.MemberRepository; import team1.BE.seamless.repository.ProjectRepository; +import team1.BE.seamless.util.auth.ParsingPram; import team1.BE.seamless.util.errorException.BaseHandler; +import java.time.LocalDateTime; + @Service public class MemberService { private final MemberRepository memberRepository; - private final MemberMapper memberMapper; - private final ProjectRepository projectRepository; + private final ParsingPram parsingPram; @Autowired public MemberService(MemberRepository memberRepository, MemberMapper memberMapper, - ProjectRepository projectRepository) { + ProjectRepository projectRepository, ParsingPram parsingPram) { this.memberRepository = memberRepository; this.memberMapper = memberMapper; this.projectRepository = projectRepository; + this.parsingPram = parsingPram; } - public MemberEntity getMember(Long projectId, Long memberId) { -// Optional memberEntity = memberRepository.findById(memberId); -// if (memberEntity.isPresent()) { -// return memberEntity.get(); -// } -// else { -// throw new BaseHandler(HttpStatus.NOT_FOUND, "프로젝트가 존재하지 않음"); -// } - return memberRepository.findByIdAndProjectEntityIdAndIsDeleteFalse(memberId, projectId) + public MemberResponseDTO getMember(Long projectId, Long memberId, HttpServletRequest req) { + // 팀원인지 확인.. 삭제함 + + ProjectEntity project = projectRepository.findById(projectId) + .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "해당 프로젝트가 존재하지 않습니다.")); + +// if (project.getEndDate().isBefore(LocalDateTime.now())) { +// throw new BaseHandler(HttpStatus.BAD_REQUEST, "프로젝트는 종료되었습니다."); +// } 프로젝트 initData에 EndDate 설정이 안되어있어서 지금 테스트하면 오류걸림 그래서 주석처리 해놓음ㅇㅇ + + MemberEntity memberEntity = memberRepository.findByProjectEntityIdAndIdAndIsDeleteFalse(projectId, memberId) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "해당 멤버가 존재하지 않습니다.")); + + return memberMapper.toGetResponseDTO(memberEntity); } public Page getMemberList(@Valid Long projectId, - getMemberList memberListRequestDTO) { + getMemberList memberListRequestDTO, HttpServletRequest req) { + // 팀원인지 확인.. 삭제함 + + ProjectEntity project = projectRepository.findById(projectId) + .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "해당 프로젝트가 존재하지 않습니다.")); + +// if (project.getEndDate().isBefore(LocalDateTime.now())) { +// throw new BaseHandler(HttpStatus.BAD_REQUEST, "프로젝트는 종료되었습니다."); +// } 프로젝트 initData에 EndDate 설정이 안되어있어서 지금 테스트하면 오류걸림 그래서 주석처리 해놓음ㅇㅇ + return memberRepository.findAllByProjectEntityIdAndIsDeleteFalse(projectId, memberListRequestDTO.toPageable()); } - public MemberEntity createMember(Long projectId, CreateMember create) { -// Optional projectEntity = projectRepository.findById(projectId); -// if(projectEntity.isPresent()) { -// MemberEntity memberEntity = memberMapper.toMemberEntity(memberRequestDTO); -// memberEntity.setProject(projectEntity.get()); // 코드 구조상 어쩔 수 없이 setter 사용..(get메서드가 Optional기능이라 이렇게 함) -// memberRepository.save(memberEntity); -// // memberRepository.save(memberEntity) 리턴값은 MemberEntity임 JPA 기능임! -// MemberResponseDTO memberResponseDTO = new MemberResponseDTO("팀원이 추가되었습니다."); -// return memberResponseDTO; -// } -// else { -// throw new BaseHandler(HttpStatus.NOT_FOUND, "해당하는 프로젝트가 존재하지 않습니다."); -// } + public MemberResponseDTO createMember(Long projectId, CreateMember create, HttpServletRequest req) { + // 팀원인지 확인.. 삭제함 + ProjectEntity project = projectRepository.findById(projectId) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "해당 프로젝트가 존재하지 않습니다.")); + // 아래는 프로젝트가 종료됐는데, 그 후에 팀원이 참여링크를 통해 프로젝트 참여를 했을 때 걸러내는거임ㅇㅇ +// if (project.getEndDate().isBefore(LocalDateTime.now())) { +// throw new BaseHandler(HttpStatus.BAD_REQUEST, "프로젝트는 종료되었습니다."); +// } 프로젝트 initData에 EndDate 설정이 안되어있어서 지금 테스트하면 오류걸림 그래서 주석처리 해놓음ㅇㅇ + + MemberEntity member = memberMapper.toEntity(create, project); + memberRepository.save(member); + + return memberMapper.toCreateResponseDTO(member); + } + + public MemberResponseDTO createMember(Long projectId, CreateMember create) { + // 테스트용 오버로딩임. 삭제 금지 + + ProjectEntity project = projectRepository.findById(projectId) + .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "해당 프로젝트가 존재하지 않습니다.")); + +// if (project.getEndDate().isBefore(LocalDateTime.now())) { +// throw new BaseHandler(HttpStatus.BAD_REQUEST, "프로젝트는 종료되었습니다."); +// } 프로젝트 initData에 EndDate 설정이 안되어있어서 지금 테스트하면 오류걸림 그래서 주석처리 해놓음ㅇㅇ + MemberEntity member = memberMapper.toEntity(create, project); memberRepository.save(member); - return member; + return memberMapper.toCreateResponseDTO(member); } @Transactional - public MemberEntity updateMember(Long projectId, Long memberId, UpdateMember update) { -// Optional existingMemberEntity = memberRepository.findById(memberId); -// if(existingMemberEntity.isPresent()) { -// MemberEntity updatedMember = memberMapper.toMemberUpdateEntity(memberRequestDTO,existingMemberEntity); -// memberRepository.save(updatedMember); -// MemberResponseDTO memberResponseDTO = new MemberResponseDTO("팀원 정보가 성공적으로 변경되었습니다."); -// return memberResponseDTO; -// } -// else { -// throw new BaseHandler(HttpStatus.NOT_FOUND,"해당하는 팀원이 존재하지 않습니다."); -// } - MemberEntity member = memberRepository.findByIdAndProjectEntityIdAndIsDeleteFalse(memberId, - projectId) + public MemberResponseDTO updateMember(Long projectId, Long memberId, UpdateMember update, HttpServletRequest req) { + ProjectEntity project = projectRepository.findById(projectId) + .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "해당 프로젝트가 존재하지 않습니다.")); + +// if (project.getEndDate().isBefore(LocalDateTime.now())) { +// throw new BaseHandler(HttpStatus.BAD_REQUEST, "프로젝트는 종료되었습니다."); +// } 프로젝트 initData에 EndDate 설정이 안되어있어서 지금 테스트하면 오류걸림 그래서 주석처리 해놓음ㅇㅇ + // 팀장인지 확인(팀원인지 굳이 한번 더 확인하지 않음. 팀장인지만 검증.) + if (parsingPram.getRole(req).equals(Role.USER.toString())) { + throw new BaseHandler(HttpStatus.UNAUTHORIZED,"수정 권한이 없습니다."); + } + + MemberEntity member = memberRepository.findByProjectEntityIdAndIdAndIsDeleteFalse( + projectId,memberId) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "해당 멤버가 존재하지 않습니다.")); - memberMapper.toUpdate(member, update); - return member; + MemberEntity memberEntity = memberMapper.toUpdate(member, update); + return memberMapper.toPutResponseDTO(memberEntity); } @Transactional - public MemberEntity deleteMember(Long projectId, Long memberId) { -// Optional existingMemberEntity = memberRepository.findById(memberId); -// if(existingMemberEntity.isPresent()) { -// String name = existingMemberEntity.get().getName(); -// memberRepository.delete(existingMemberEntity.get()); -// return new MemberResponseDTO("팀원 (" + name + ")이 팀에서 삭제되었습니다."); -// } -// else { -// throw new BaseHandler(HttpStatus.NOT_FOUND,"해당하는 팀원이 존재하지 않습니다."); -// } - MemberEntity member = memberRepository.findByIdAndProjectEntityIdAndIsDeleteFalse(memberId, - projectId) + public MemberResponseDTO deleteMember(Long projectId, Long memberId, HttpServletRequest req) { + ProjectEntity project = projectRepository.findById(projectId) + .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "해당 프로젝트가 존재하지 않습니다.")); +// +// if (project.getEndDate().isBefore(LocalDateTime.now())) { +// throw new BaseHandler(HttpStatus.BAD_REQUEST, "프로젝트는 종료되었습니다."); +// } 프로젝트 initData에 EndDate 설정이 안되어있어서 지금 테스트하면 오류걸림 그래서 주석처리 해놓음ㅇㅇ + + // 팀장인지 확인(팀원인지 굳이 한번 더 확인하지 않음. 팀장인지만 검증.) + if (parsingPram.getRole(req).equals(Role.USER.toString())) { + throw new BaseHandler(HttpStatus.UNAUTHORIZED,"수정 권한이 없습니다."); + } + + MemberEntity member = memberRepository.findByProjectEntityIdAndIdAndIsDeleteFalse( + projectId, memberId) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "해당 멤버가 존재하지 않습니다.")); member.setDelete(true); - return member; + return memberMapper.toDeleteResponseDTO(member); } } diff --git a/src/main/java/team1/BE/seamless/service/ProjectService.java b/src/main/java/team1/BE/seamless/service/ProjectService.java index ec679be..3c2d468 100644 --- a/src/main/java/team1/BE/seamless/service/ProjectService.java +++ b/src/main/java/team1/BE/seamless/service/ProjectService.java @@ -8,6 +8,7 @@ import org.springframework.transaction.annotation.Transactional; import team1.BE.seamless.DTO.ProjectDTO; import team1.BE.seamless.DTO.ProjectDTO.ProjectCreate; +import team1.BE.seamless.DTO.ProjectDTO.ProjectDetail; import team1.BE.seamless.DTO.ProjectDTO.ProjectPeriod; import team1.BE.seamless.DTO.ProjectDTO.ProjectUpdate; import team1.BE.seamless.entity.MemberEntity; @@ -38,47 +39,90 @@ public ProjectService(ProjectRepository projectRepository, UserRepository userRe this.projectMapper = projectMapper; } - public Page getProjectList(ProjectDTO.getList param, String email) { - return projectRepository.findAllByUserEntityEmailAndIsDeletedFalse(param.toPageable(), - email); + /** + * @param param : 페이지네이션에 관한 parameter + * @param email : 유저 토큰에서 추출한 email 정보 + * @return : 페이지네이션된 프로젝트들에 대한 정보 + * ProjectEntity에 매핑된 UserEntity의 email 정보가 일치하고 isDeleted가 false인 프로젝트를 페이지네이션 형식으로 반환 + * */ + public Page getProjectList(ProjectDTO.getList param, String email) { + return projectRepository.findAllByUserEntityEmailAndIsDeletedFalse(param.toPageable(), email).map(projectMapper::toDetail); + } - public ProjectEntity getProject(long id) { - return projectRepository.findById(id) + /** + * @param id : 프로젝트 Id + * @return : 해당 Id의 프로젝트의 정보를 반환 + * repository 조회시 존재 하지 않을 경우 Throw Not Found + * */ + public ProjectDetail getProject(long id) { + ProjectEntity projectEntity = projectRepository.findById(id) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "프로젝트가 존재하지 않음")); + return projectMapper.toDetail(projectEntity); } + /** + * @param id : 프로젝트 Id + * @return : 해당 id를 가진 프로젝트에 참여한 팀원들의 목록 + * */ public List getProjectMembers(long id) { ProjectEntity projectEntity = projectRepository.findById(id) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "프로젝트가 존재하지 않음")); return projectEntity.getMemberEntities(); } + /** + * @param param : 페이지네이션에 관한 parameter + * @param email : 유저 토큰에서 추출한 email 정보 + * @return : 프로젝트의 Id, name, startDate, endDate 정보를 페이지네이션*/ public Page getProjectPeriod(ProjectDTO.getList param, String email) { - return projectRepository.findByUserEntityEmailAndIsDeletedFalse(param.toPageable(), email); + return projectRepository.findAllByUserEntityEmailAndIsDeletedFalse(param.toPageable(), email).map(projectMapper::toPeriod); } + /** + * @param create : 프로젝트 생성 시 필요한 정보를 담은 DTO + * @param email : 유저 토큰에서 추출한 email 정보 + * @return : 생성한 프로젝트의 정보 + * 플로우 : + * email을 통해 유저가 존재하는 지 검증 -> + * DTO에 담긴 optionEntity들의 id 정보들을 통해 OptionEntity조회 -> + * OptionEntity을 ProjectOption으로 매핑 -> + * 해당 정보를 가진 ProjectEntity를 생성 후 Repo에 save -> + * 각 ProjectOption의 ProjectEntity field를 생성한 ProjectEntity로 설정 + * */ @Transactional - public ProjectEntity createProject(ProjectCreate create, String email) { + public ProjectDetail createProject(ProjectCreate create, String email) { UserEntity userEntity = userRepository.findByEmailAndIsDeleteFalse(email) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "사용자가 존재하지 않음")); List optionEntities = optionRepository.findByIdIn(create.getOptionIds()); List projectOptions = optionEntities.stream() - .map(optionEntity -> new ProjectOption(optionEntity.getName(), optionEntity)) + .map(optionEntity -> new ProjectOption(optionEntity)) .toList(); ProjectEntity projectEntity = projectRepository.save( projectMapper.toEntity(create, userEntity, projectOptions)); projectOptions.forEach( option -> option.setProjectEntity(projectEntity)); //ProjectOption에 Project 매핑 - return projectEntity; + return projectMapper.toDetail(projectEntity); } + /** + * @param id : 프로젝트 Id + * @param update : 프로젝트 업데이트 시 필요한 정보를 담은 DTO + * @return : 수정한 프로젝트의 정보 + * 플로우 : + * 프로젝트가 존재하는지 검증 -> + * 기존의 ProjectOption 리스트 초기화-> + * DTO에 담긴 Option id들을 통해 OptionEntity 조회 -> + * OptionEntity를 ProjectOption으로 매핑 -> + * ProjectOption 리스트에 추가 -> + * 나머지 정보 업데이트 후 저장 + * */ @Transactional - public ProjectEntity updateProject(long get, ProjectUpdate update) { - ProjectEntity projectEntity = projectRepository.findById(get) + public ProjectDetail updateProject(long id, ProjectUpdate update) { + ProjectEntity projectEntity = projectRepository.findById(id) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "프로젝트가 존재하지 않음")); // 기존 옵션 목록 List projectOptions = projectEntity.getProjectOptions(); @@ -87,8 +131,7 @@ public ProjectEntity updateProject(long get, ProjectUpdate update) { // 새로운 옵션 추가 List optionEntities = optionRepository.findByIdIn(update.getOptionIds()); for (OptionEntity optionEntity : optionEntities) { - ProjectOption projectOption = new ProjectOption(optionEntity.getName(), projectEntity, - optionEntity); + ProjectOption projectOption = new ProjectOption(projectEntity, optionEntity); projectOptions.add(projectOption); } @@ -98,15 +141,20 @@ public ProjectEntity updateProject(long get, ProjectUpdate update) { update.getEndDate() ); - projectRepository.save(projectEntity); - return projectEntity; + return projectMapper.toDetail(projectEntity); } + /** + * @param id : 프로젝트 Id + * @return : 삭제한 프로젝트의 Id + * 프로젝트의 존재 검증 후 존재 시 삭제 + * */ @Transactional - public Long deleteProject(long get) { - ProjectEntity projectEntity = projectRepository.findById(get) + public Long deleteProject(long id) { + ProjectEntity projectEntity = projectRepository.findById(id) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "프로젝트가 존재하지 않음")); - projectRepository.deleteById(projectEntity.getId()); + + projectEntity.setIsDeleted(true); return projectEntity.getId(); } diff --git a/src/main/java/team1/BE/seamless/service/TaskService.java b/src/main/java/team1/BE/seamless/service/TaskService.java index c088c47..5ca6828 100644 --- a/src/main/java/team1/BE/seamless/service/TaskService.java +++ b/src/main/java/team1/BE/seamless/service/TaskService.java @@ -8,6 +8,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import team1.BE.seamless.DTO.TaskDTO.Create; +import team1.BE.seamless.DTO.TaskDTO.TaskDetail; import team1.BE.seamless.DTO.TaskDTO.Update; import team1.BE.seamless.DTO.TaskDTO.getList; import team1.BE.seamless.entity.MemberEntity; @@ -40,23 +41,27 @@ public TaskService(TaskRepository taskRepository, ProjectRepository projectRepos this.parsingPram = parsingPram; } - public TaskEntity getTask(Long taskId) { - return taskRepository.findByIdAndIsDeletedFalse(taskId) + public TaskDetail getTask(Long taskId) { + TaskEntity taskEntity = taskRepository.findByIdAndIsDeletedFalse(taskId) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "존재하지 않는 태스크")); + + return taskMapper.toDetail(taskEntity); } - public Page getTaskList(Long projectId, getList param) { - return taskRepository.findAllByProjectIdAndIsDeletedFalse(projectId, param.toPageable()); + public Page getTaskList(Long projectId, getList param) { + Page taskEntities = taskRepository.findAllByProjectEntityIdAndIsDeletedFalse(projectId, param.toPageable()); + + return taskEntities.map(taskMapper::toDetail); } - public TaskEntity createTask(HttpServletRequest req, @Valid Long projectId, Create create) { + public TaskDetail createTask(HttpServletRequest req, @Valid Long projectId, Create create) { ProjectEntity project = projectRepository.findByIdAndUserEntityEmailAndIsDeletedFalse( projectId, parsingPram.getEmail(req)) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "존재하지 않는 프로젝트")); // 태스크의 일정 검증 - if (project.getStartDate().isBefore(create.getStartDate()) || project.getStartDate() - .isAfter(create.getEndDate())) { + if (project.getStartDate().isAfter(create.getStartDate()) || project.getEndDate() + .isBefore(create.getEndDate())) { throw new BaseHandler(HttpStatus.FORBIDDEN, "태스크는 프로젝트의 기한을 넘어설 수 없습니다."); } @@ -66,17 +71,18 @@ public TaskEntity createTask(HttpServletRequest req, @Valid Long projectId, Crea TaskEntity taskEntity = taskMapper.toEntity(project, member, create); taskRepository.save(taskEntity); - return taskEntity; + + return taskMapper.toDetail(taskEntity); } @Transactional - public TaskEntity updateTask(HttpServletRequest req, @Valid Long taskId, @Valid Update update) { + public TaskDetail updateTask(HttpServletRequest req, @Valid Long taskId, @Valid Update update) { TaskEntity task = taskRepository.findByIdAndIsDeletedFalse(taskId) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "존재하지 않는 태스크")); // 태스크의 일정 검증 - if (task.getProject().getStartDate().isBefore(update.getStartDate()) || task.getProject() - .getStartDate().isAfter(update.getEndDate())) { + if (task.getProject().getStartDate().isAfter(update.getStartDate()) || task.getProject() + .getEndDate().isBefore(update.getEndDate())) { throw new BaseHandler(HttpStatus.FORBIDDEN, "태스크는 프로젝트의 기한을 넘어설 수 없습니다."); } @@ -100,7 +106,8 @@ public TaskEntity updateTask(HttpServletRequest req, @Valid Long taskId, @Valid } taskMapper.toUpdate(task, update); - return task; + + return taskMapper.toDetail(task); } @Transactional diff --git a/src/main/java/team1/BE/seamless/service/TestService.java b/src/main/java/team1/BE/seamless/service/TestService.java index ff0f9af..895937a 100644 --- a/src/main/java/team1/BE/seamless/service/TestService.java +++ b/src/main/java/team1/BE/seamless/service/TestService.java @@ -1,15 +1,22 @@ package team1.BE.seamless.service; import jakarta.validation.Valid; +import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import team1.BE.seamless.DTO.TestDTO.create; import team1.BE.seamless.DTO.TestDTO.getList; +import team1.BE.seamless.entity.MemberEntity; import team1.BE.seamless.entity.TestEntity; +import team1.BE.seamless.entity.UserEntity; import team1.BE.seamless.mapper.TestMapper; +import team1.BE.seamless.repository.MemberRepository; import team1.BE.seamless.repository.TestRepository; +import team1.BE.seamless.repository.UserRepository; +import team1.BE.seamless.util.auth.JwtToken; +import team1.BE.seamless.util.auth.Token; import team1.BE.seamless.util.errorException.BaseHandler; @Service @@ -17,11 +24,16 @@ public class TestService { private final TestMapper testMapper; private final TestRepository testRepository; + private final JwtToken jwtToken; + private final UserRepository userRepository; @Autowired - public TestService(TestMapper testMapper, TestRepository testRepository) { + public TestService(TestMapper testMapper, TestRepository testRepository, JwtToken jwtToken, + UserRepository userRepository) { this.testMapper = testMapper; this.testRepository = testRepository; + this.jwtToken = jwtToken; + this.userRepository = userRepository; } public Page getTestList(@Valid getList param) { @@ -41,4 +53,13 @@ public TestEntity createTest(@Valid create create) { testRepository.save(entity); return entity; } + + public Token TestTokenCreate(Long userId) { + UserEntity user = userRepository.findById(userId) + .orElseThrow(()-> new BaseHandler(HttpStatus.FORBIDDEN, "유저 없음")); + + String token = jwtToken.createUserToken(user); + + return new Token(token); + } } diff --git a/src/main/java/team1/BE/seamless/service/UserService.java b/src/main/java/team1/BE/seamless/service/UserService.java index 04679c9..791b69e 100644 --- a/src/main/java/team1/BE/seamless/service/UserService.java +++ b/src/main/java/team1/BE/seamless/service/UserService.java @@ -48,7 +48,7 @@ public UserSimple updateUser(HttpServletRequest req, @Valid UserUpdate update) { @Transactional public UserSimple deleteUser(HttpServletRequest req) { - UserEntity user = userRepository.findByEmailAndIsDeleteFalse(parsingPram.getEmail(req), 0) + UserEntity user = userRepository.findByEmailAndIsDeleteFalse(parsingPram.getEmail(req)) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "해당 유저가 존재하지 않습니다.")); user.setIsDelete(); diff --git a/src/main/java/team1/BE/seamless/util/auth/JwtToken.java b/src/main/java/team1/BE/seamless/util/auth/JwtToken.java index fd3016d..bf93b4e 100644 --- a/src/main/java/team1/BE/seamless/util/auth/JwtToken.java +++ b/src/main/java/team1/BE/seamless/util/auth/JwtToken.java @@ -23,6 +23,7 @@ import org.springframework.stereotype.Component; import team1.BE.seamless.DTO.AuthDTO.PrincipalDetails; import team1.BE.seamless.entity.MemberEntity; +import team1.BE.seamless.entity.UserEntity; import team1.BE.seamless.entity.enums.Role; import team1.BE.seamless.util.errorException.RuntimeHandler; @@ -74,6 +75,24 @@ public String createMemberToken(MemberEntity member) { .compact(); } + /** + * 테스트외 절대 사용 금지 + * */ + public String createUserToken(UserEntity user) { + ZonedDateTime now = ZonedDateTime.now().withZoneSameInstant(ZoneId.of("UTC")); + ZonedDateTime expirationDateTime = now.plusSeconds(tokenExpTime); + + Claims claims = Jwts.claims(); + claims.put("authentication", Role.MEMBER.toString()); + claims.put("email", user.getEmail()); + return Jwts.builder() + .setClaims(claims) + .setIssuedAt(Date.from(now.toInstant())) + .setExpiration(Date.from(expirationDateTime.toInstant())) + .signWith(secretKey, SignatureAlgorithm.HS256) + .compact(); + } + public Claims validateToken(String token) { try { return Jwts.parserBuilder() diff --git a/src/main/java/team1/BE/seamless/util/auth/SecurityConfig.java b/src/main/java/team1/BE/seamless/util/auth/SecurityConfig.java index fabd190..5777bc2 100644 --- a/src/main/java/team1/BE/seamless/util/auth/SecurityConfig.java +++ b/src/main/java/team1/BE/seamless/util/auth/SecurityConfig.java @@ -3,6 +3,7 @@ import org.springframework.beans.factory.annotation.Autowired; 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.annotation.web.configurers.AbstractHttpConfigurer; @@ -59,7 +60,11 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti "/**/*.svg", "/**/*.jpg", "/**/*.html", "/**/*.css", "/**/*.js") .permitAll() // 인증, h2 - .requestMatchers("/h2-console/**").permitAll() + .requestMatchers("/h2-console/**", "/auth/**","/api/test/**").permitAll() +// 멤버 생성 + .requestMatchers(HttpMethod.POST,"/api/project/{project_id}/member/**").permitAll() +// 멤버 조회 + .requestMatchers(HttpMethod.GET,"/api/project/{project_id}/member/**").permitAll() .anyRequest() .authenticated() ) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index c84f0c7..6079bcb 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -7,8 +7,8 @@ spring.h2.console.enabled=true spring.sql.init.mode=always #spring.sql.init.schema-locations=classpath:Schema.sql #spring.sql.init.data-locations=classpath:Data.sql -spring.jpa.hibernate.ddl-auto=create-drop -spring.jpa.generate-ddl=false +spring.jpa.hibernate.ddl-auto=update +spring.jpa.generate-ddl=true spring.jpa.database=h2 spring.jpa.defer-datasource-initialization=true spring.jpa.properties.hibernate.format_sql=true @@ -17,7 +17,7 @@ spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect jwt.secretKey=KO)$*"g+zWechI9]KE|'irIM?ky--x/|p.K&>bA>Ef,gfD/)ekd/UcBE2kQ@BhE jwt.tokenExpTime=3600 code.secretKey=ae2af8cfb8721e39189f6f5edc928589 -spring.profiles.active=dev +spring.profiles.active=build server.forward-headers-strategy=framework spring.profiles.include=oauth spring.mvc.pathmatch.matching-strategy=ant_path_matcher diff --git a/src/test/java/team1/BE/seamless/service/UserTokenParseTest.java b/src/test/java/team1/BE/seamless/service/UserTokenParseTest.java new file mode 100644 index 0000000..bcacf55 --- /dev/null +++ b/src/test/java/team1/BE/seamless/service/UserTokenParseTest.java @@ -0,0 +1,50 @@ +package team1.BE.seamless.service; + +import static org.springframework.http.HttpMethod.POST; + +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class UserTokenParseTest { + + @LocalServerPort + private int port; + private String url = "http://localhost:"; + private final TestRestTemplate restTemplate; + private String token; + private HttpHeaders headers = new HttpHeaders(); + + + @Autowired + public UserTokenParseTest(TestRestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + + /** + * 특정 유저id의 토큰 파싱 + */ + @BeforeEach + public void setUp() { + HttpEntity requestEntity = new HttpEntity<>(null); + ResponseEntity responseEntity = restTemplate.exchange( + url + port + "/api/test/userToken/1", + POST, + requestEntity, String.class); + + int startIndex = responseEntity.getBody().indexOf("\"token\":\"") + "\"token\":\"".length(); + int endIndex = responseEntity.getBody().indexOf("\"", startIndex); + + token = responseEntity.getBody().substring(startIndex, endIndex); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setBearerAuth(token); + } + +} \ No newline at end of file