From 061c1f722d3c0a2d411ad10952d5559256b3585a Mon Sep 17 00:00:00 2001 From: youKeon Date: Fri, 13 Oct 2023 00:18:05 +0900 Subject: [PATCH 01/11] =?UTF-8?q?feat=20:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EB=8B=89=EB=84=A4=EC=9E=84=EC=9C=BC=EB=A1=9C=20=EB=A7=88?= =?UTF-8?q?=EC=9D=B4=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/controller/MemberController.java | 9 ++++++++- .../domain/member/repository/MemberRepository.java | 4 ++-- .../backend/domain/member/service/MemberService.java | 7 +++++++ .../project/dto/response/GetProjectInfoResponse.java | 9 ++++++--- .../backend/domain/project/service/ProjectService.java | 8 ++++++++ .../com/graphy/backend/global/config/SecurityConfig.java | 1 + 6 files changed, 32 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/com/graphy/backend/domain/member/controller/MemberController.java b/backend/src/main/java/com/graphy/backend/domain/member/controller/MemberController.java index f2d79f83..17a124b5 100644 --- a/backend/src/main/java/com/graphy/backend/domain/member/controller/MemberController.java +++ b/backend/src/main/java/com/graphy/backend/domain/member/controller/MemberController.java @@ -1,10 +1,10 @@ package com.graphy.backend.domain.member.controller; +import com.graphy.backend.domain.auth.util.annotation.CurrentUser; import com.graphy.backend.domain.member.domain.Member; import com.graphy.backend.domain.member.dto.response.GetMemberResponse; import com.graphy.backend.domain.member.dto.response.GetMyPageResponse; import com.graphy.backend.domain.member.service.MemberService; -import com.graphy.backend.domain.auth.util.annotation.CurrentUser; import com.graphy.backend.domain.project.service.ProjectService; import com.graphy.backend.global.result.ResultCode; import com.graphy.backend.global.result.ResultResponse; @@ -39,4 +39,11 @@ public ResponseEntity myPage(@CurrentUser Member member) { GetMyPageResponse result = projectService.myPage(member); return ResponseEntity.ok(ResultResponse.of(ResultCode.MYPAGE_GET_SUCCESS, result)); } + + @Operation(summary = "get myPage by nickname", description = "사용자 닉네임으로 마이페이지 조회") + @GetMapping("/mypage/{nickname}") + public ResponseEntity mypageByNickname(@PathVariable String nickname) { + GetMyPageResponse result = projectService.myPageByNickname(nickname); + return ResponseEntity.ok(ResultResponse.of(ResultCode.MYPAGE_GET_SUCCESS, result)); + } } diff --git a/backend/src/main/java/com/graphy/backend/domain/member/repository/MemberRepository.java b/backend/src/main/java/com/graphy/backend/domain/member/repository/MemberRepository.java index 6d74d269..41161cd1 100644 --- a/backend/src/main/java/com/graphy/backend/domain/member/repository/MemberRepository.java +++ b/backend/src/main/java/com/graphy/backend/domain/member/repository/MemberRepository.java @@ -2,8 +2,6 @@ import com.graphy.backend.domain.member.domain.Member; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Modifying; -import org.springframework.data.jpa.repository.Query; import java.util.List; import java.util.Optional; @@ -11,4 +9,6 @@ public interface MemberRepository extends JpaRepository, MemberCustomRepository { Optional findByEmail(String email); List findMemberByNicknameStartingWith(String nickname); + + Optional findFirstByNickname(String nickname); } diff --git a/backend/src/main/java/com/graphy/backend/domain/member/service/MemberService.java b/backend/src/main/java/com/graphy/backend/domain/member/service/MemberService.java index 2e98a41f..31f64e45 100644 --- a/backend/src/main/java/com/graphy/backend/domain/member/service/MemberService.java +++ b/backend/src/main/java/com/graphy/backend/domain/member/service/MemberService.java @@ -49,4 +49,11 @@ public Member findMemberByEmail(String email) { () -> new EmptyResultException(MEMBER_NOT_EXIST) ); } + + public Member findMemberByNickname(String nickname) { + return memberRepository.findFirstByNickname(nickname).orElseThrow( + () -> new EmptyResultException(MEMBER_NOT_EXIST) + ); + } + } diff --git a/backend/src/main/java/com/graphy/backend/domain/project/dto/response/GetProjectInfoResponse.java b/backend/src/main/java/com/graphy/backend/domain/project/dto/response/GetProjectInfoResponse.java index 2f8795e2..ec4de5f4 100644 --- a/backend/src/main/java/com/graphy/backend/domain/project/dto/response/GetProjectInfoResponse.java +++ b/backend/src/main/java/com/graphy/backend/domain/project/dto/response/GetProjectInfoResponse.java @@ -1,7 +1,10 @@ package com.graphy.backend.domain.project.dto.response; import com.graphy.backend.domain.project.domain.Project; -import lombok.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; @Getter @Builder @@ -13,13 +16,13 @@ public class GetProjectInfoResponse { private String projectName; - private String description; + private String content; public static GetProjectInfoResponse from(Project project) { return GetProjectInfoResponse.builder() .id(project.getId()) .projectName(project.getProjectName()) - .description(project.getDescription()) + .content(project.getContent()) .build(); } } \ No newline at end of file diff --git a/backend/src/main/java/com/graphy/backend/domain/project/service/ProjectService.java b/backend/src/main/java/com/graphy/backend/domain/project/service/ProjectService.java index dafc4a3f..4297239d 100644 --- a/backend/src/main/java/com/graphy/backend/domain/project/service/ProjectService.java +++ b/backend/src/main/java/com/graphy/backend/domain/project/service/ProjectService.java @@ -13,6 +13,7 @@ import com.graphy.backend.domain.project.dto.request.UpdateProjectRequest; import com.graphy.backend.domain.project.dto.response.*; import com.graphy.backend.domain.project.repository.ProjectRepository; +import com.graphy.backend.domain.project.repository.TagRepository; import com.graphy.backend.global.chatgpt.dto.GptCompletionDto.GptCompletionRequest; import com.graphy.backend.global.chatgpt.dto.GptCompletionDto.GptCompletionResponse; import com.graphy.backend.global.chatgpt.service.GPTChatRestService; @@ -46,6 +47,7 @@ public class ProjectService { private final CommentService commentService; private final TagService tagService; private final GPTChatRestService gptChatRestService; + private final TagRepository tagRepository; // @PostConstruct // public void initTag() throws IOException { @@ -124,6 +126,12 @@ public GetMyPageResponse myPage(Member member) { return GetMyPageResponse.of(member, projectInfoList); } + public GetMyPageResponse myPageByNickname(String nickname) { + Member member = memberService.findMemberByNickname(nickname); + List projectInfoList = this.findProjectInfoList(member.getId()); + return GetMyPageResponse.of(member, projectInfoList); + } + public Project getProjectById(Long id) { return projectRepository.findById(id).orElseThrow(() -> new EmptyResultException(ErrorCode.PROJECT_DELETED_OR_NOT_EXIST)); diff --git a/backend/src/main/java/com/graphy/backend/global/config/SecurityConfig.java b/backend/src/main/java/com/graphy/backend/global/config/SecurityConfig.java index 62e691c9..fba011cf 100644 --- a/backend/src/main/java/com/graphy/backend/global/config/SecurityConfig.java +++ b/backend/src/main/java/com/graphy/backend/global/config/SecurityConfig.java @@ -39,6 +39,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws "/api/v1/auth/signin", "/api/v1/auth/logout", "/api/v1/projects", + "/api/v1/members/**", "/swagger-ui/**").permitAll() .antMatchers(HttpMethod.GET, "/api/v1/projects/{projectId}").permitAll() .antMatchers(HttpMethod.GET, "/api/v1/comments/{commentId}").permitAll() From 484ecc70cf8152d2053da2fe35f6591075c642d2 Mon Sep 17 00:00:00 2001 From: youKeon Date: Fri, 13 Oct 2023 00:25:32 +0900 Subject: [PATCH 02/11] =?UTF-8?q?test=20:=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=EC=97=90=20=EB=94=B0=EB=A5=B8=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/controller/MemberControllerTest.java | 9 +++++---- .../domain/project/service/ProjectServiceTest.java | 8 ++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/backend/src/test/java/com/graphy/backend/domain/member/controller/MemberControllerTest.java b/backend/src/test/java/com/graphy/backend/domain/member/controller/MemberControllerTest.java index 1605f9cc..6bacde71 100644 --- a/backend/src/test/java/com/graphy/backend/domain/member/controller/MemberControllerTest.java +++ b/backend/src/test/java/com/graphy/backend/domain/member/controller/MemberControllerTest.java @@ -24,10 +24,11 @@ import java.util.List; import static org.hamcrest.Matchers.hasSize; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.when; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.payload.PayloadDocumentation.*; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -133,8 +134,8 @@ void getMyPageTest() throws Exception { .followerCount(member1.getFollowerCount()) .followingCount(member1.getFollowingCount()) .getProjectInfoResponseList(Arrays.asList( - GetProjectInfoResponse.builder().id(1L).projectName("project1").description("description1").build(), - GetProjectInfoResponse.builder().id(2L).projectName("project2").description("description2").build() + GetProjectInfoResponse.builder().id(1L).projectName("project1").content("content1").build(), + GetProjectInfoResponse.builder().id(2L).projectName("project2").content("content2").build() )) .build(); diff --git a/backend/src/test/java/com/graphy/backend/domain/project/service/ProjectServiceTest.java b/backend/src/test/java/com/graphy/backend/domain/project/service/ProjectServiceTest.java index d388ea59..98a44b8f 100644 --- a/backend/src/test/java/com/graphy/backend/domain/project/service/ProjectServiceTest.java +++ b/backend/src/test/java/com/graphy/backend/domain/project/service/ProjectServiceTest.java @@ -202,7 +202,7 @@ void myPageTest() { .projectName("project1") .description("description1") .thumbNail("thumb") - .content("content") + .content("content1") .build(); Project project2 = Project.builder() @@ -211,19 +211,19 @@ void myPageTest() { .projectName("project2") .description("description2") .thumbNail("thumb") - .content("content") + .content("content2") .build(); GetProjectInfoResponse response1 = GetProjectInfoResponse.builder() .id(1L) .projectName("project1") - .description("description1") + .content("content1") .build(); GetProjectInfoResponse response2 = GetProjectInfoResponse.builder() .id(2L) .projectName("project2") - .description("description2") + .content("content2") .build(); List responseList = Arrays.asList(response1, response2); From e064f7d26c0fba0a495b3f72d043f177961a13ac Mon Sep 17 00:00:00 2001 From: youKeon Date: Fri, 13 Oct 2023 00:35:26 +0900 Subject: [PATCH 03/11] =?UTF-8?q?test=20:=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=EC=97=90=20=EB=94=B0=EB=A5=B8=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/controller/MemberControllerTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/test/java/com/graphy/backend/domain/member/controller/MemberControllerTest.java b/backend/src/test/java/com/graphy/backend/domain/member/controller/MemberControllerTest.java index 6bacde71..1855b6f7 100644 --- a/backend/src/test/java/com/graphy/backend/domain/member/controller/MemberControllerTest.java +++ b/backend/src/test/java/com/graphy/backend/domain/member/controller/MemberControllerTest.java @@ -157,11 +157,11 @@ void getMyPageTest() throws Exception { .andExpect(jsonPath("$.data.getProjectInfoResponseList[0].id").value(1)) .andExpect(jsonPath("$.data.getProjectInfoResponseList[0].projectName").value("project1")) - .andExpect(jsonPath("$.data.getProjectInfoResponseList[0].description").value("description1")) + .andExpect(jsonPath("$.data.getProjectInfoResponseList[0].content").value("content1")) .andExpect(jsonPath("$.data.getProjectInfoResponseList[1].id").value(2)) .andExpect(jsonPath("$.data.getProjectInfoResponseList[1].projectName").value("project2")) - .andExpect(jsonPath("$.data.getProjectInfoResponseList[1].description").value("description2")) + .andExpect(jsonPath("$.data.getProjectInfoResponseList[1].content").value("content2")) .andDo(document("members/myPage/find/success", responseFields( @@ -177,7 +177,7 @@ void getMyPageTest() throws Exception { fieldWithPath("data.getProjectInfoResponseList[].id").description("프로젝트 ID"), fieldWithPath("data.getProjectInfoResponseList[].projectName").description("프로젝트 이름"), - fieldWithPath("data.getProjectInfoResponseList[].description").description("프로젝트 설명") + fieldWithPath("data.getProjectInfoResponseList[].content").description("프로젝트 소개") ))); } } From 63fdf94f80a4f89ab548d136bd12f0cd273d2b8b Mon Sep 17 00:00:00 2001 From: HaLin Kim Date: Sat, 14 Oct 2023 09:41:57 +0900 Subject: [PATCH 04/11] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EC=83=81=EC=84=B8=20=EC=A1=B0=ED=9A=8C=EC=8B=9C,?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=EC=88=98=20=EC=A6=9D=EA=B0=80=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project/controller/ProjectController.java | 21 +++++++-- .../domain/project/domain/Project.java | 9 +++- .../project/service/ProjectService.java | 46 +++++++++++++++---- 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/backend/src/main/java/com/graphy/backend/domain/project/controller/ProjectController.java b/backend/src/main/java/com/graphy/backend/domain/project/controller/ProjectController.java index a4b129e1..7534afc5 100644 --- a/backend/src/main/java/com/graphy/backend/domain/project/controller/ProjectController.java +++ b/backend/src/main/java/com/graphy/backend/domain/project/controller/ProjectController.java @@ -1,5 +1,6 @@ package com.graphy.backend.domain.project.controller; +import com.graphy.backend.domain.auth.util.annotation.CurrentUser; import com.graphy.backend.domain.member.domain.Member; import com.graphy.backend.domain.project.dto.request.CreateProjectRequest; import com.graphy.backend.domain.project.dto.request.GetProjectPlanRequest; @@ -10,8 +11,7 @@ import com.graphy.backend.domain.project.dto.response.GetProjectResponse; import com.graphy.backend.domain.project.dto.response.UpdateProjectResponse; import com.graphy.backend.domain.project.service.ProjectService; -import com.graphy.backend.domain.auth.util.annotation.CurrentUser; -import com.graphy.backend.global.common.PageRequest; +import com.graphy.backend.global.common.dto.PageRequest; import com.graphy.backend.global.error.ErrorCode; import com.graphy.backend.global.error.exception.EmptyResultException; import com.graphy.backend.global.result.ResultCode; @@ -22,14 +22,19 @@ import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseCookie; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import static org.springframework.http.HttpHeaders.SET_COOKIE; + @Tag(name = "ProjectController", description = "프로젝트 관련 API") @RestController @RequestMapping("api/v1/projects") @@ -86,9 +91,17 @@ public ResponseEntity projectList(PageRequest pageRequest, @Curr @Operation(summary = "findProject", description = "프로젝트 상세 조회") @GetMapping("/{projectId}") - public ResponseEntity projectDetails(@PathVariable Long projectId) { + public ResponseEntity projectDetails(@PathVariable Long projectId, HttpServletRequest request) { GetProjectDetailResponse result = projectService.findProjectById(projectId); - return ResponseEntity.ok(ResultResponse.of(ResultCode.PROJECT_GET_SUCCESS, result)); + Cookie cookie = projectService.addViewCount(request, projectId); + ResponseCookie responseCookie = ResponseCookie.from(cookie.getName(), cookie.getValue()) + .path(cookie.getPath()) + .maxAge(cookie.getMaxAge()) + .build(); + + return ResponseEntity.ok() + .header(SET_COOKIE, responseCookie.toString()) + .body(ResultResponse.of(ResultCode.PROJECT_GET_SUCCESS, result)); } @Operation(summary = "getProjectPlan", description = "프로젝트 고도화 계획 제안") diff --git a/backend/src/main/java/com/graphy/backend/domain/project/domain/Project.java b/backend/src/main/java/com/graphy/backend/domain/project/domain/Project.java index 6f591d43..0283ea07 100644 --- a/backend/src/main/java/com/graphy/backend/domain/project/domain/Project.java +++ b/backend/src/main/java/com/graphy/backend/domain/project/domain/Project.java @@ -53,10 +53,14 @@ public class Project extends BaseEntity { private String thumbNail; - @Column(nullable = true) + @Column(nullable = false) @ColumnDefault("0") private int likeCount = 0; + @Column(nullable = false) + @ColumnDefault("0") + private int viewCount = 0; + public void updateProject(String projectName, String content, String description, Tags tags, String thumbNail) { @@ -71,6 +75,9 @@ public void updateProject(String projectName, String content, public void updateLikeCount(int amount) { this.likeCount += amount; } + public void addViewCount() { + this.viewCount++; + } public void addTag(Tags tags) { projectTags.add(this, tags); diff --git a/backend/src/main/java/com/graphy/backend/domain/project/service/ProjectService.java b/backend/src/main/java/com/graphy/backend/domain/project/service/ProjectService.java index dafc4a3f..c1954dc3 100644 --- a/backend/src/main/java/com/graphy/backend/domain/project/service/ProjectService.java +++ b/backend/src/main/java/com/graphy/backend/domain/project/service/ProjectService.java @@ -21,7 +21,6 @@ import com.graphy.backend.global.error.exception.LongRequestException; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -29,6 +28,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; @@ -36,7 +37,8 @@ import static com.graphy.backend.global.config.ChatGPTConfig.MAX_REQUEST_TOKEN; -@Service @Slf4j +@Service +@Transactional(readOnly = true) @RequiredArgsConstructor(access = AccessLevel.PROTECTED) public class ProjectService { private final ProjectRepository projectRepository; @@ -94,14 +96,44 @@ public UpdateProjectResponse modifyProject(Long projectId, UpdateProjectRequest } public GetProjectDetailResponse findProjectById(Long projectId) { - Project project = projectRepository.findById(projectId) - .orElseThrow(() -> new EmptyResultException(ErrorCode.PROJECT_DELETED_OR_NOT_EXIST)); - + Project project = this.getProjectById(projectId); List comments = commentService.findCommentListWithMasking(projectId); - return GetProjectDetailResponse.of(project, comments); } + @Transactional + public Cookie addViewCount(HttpServletRequest request, Long projectId) { + Project project = this.getProjectById(projectId); + + Cookie[] cookies = request.getCookies(); + Cookie oldCookie = this.findCookie(cookies, "View_Count"); + + if (oldCookie != null) { + if (!oldCookie.getValue().contains("[" + projectId + "]")) { + oldCookie.setValue(oldCookie.getValue() + "[" + projectId + "]"); + project.addViewCount(); + } + oldCookie.setPath("/"); + return oldCookie; + } else { + Cookie newCookie = new Cookie("View_Count", "[" + projectId + "]"); + newCookie.setPath("/"); + project.addViewCount(); + return newCookie; + } + } + + private Cookie findCookie(Cookie[] cookies, String cookieName) { + if (cookies != null) { + for (Cookie cookie : cookies) { + if (cookieName.equals(cookie.getName())) { + return cookie; + } + } + } + return null; + } + public List findProjectList(GetProjectsRequest dto, Pageable pageable) { Page projects = projectRepository.searchProjectsWith(pageable, dto.getProjectName(), dto.getContent()); return GetProjectResponse.listOf(projects).getContent(); @@ -144,9 +176,7 @@ public CompletableFuture getProjectPlanAsync(String prompt) { } private void GptApiCall(GptCompletionRequest request, Consumer callback) { - log.info("비동기 작업 시작"); GptCompletionResponse result = gptChatRestService.completion(request); - log.info("비동기 작업 완료"); String response = result.getMessages().get(0).getText() .replace("\n", " ").replace("\n\n", " "); callback.accept(response); From f0fa6a32b0202d55f92f2fbde7953e1e3eac8d1f Mon Sep 17 00:00:00 2001 From: HaLin Kim Date: Sat, 14 Oct 2023 09:52:30 +0900 Subject: [PATCH 05/11] =?UTF-8?q?chore:=20pagerequest=20=EC=9C=84=EC=B9=98?= =?UTF-8?q?=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/{bookmark => comment}/domain/Bookmark.java | 2 +- .../backend/domain/job/controller/JobController.java | 9 ++++++--- .../domain/message/controller/MessageController.java | 2 +- .../notification/controller/NotificationController.java | 2 +- .../domain/notification/service/NotificationService.java | 2 +- .../recruitment/controller/RecruitmentController.java | 2 +- .../backend/global/common/{ => dto}/PageRequest.java | 2 +- .../domain/project/service/ProjectServiceTest.java | 2 +- 8 files changed, 13 insertions(+), 10 deletions(-) rename backend/src/main/java/com/graphy/backend/domain/{bookmark => comment}/domain/Bookmark.java (92%) rename backend/src/main/java/com/graphy/backend/global/common/{ => dto}/PageRequest.java (96%) diff --git a/backend/src/main/java/com/graphy/backend/domain/bookmark/domain/Bookmark.java b/backend/src/main/java/com/graphy/backend/domain/comment/domain/Bookmark.java similarity index 92% rename from backend/src/main/java/com/graphy/backend/domain/bookmark/domain/Bookmark.java rename to backend/src/main/java/com/graphy/backend/domain/comment/domain/Bookmark.java index 42fd8fb7..62e74329 100644 --- a/backend/src/main/java/com/graphy/backend/domain/bookmark/domain/Bookmark.java +++ b/backend/src/main/java/com/graphy/backend/domain/comment/domain/Bookmark.java @@ -1,4 +1,4 @@ -package com.graphy.backend.domain.bookmark.domain; +package com.graphy.backend.domain.comment.domain; import com.graphy.backend.domain.member.domain.Member; import com.graphy.backend.domain.recruitment.domain.Recruitment; diff --git a/backend/src/main/java/com/graphy/backend/domain/job/controller/JobController.java b/backend/src/main/java/com/graphy/backend/domain/job/controller/JobController.java index 12e5314e..1bae4526 100644 --- a/backend/src/main/java/com/graphy/backend/domain/job/controller/JobController.java +++ b/backend/src/main/java/com/graphy/backend/domain/job/controller/JobController.java @@ -2,19 +2,22 @@ import com.graphy.backend.domain.job.dto.response.GetJobResponse; import com.graphy.backend.domain.job.service.JobService; -import com.graphy.backend.global.common.PageRequest; +import com.graphy.backend.global.common.dto.PageRequest; import com.graphy.backend.global.error.ErrorCode; import com.graphy.backend.global.error.exception.EmptyResultException; import com.graphy.backend.global.result.ResultCode; import com.graphy.backend.global.result.ResultResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; -import java.util.List; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; @Tag(name = "EmploymentController", description = "신규 채용 공고 API") @RestController diff --git a/backend/src/main/java/com/graphy/backend/domain/message/controller/MessageController.java b/backend/src/main/java/com/graphy/backend/domain/message/controller/MessageController.java index 14f1b0d4..c8e8ae38 100644 --- a/backend/src/main/java/com/graphy/backend/domain/message/controller/MessageController.java +++ b/backend/src/main/java/com/graphy/backend/domain/message/controller/MessageController.java @@ -6,7 +6,7 @@ import com.graphy.backend.domain.message.dto.response.GetMessageDetailResponse; import com.graphy.backend.domain.message.dto.response.GetMessageResponse; import com.graphy.backend.domain.message.service.MessageService; -import com.graphy.backend.global.common.PageRequest; +import com.graphy.backend.global.common.dto.PageRequest; import com.graphy.backend.global.result.ResultCode; import com.graphy.backend.global.result.ResultResponse; import io.swagger.v3.oas.annotations.Operation; diff --git a/backend/src/main/java/com/graphy/backend/domain/notification/controller/NotificationController.java b/backend/src/main/java/com/graphy/backend/domain/notification/controller/NotificationController.java index 695f0232..22ca15c2 100644 --- a/backend/src/main/java/com/graphy/backend/domain/notification/controller/NotificationController.java +++ b/backend/src/main/java/com/graphy/backend/domain/notification/controller/NotificationController.java @@ -4,7 +4,7 @@ import com.graphy.backend.domain.member.domain.Member; import com.graphy.backend.domain.notification.dto.response.GetNotificationResponse; import com.graphy.backend.domain.notification.service.NotificationService; -import com.graphy.backend.global.common.PageRequest; +import com.graphy.backend.global.common.dto.PageRequest; import com.graphy.backend.global.result.ResultCode; import com.graphy.backend.global.result.ResultResponse; import io.swagger.v3.oas.annotations.Operation; diff --git a/backend/src/main/java/com/graphy/backend/domain/notification/service/NotificationService.java b/backend/src/main/java/com/graphy/backend/domain/notification/service/NotificationService.java index 12aa204d..4d4bae85 100644 --- a/backend/src/main/java/com/graphy/backend/domain/notification/service/NotificationService.java +++ b/backend/src/main/java/com/graphy/backend/domain/notification/service/NotificationService.java @@ -6,7 +6,7 @@ import com.graphy.backend.domain.notification.dto.NotificationDto; import com.graphy.backend.domain.notification.dto.response.GetNotificationResponse; import com.graphy.backend.domain.notification.repository.NotificationRepository; -import com.graphy.backend.global.common.PageRequest; +import com.graphy.backend.global.common.dto.PageRequest; import com.graphy.backend.global.error.exception.BusinessException; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; diff --git a/backend/src/main/java/com/graphy/backend/domain/recruitment/controller/RecruitmentController.java b/backend/src/main/java/com/graphy/backend/domain/recruitment/controller/RecruitmentController.java index 8090b454..88c08635 100644 --- a/backend/src/main/java/com/graphy/backend/domain/recruitment/controller/RecruitmentController.java +++ b/backend/src/main/java/com/graphy/backend/domain/recruitment/controller/RecruitmentController.java @@ -9,7 +9,7 @@ import com.graphy.backend.domain.recruitment.dto.response.GetRecruitmentDetailResponse; import com.graphy.backend.domain.recruitment.dto.response.GetRecruitmentResponse; import com.graphy.backend.domain.recruitment.service.RecruitmentService; -import com.graphy.backend.global.common.PageRequest; +import com.graphy.backend.global.common.dto.PageRequest; import com.graphy.backend.global.result.ResultCode; import com.graphy.backend.global.result.ResultResponse; import io.swagger.v3.oas.annotations.Operation; diff --git a/backend/src/main/java/com/graphy/backend/global/common/PageRequest.java b/backend/src/main/java/com/graphy/backend/global/common/dto/PageRequest.java similarity index 96% rename from backend/src/main/java/com/graphy/backend/global/common/PageRequest.java rename to backend/src/main/java/com/graphy/backend/global/common/dto/PageRequest.java index e46a8995..13d8dd2f 100644 --- a/backend/src/main/java/com/graphy/backend/global/common/PageRequest.java +++ b/backend/src/main/java/com/graphy/backend/global/common/dto/PageRequest.java @@ -1,4 +1,4 @@ -package com.graphy.backend.global.common; +package com.graphy.backend.global.common.dto; import com.graphy.backend.global.error.exception.BusinessException; import org.springframework.data.domain.Sort; diff --git a/backend/src/test/java/com/graphy/backend/domain/project/service/ProjectServiceTest.java b/backend/src/test/java/com/graphy/backend/domain/project/service/ProjectServiceTest.java index d388ea59..4375500e 100644 --- a/backend/src/test/java/com/graphy/backend/domain/project/service/ProjectServiceTest.java +++ b/backend/src/test/java/com/graphy/backend/domain/project/service/ProjectServiceTest.java @@ -16,7 +16,7 @@ import com.graphy.backend.domain.project.dto.response.GetProjectResponse; import com.graphy.backend.domain.project.dto.response.UpdateProjectResponse; import com.graphy.backend.domain.project.repository.ProjectRepository; -import com.graphy.backend.global.common.PageRequest; +import com.graphy.backend.global.common.dto.PageRequest; import com.graphy.backend.global.error.ErrorCode; import com.graphy.backend.global.error.exception.EmptyResultException; import com.graphy.backend.test.MockTest; From d0c9ca8d9255ca78c55eeb1c054e4c682fc968c2 Mon Sep 17 00:00:00 2001 From: HaLin Kim Date: Thu, 26 Oct 2023 13:29:32 +0900 Subject: [PATCH 06/11] =?UTF-8?q?fix:=20schema.sql=EC=97=90=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=82=AC=ED=95=AD=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/test/resources/schema.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/test/resources/schema.sql b/backend/src/test/resources/schema.sql index 93578f08..16f92c3c 100644 --- a/backend/src/test/resources/schema.sql +++ b/backend/src/test/resources/schema.sql @@ -45,6 +45,7 @@ create table project content longtext null, description varchar(255) null, like_count int default 0 null, + view_count int default 0 null, project_name varchar(255) not null, thumb_nail varchar(255) null, member_id bigint not null, From 882d50cebcfb1645c8ee8442a29bd75ca26c1f2e Mon Sep 17 00:00:00 2001 From: youKeon Date: Thu, 26 Oct 2023 22:07:00 +0900 Subject: [PATCH 07/11] =?UTF-8?q?feat:=20=EC=A1=B0=ED=9A=8C=20=EC=88=98=20?= =?UTF-8?q?=EA=B8=B0=EC=A4=80=20Top10=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project/controller/ProjectController.java | 12 ++++--- .../response/GetProjectRankingResponse.java | 27 +++++++++++++++ .../project/service/ProjectService.java | 27 +++++++++++++++ .../backend/global/config/RedisConfig.java | 33 +++++++++++++++++++ 4 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 backend/src/main/java/com/graphy/backend/domain/project/dto/response/GetProjectRankingResponse.java diff --git a/backend/src/main/java/com/graphy/backend/domain/project/controller/ProjectController.java b/backend/src/main/java/com/graphy/backend/domain/project/controller/ProjectController.java index 7534afc5..504069de 100644 --- a/backend/src/main/java/com/graphy/backend/domain/project/controller/ProjectController.java +++ b/backend/src/main/java/com/graphy/backend/domain/project/controller/ProjectController.java @@ -6,10 +6,7 @@ import com.graphy.backend.domain.project.dto.request.GetProjectPlanRequest; import com.graphy.backend.domain.project.dto.request.GetProjectsRequest; import com.graphy.backend.domain.project.dto.request.UpdateProjectRequest; -import com.graphy.backend.domain.project.dto.response.CreateProjectResponse; -import com.graphy.backend.domain.project.dto.response.GetProjectDetailResponse; -import com.graphy.backend.domain.project.dto.response.GetProjectResponse; -import com.graphy.backend.domain.project.dto.response.UpdateProjectResponse; +import com.graphy.backend.domain.project.dto.response.*; import com.graphy.backend.domain.project.service.ProjectService; import com.graphy.backend.global.common.dto.PageRequest; import com.graphy.backend.global.error.ErrorCode; @@ -104,6 +101,13 @@ public ResponseEntity projectDetails(@PathVariable Long projectI .body(ResultResponse.of(ResultCode.PROJECT_GET_SUCCESS, result)); } + @Operation(summary = "findProjectRank", description = "프로젝트 랭킹 조회") + @GetMapping("/rank") + public ResponseEntity projectRankList() { + List result = projectService.findProjectRank(); + return ResponseEntity.ok(ResultResponse.of(ResultCode.PROJECT_GET_SUCCESS, result)); + } + @Operation(summary = "getProjectPlan", description = "프로젝트 고도화 계획 제안") @PostMapping("/plans") public ResponseEntity projectPlanDetails(final @RequestBody GetProjectPlanRequest getPlanRequest, @CurrentUser Member loginUser) throws ExecutionException, InterruptedException { diff --git a/backend/src/main/java/com/graphy/backend/domain/project/dto/response/GetProjectRankingResponse.java b/backend/src/main/java/com/graphy/backend/domain/project/dto/response/GetProjectRankingResponse.java new file mode 100644 index 00000000..52887b93 --- /dev/null +++ b/backend/src/main/java/com/graphy/backend/domain/project/dto/response/GetProjectRankingResponse.java @@ -0,0 +1,27 @@ +package com.graphy.backend.domain.project.dto.response; + +import com.graphy.backend.domain.project.domain.Project; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GetProjectRankingResponse { + private Long id; + private String projectName; + private int viewCount; + private int likeCount; + + public static GetProjectRankingResponse from(Project project) { + return GetProjectRankingResponse.builder() + .id(project.getId()) + .projectName(project.getProjectName()) + .viewCount(project.getViewCount()) + .likeCount(project.getLikeCount()) + .build(); + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/graphy/backend/domain/project/service/ProjectService.java b/backend/src/main/java/com/graphy/backend/domain/project/service/ProjectService.java index 2b9d2c8f..97dd86b8 100644 --- a/backend/src/main/java/com/graphy/backend/domain/project/service/ProjectService.java +++ b/backend/src/main/java/com/graphy/backend/domain/project/service/ProjectService.java @@ -25,6 +25,8 @@ import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ZSetOperations; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -32,6 +34,8 @@ import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import java.util.List; +import java.util.Objects; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -50,6 +54,8 @@ public class ProjectService { private final TagService tagService; private final GPTChatRestService gptChatRestService; private final TagRepository tagRepository; + private final RedisTemplate redisTemplate; + private final String KEY = "ranking"; // @PostConstruct // public void initTag() throws IOException { @@ -114,6 +120,7 @@ public Cookie addViewCount(HttpServletRequest request, Long projectId) { if (!oldCookie.getValue().contains("[" + projectId + "]")) { oldCookie.setValue(oldCookie.getValue() + "[" + projectId + "]"); project.addViewCount(); + redisTemplate.opsForZSet().incrementScore(KEY, projectId, 1); } oldCookie.setPath("/"); return oldCookie; @@ -121,6 +128,7 @@ public Cookie addViewCount(HttpServletRequest request, Long projectId) { Cookie newCookie = new Cookie("View_Count", "[" + projectId + "]"); newCookie.setPath("/"); project.addViewCount(); + redisTemplate.opsForZSet().incrementScore(KEY, projectId, 1); return newCookie; } } @@ -203,4 +211,23 @@ public String getPrompt(final GetProjectPlanRequest request){ return techStacks + "를 이용해" + request.getTopic() +"를 개발 중이고, 현재" + features + "까지 기능 구현한 상태에서 고도화된 기능과 " + plans + "을 사용한 고도화 방안을 알려줘"; } + + public List findProjectRank() { + Set> projectRanking = getProjectRank(); + + return projectRanking.stream() + .map(this::getProjectFromTypedTuple) + .map(GetProjectRankingResponse::from) + .collect(Collectors.toList()); + } + + private Set> getProjectRank() { + ZSetOperations ZSetOperations = redisTemplate.opsForZSet(); + return ZSetOperations.reverseRangeWithScores(KEY, 0, 9); + } + + private Project getProjectFromTypedTuple(ZSetOperations.TypedTuple tuple) { + return projectRepository.findById(Objects.requireNonNull(tuple.getValue())) + .orElseThrow(() -> new EmptyResultException(ErrorCode.PROJECT_DELETED_OR_NOT_EXIST)); + } } \ No newline at end of file diff --git a/backend/src/main/java/com/graphy/backend/global/config/RedisConfig.java b/backend/src/main/java/com/graphy/backend/global/config/RedisConfig.java index 84315235..ddc4037c 100644 --- a/backend/src/main/java/com/graphy/backend/global/config/RedisConfig.java +++ b/backend/src/main/java/com/graphy/backend/global/config/RedisConfig.java @@ -1,10 +1,18 @@ package com.graphy.backend.global.config; + import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.data.redis.serializer.StringRedisSerializer; @EnableRedisRepositories @Configuration @@ -20,4 +28,29 @@ public class RedisConfig { public RedisConnectionFactory redisConnectionFactory() { return new LettuceConnectionFactory(redisHost, redisPort); } + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){ + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Long.class)); + return redisTemplate; + } + + @Bean + public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) { + RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() + .serializeKeysWith(RedisSerializationContext + .SerializationPair.fromSerializer(new StringRedisSerializer())) + .serializeValuesWith(RedisSerializationContext + .SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); + + + return RedisCacheManager + .RedisCacheManagerBuilder + .fromConnectionFactory(redisConnectionFactory) + .cacheDefaults(redisCacheConfiguration) + .build(); + } } \ No newline at end of file From 4b983a6e480473b129c28c5ab9eaf487dcea7a0a Mon Sep 17 00:00:00 2001 From: youKeon Date: Fri, 27 Oct 2023 03:50:28 +0900 Subject: [PATCH 08/11] =?UTF-8?q?feat:=20=EC=9D=B8=EA=B8=B0=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=EC=A0=9D=ED=8A=B8=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project/controller/ProjectController.java | 2 +- .../project/service/ProjectService.java | 44 +++++++++++++------ .../backend/global/config/CacheConfig.java | 9 ++++ .../backend/global/config/RedisConfig.java | 10 +++++ .../backend/global/config/SecurityConfig.java | 1 + 5 files changed, 52 insertions(+), 14 deletions(-) create mode 100644 backend/src/main/java/com/graphy/backend/global/config/CacheConfig.java diff --git a/backend/src/main/java/com/graphy/backend/domain/project/controller/ProjectController.java b/backend/src/main/java/com/graphy/backend/domain/project/controller/ProjectController.java index 504069de..124d64d8 100644 --- a/backend/src/main/java/com/graphy/backend/domain/project/controller/ProjectController.java +++ b/backend/src/main/java/com/graphy/backend/domain/project/controller/ProjectController.java @@ -104,7 +104,7 @@ public ResponseEntity projectDetails(@PathVariable Long projectI @Operation(summary = "findProjectRank", description = "프로젝트 랭킹 조회") @GetMapping("/rank") public ResponseEntity projectRankList() { - List result = projectService.findProjectRank(); + List result = projectService.findTopRankingProjectList(); return ResponseEntity.ok(ResultResponse.of(ResultCode.PROJECT_GET_SUCCESS, result)); } diff --git a/backend/src/main/java/com/graphy/backend/domain/project/service/ProjectService.java b/backend/src/main/java/com/graphy/backend/domain/project/service/ProjectService.java index 97dd86b8..44114db8 100644 --- a/backend/src/main/java/com/graphy/backend/domain/project/service/ProjectService.java +++ b/backend/src/main/java/com/graphy/backend/domain/project/service/ProjectService.java @@ -1,5 +1,6 @@ package com.graphy.backend.domain.project.service; +import com.fasterxml.jackson.databind.ObjectMapper; import com.graphy.backend.domain.comment.dto.response.GetCommentWithMaskingResponse; import com.graphy.backend.domain.comment.service.CommentService; import com.graphy.backend.domain.member.domain.Member; @@ -22,21 +23,23 @@ import com.graphy.backend.global.error.exception.LongRequestException; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.Cacheable; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ZSetOperations; import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import java.util.List; -import java.util.Objects; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -55,7 +58,13 @@ public class ProjectService { private final GPTChatRestService gptChatRestService; private final TagRepository tagRepository; private final RedisTemplate redisTemplate; - private final String KEY = "ranking"; + private final RedisTemplate redisRankingTemplate; + private final String RANKING_KEY = "ranking"; + private final String TOP_RANKING_PROJECT_KEY = "topRankingProject"; + private final int START_RANKING = 0; + private final int END_RANKING = 9; + ObjectMapper objectMapper = new ObjectMapper(); + // @PostConstruct // public void initTag() throws IOException { @@ -120,7 +129,7 @@ public Cookie addViewCount(HttpServletRequest request, Long projectId) { if (!oldCookie.getValue().contains("[" + projectId + "]")) { oldCookie.setValue(oldCookie.getValue() + "[" + projectId + "]"); project.addViewCount(); - redisTemplate.opsForZSet().incrementScore(KEY, projectId, 1); + redisTemplate.opsForZSet().incrementScore(RANKING_KEY, projectId, 1); } oldCookie.setPath("/"); return oldCookie; @@ -128,7 +137,7 @@ public Cookie addViewCount(HttpServletRequest request, Long projectId) { Cookie newCookie = new Cookie("View_Count", "[" + projectId + "]"); newCookie.setPath("/"); project.addViewCount(); - redisTemplate.opsForZSet().incrementScore(KEY, projectId, 1); + redisTemplate.opsForZSet().incrementScore(RANKING_KEY, projectId, 1); return newCookie; } } @@ -212,22 +221,31 @@ public String getPrompt(final GetProjectPlanRequest request){ + features + "까지 기능 구현한 상태에서 고도화된 기능과 " + plans + "을 사용한 고도화 방안을 알려줘"; } - public List findProjectRank() { - Set> projectRanking = getProjectRank(); + @Cacheable(value = "topRankingProjects") + public List findTopRankingProjectList() { + return redisRankingTemplate.opsForList().range(TOP_RANKING_PROJECT_KEY, START_RANKING, END_RANKING); + } - return projectRanking.stream() - .map(this::getProjectFromTypedTuple) - .map(GetProjectRankingResponse::from) + @Scheduled(cron = "0 0 6 ? * MON", zone = "Asia/Seoul") + protected void initializeProjectRanking() { + List projectIds = getProjectRank().stream() + .map(ZSetOperations.TypedTuple::getValue) .collect(Collectors.toList()); + + getRankedProjectListById(projectIds).stream() + .map(GetProjectRankingResponse::from) + .forEach(e -> { + redisRankingTemplate.opsForList().rightPush(TOP_RANKING_PROJECT_KEY, e); + }); + redisRankingTemplate.expire(TOP_RANKING_PROJECT_KEY, 7, TimeUnit.DAYS); } private Set> getProjectRank() { ZSetOperations ZSetOperations = redisTemplate.opsForZSet(); - return ZSetOperations.reverseRangeWithScores(KEY, 0, 9); + return ZSetOperations.reverseRangeWithScores(RANKING_KEY, START_RANKING, END_RANKING); } - private Project getProjectFromTypedTuple(ZSetOperations.TypedTuple tuple) { - return projectRepository.findById(Objects.requireNonNull(tuple.getValue())) - .orElseThrow(() -> new EmptyResultException(ErrorCode.PROJECT_DELETED_OR_NOT_EXIST)); + private List getRankedProjectListById(List projectIds) { + return projectRepository.findAllById(projectIds); } } \ No newline at end of file diff --git a/backend/src/main/java/com/graphy/backend/global/config/CacheConfig.java b/backend/src/main/java/com/graphy/backend/global/config/CacheConfig.java new file mode 100644 index 00000000..f7e8e2cf --- /dev/null +++ b/backend/src/main/java/com/graphy/backend/global/config/CacheConfig.java @@ -0,0 +1,9 @@ +package com.graphy.backend.global.config; + +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Configuration; + +@EnableCaching +@Configuration +public class CacheConfig { +} diff --git a/backend/src/main/java/com/graphy/backend/global/config/RedisConfig.java b/backend/src/main/java/com/graphy/backend/global/config/RedisConfig.java index ddc4037c..85397537 100644 --- a/backend/src/main/java/com/graphy/backend/global/config/RedisConfig.java +++ b/backend/src/main/java/com/graphy/backend/global/config/RedisConfig.java @@ -1,5 +1,6 @@ package com.graphy.backend.global.config; +import com.graphy.backend.domain.project.dto.response.GetProjectRankingResponse; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -38,6 +39,15 @@ public RedisTemplate redisTemplate(RedisConnectionFactory redisCon return redisTemplate; } + @Bean + public RedisTemplate redisRankDtoTemplate(RedisConnectionFactory redisConnectionFactory){ + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(GetProjectRankingResponse.class)); + return redisTemplate; + } + @Bean public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) { RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() diff --git a/backend/src/main/java/com/graphy/backend/global/config/SecurityConfig.java b/backend/src/main/java/com/graphy/backend/global/config/SecurityConfig.java index fba011cf..82479128 100644 --- a/backend/src/main/java/com/graphy/backend/global/config/SecurityConfig.java +++ b/backend/src/main/java/com/graphy/backend/global/config/SecurityConfig.java @@ -39,6 +39,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws "/api/v1/auth/signin", "/api/v1/auth/logout", "/api/v1/projects", + "/api/v1/projects/rank", "/api/v1/members/**", "/swagger-ui/**").permitAll() .antMatchers(HttpMethod.GET, "/api/v1/projects/{projectId}").permitAll() From dff2bde61227ef15ca112d587c0dce853c92f697 Mon Sep 17 00:00:00 2001 From: youKeon Date: Fri, 27 Oct 2023 21:12:15 +0900 Subject: [PATCH 09/11] =?UTF-8?q?feat:=20=EC=88=9C=EC=9C=84=EA=B6=8C=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=EC=A0=9D=ED=8A=B8=20=EC=88=98=EC=A0=95/?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EC=8B=9C=20Redis=EC=97=90=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/build.gradle | 5 ++ .../GetCommentWithMaskingResponse.java | 8 +- .../dto/response/GetMemberResponse.java | 4 +- .../project/controller/ProjectController.java | 7 +- .../domain/project/domain/Project.java | 13 ++- .../response/GetProjectDetailResponse.java | 16 +++- .../project/service/ProjectService.java | 86 +++++++++++++------ .../backend/global/config/RedisConfig.java | 11 ++- 8 files changed, 106 insertions(+), 44 deletions(-) diff --git a/backend/build.gradle b/backend/build.gradle index a7739985..e9cdb512 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -97,6 +97,11 @@ dependencies { // Redis implementation 'org.springframework.boot:spring-boot-starter-data-redis:2.3.1.RELEASE' + // LocalDataTime 역직렬화 문제 해결 패키지 + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' + implementation 'com.fasterxml.jackson.core:jackson-databind' + + } tasks.named('test') { diff --git a/backend/src/main/java/com/graphy/backend/domain/comment/dto/response/GetCommentWithMaskingResponse.java b/backend/src/main/java/com/graphy/backend/domain/comment/dto/response/GetCommentWithMaskingResponse.java index f110413c..762a5f80 100644 --- a/backend/src/main/java/com/graphy/backend/domain/comment/dto/response/GetCommentWithMaskingResponse.java +++ b/backend/src/main/java/com/graphy/backend/domain/comment/dto/response/GetCommentWithMaskingResponse.java @@ -1,7 +1,10 @@ package com.graphy.backend.domain.comment.dto.response; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import com.querydsl.core.annotations.QueryProjection; -import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -14,6 +17,9 @@ public class GetCommentWithMaskingResponse { private String content; private Long commentId; + + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) private LocalDateTime createdAt; private String nickname; private Long childCount; diff --git a/backend/src/main/java/com/graphy/backend/domain/member/dto/response/GetMemberResponse.java b/backend/src/main/java/com/graphy/backend/domain/member/dto/response/GetMemberResponse.java index 360e249d..ec129461 100644 --- a/backend/src/main/java/com/graphy/backend/domain/member/dto/response/GetMemberResponse.java +++ b/backend/src/main/java/com/graphy/backend/domain/member/dto/response/GetMemberResponse.java @@ -3,11 +3,13 @@ import com.graphy.backend.domain.member.domain.Member; import lombok.*; +import java.io.Serializable; + @Getter @Builder @AllArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor -public class GetMemberResponse { +public class GetMemberResponse implements Serializable { private String nickname; private String email; diff --git a/backend/src/main/java/com/graphy/backend/domain/project/controller/ProjectController.java b/backend/src/main/java/com/graphy/backend/domain/project/controller/ProjectController.java index 124d64d8..acce67dd 100644 --- a/backend/src/main/java/com/graphy/backend/domain/project/controller/ProjectController.java +++ b/backend/src/main/java/com/graphy/backend/domain/project/controller/ProjectController.java @@ -6,7 +6,10 @@ import com.graphy.backend.domain.project.dto.request.GetProjectPlanRequest; import com.graphy.backend.domain.project.dto.request.GetProjectsRequest; import com.graphy.backend.domain.project.dto.request.UpdateProjectRequest; -import com.graphy.backend.domain.project.dto.response.*; +import com.graphy.backend.domain.project.dto.response.CreateProjectResponse; +import com.graphy.backend.domain.project.dto.response.GetProjectDetailResponse; +import com.graphy.backend.domain.project.dto.response.GetProjectResponse; +import com.graphy.backend.domain.project.dto.response.UpdateProjectResponse; import com.graphy.backend.domain.project.service.ProjectService; import com.graphy.backend.global.common.dto.PageRequest; import com.graphy.backend.global.error.ErrorCode; @@ -104,7 +107,7 @@ public ResponseEntity projectDetails(@PathVariable Long projectI @Operation(summary = "findProjectRank", description = "프로젝트 랭킹 조회") @GetMapping("/rank") public ResponseEntity projectRankList() { - List result = projectService.findTopRankingProjectList(); + List result = projectService.findTopRankingProjectList(); return ResponseEntity.ok(ResultResponse.of(ResultCode.PROJECT_GET_SUCCESS, result)); } diff --git a/backend/src/main/java/com/graphy/backend/domain/project/domain/Project.java b/backend/src/main/java/com/graphy/backend/domain/project/domain/Project.java index 0283ea07..160322be 100644 --- a/backend/src/main/java/com/graphy/backend/domain/project/domain/Project.java +++ b/backend/src/main/java/com/graphy/backend/domain/project/domain/Project.java @@ -2,6 +2,7 @@ import com.graphy.backend.domain.comment.domain.Comment; import com.graphy.backend.domain.member.domain.Member; +import com.graphy.backend.domain.project.dto.request.UpdateProjectRequest; import com.graphy.backend.global.common.BaseEntity; import lombok.AllArgsConstructor; import lombok.Builder; @@ -61,13 +62,11 @@ public class Project extends BaseEntity { @ColumnDefault("0") private int viewCount = 0; - public void updateProject(String projectName, String content, - String description, Tags tags, - String thumbNail) { - this.projectName = projectName; - this.content = content; - this.description = description; - this.thumbNail = thumbNail; + public void updateProject(UpdateProjectRequest dto, Tags tags) { + this.projectName = dto.getProjectName(); + this.content = dto.getContent(); + this.description = dto.getDescription(); + this.thumbNail = dto.getThumbNail(); projectTags.clear(); addTag(tags); } diff --git a/backend/src/main/java/com/graphy/backend/domain/project/dto/response/GetProjectDetailResponse.java b/backend/src/main/java/com/graphy/backend/domain/project/dto/response/GetProjectDetailResponse.java index 4e08a99d..ed952949 100644 --- a/backend/src/main/java/com/graphy/backend/domain/project/dto/response/GetProjectDetailResponse.java +++ b/backend/src/main/java/com/graphy/backend/domain/project/dto/response/GetProjectDetailResponse.java @@ -1,21 +1,29 @@ package com.graphy.backend.domain.project.dto.response; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import com.graphy.backend.domain.comment.dto.response.GetCommentWithMaskingResponse; import com.graphy.backend.domain.member.dto.response.GetMemberResponse; import com.graphy.backend.domain.project.domain.Project; -import lombok.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import java.io.Serializable; import java.time.LocalDateTime; import java.util.List; -import static com.graphy.backend.domain.member.dto.response.GetMemberResponse.*; +import static com.graphy.backend.domain.member.dto.response.GetMemberResponse.from; @Getter @Builder @NoArgsConstructor @AllArgsConstructor -public class GetProjectDetailResponse { +public class GetProjectDetailResponse implements Serializable { private Long id; @@ -29,6 +37,8 @@ public class GetProjectDetailResponse { private List commentsList; + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) private LocalDateTime createdAt; private List techTags; diff --git a/backend/src/main/java/com/graphy/backend/domain/project/service/ProjectService.java b/backend/src/main/java/com/graphy/backend/domain/project/service/ProjectService.java index 44114db8..1ec03503 100644 --- a/backend/src/main/java/com/graphy/backend/domain/project/service/ProjectService.java +++ b/backend/src/main/java/com/graphy/backend/domain/project/service/ProjectService.java @@ -1,6 +1,5 @@ package com.graphy.backend.domain.project.service; -import com.fasterxml.jackson.databind.ObjectMapper; import com.graphy.backend.domain.comment.dto.response.GetCommentWithMaskingResponse; import com.graphy.backend.domain.comment.service.CommentService; import com.graphy.backend.domain.member.domain.Member; @@ -23,10 +22,10 @@ import com.graphy.backend.global.error.exception.LongRequestException; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; -import org.springframework.cache.annotation.Cacheable; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ZSetOperations; import org.springframework.scheduling.annotation.Async; @@ -36,8 +35,9 @@ import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; +import java.util.ArrayList; import java.util.List; -import java.util.Set; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @@ -58,12 +58,12 @@ public class ProjectService { private final GPTChatRestService gptChatRestService; private final TagRepository tagRepository; private final RedisTemplate redisTemplate; - private final RedisTemplate redisRankingTemplate; + private final RedisTemplate redisRankingTemplate; private final String RANKING_KEY = "ranking"; private final String TOP_RANKING_PROJECT_KEY = "topRankingProject"; private final int START_RANKING = 0; private final int END_RANKING = 9; - ObjectMapper objectMapper = new ObjectMapper(); + private int NEXT_RANKING = END_RANKING; // @PostConstruct @@ -96,6 +96,7 @@ public CreateProjectResponse addProject(CreateProjectRequest dto, Member loginUs public void removeProject(Long projectId) { try { projectRepository.deleteById(projectId); + if (isProjectIdExistInRanking(projectId)) addNextRankingProject(projectId); } catch (EmptyResultDataAccessException e) { throw new EmptyResultException(ErrorCode.PROJECT_DELETED_OR_NOT_EXIST); } @@ -103,11 +104,12 @@ public void removeProject(Long projectId) { @Transactional public UpdateProjectResponse modifyProject(Long projectId, UpdateProjectRequest dto) { - Project project = projectRepository.findById(projectId).get(); + Project project = projectRepository.findById(projectId).orElseThrow(() -> new EmptyResultException(ErrorCode.PROJECT_DELETED_OR_NOT_EXIST)); projectTagService.removeProjectTag(project.getId()); Tags updatedTags = tagService.findTagListByName(dto.getTechTags()); - project.updateProject(dto.getProjectName(), dto.getContent(), dto.getDescription(), updatedTags, dto.getThumbNail()); + project.updateProject(dto, updatedTags); + if (isProjectIdExistInRanking(projectId)) modifyProjectInRanking(project); return UpdateProjectResponse.from(project); } @@ -221,31 +223,67 @@ public String getPrompt(final GetProjectPlanRequest request){ + features + "까지 기능 구현한 상태에서 고도화된 기능과 " + plans + "을 사용한 고도화 방안을 알려줘"; } - @Cacheable(value = "topRankingProjects") - public List findTopRankingProjectList() { - return redisRankingTemplate.opsForList().range(TOP_RANKING_PROJECT_KEY, START_RANKING, END_RANKING); + public List findTopRankingProjectList() { + HashOperations hashOperation = redisRankingTemplate.opsForHash(); + List topRankingProjectIdList = getTopRankingProjectIdList(); + + return topRankingProjectIdList.stream() + .map(projectId -> hashOperation.get(TOP_RANKING_PROJECT_KEY, projectId)) + .collect(Collectors.toList()); } + @Scheduled(cron = "0 0 6 ? * MON", zone = "Asia/Seoul") - protected void initializeProjectRanking() { - List projectIds = getProjectRank().stream() - .map(ZSetOperations.TypedTuple::getValue) - .collect(Collectors.toList()); + public void initializeProjectRanking() { + NEXT_RANKING = END_RANKING; + HashOperations hashOperation = redisRankingTemplate.opsForHash(); + List topRankingProjectList = getRankedProjectListById(getTopRankingProjectIdList()); - getRankedProjectListById(projectIds).stream() - .map(GetProjectRankingResponse::from) - .forEach(e -> { - redisRankingTemplate.opsForList().rightPush(TOP_RANKING_PROJECT_KEY, e); - }); + topRankingProjectList.forEach(project -> hashOperation.put(TOP_RANKING_PROJECT_KEY, project.getId(), project)); redisRankingTemplate.expire(TOP_RANKING_PROJECT_KEY, 7, TimeUnit.DAYS); } - private Set> getProjectRank() { - ZSetOperations ZSetOperations = redisTemplate.opsForZSet(); - return ZSetOperations.reverseRangeWithScores(RANKING_KEY, START_RANKING, END_RANKING); + private List getTopRankingProjectIdList() { + ZSetOperations zSetOperations = redisTemplate.opsForZSet(); + return new ArrayList<>(Objects.requireNonNull(zSetOperations.reverseRange(RANKING_KEY, START_RANKING, END_RANKING))); + } + + private List getRankedProjectListById(List projectIds) { + List projectList = projectRepository.findAllById(projectIds); + + return projectList.stream() + .map(project -> { + List comments = commentService.findCommentListWithMasking(project.getId()); + return GetProjectDetailResponse.of(project, comments); + }) + .collect(Collectors.toList()); + } + + private void addNextRankingProject(Long deletedProjectId) { + ZSetOperations zSetOperations = redisTemplate.opsForZSet(); + HashOperations hashOperation = redisRankingTemplate.opsForHash(); + + hashOperation.delete(TOP_RANKING_PROJECT_KEY, deletedProjectId); + zSetOperations.remove(RANKING_KEY, deletedProjectId); + + Long nextRankingProjectId = Objects.requireNonNull(zSetOperations.reverseRange(RANKING_KEY, NEXT_RANKING, NEXT_RANKING)) + .iterator() + .next(); + NEXT_RANKING++; + + GetProjectDetailResponse nextRankingProject = this.findProjectById(nextRankingProjectId); + hashOperation.put(TOP_RANKING_PROJECT_KEY, nextRankingProjectId, nextRankingProject); + } + + private void modifyProjectInRanking(Project project) { + HashOperations hashOperation = redisRankingTemplate.opsForHash(); + List comments = commentService.findCommentListWithMasking(project.getId()); + GetProjectDetailResponse updateProject = GetProjectDetailResponse.of(project, comments); + hashOperation.put(TOP_RANKING_PROJECT_KEY, project.getId(), updateProject); } - private List getRankedProjectListById(List projectIds) { - return projectRepository.findAllById(projectIds); + private boolean isProjectIdExistInRanking(Long projectId) { + HashOperations hashOperation = redisRankingTemplate.opsForHash(); + return hashOperation.hasKey(TOP_RANKING_PROJECT_KEY, projectId); } } \ No newline at end of file diff --git a/backend/src/main/java/com/graphy/backend/global/config/RedisConfig.java b/backend/src/main/java/com/graphy/backend/global/config/RedisConfig.java index 85397537..b2be364b 100644 --- a/backend/src/main/java/com/graphy/backend/global/config/RedisConfig.java +++ b/backend/src/main/java/com/graphy/backend/global/config/RedisConfig.java @@ -1,6 +1,6 @@ package com.graphy.backend.global.config; -import com.graphy.backend.domain.project.dto.response.GetProjectRankingResponse; +import com.graphy.backend.domain.project.dto.response.GetProjectDetailResponse; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -31,7 +31,7 @@ public RedisConnectionFactory redisConnectionFactory() { } @Bean - public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){ + public RedisTemplate redisProjectRankingTemplate(RedisConnectionFactory redisConnectionFactory){ RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer()); @@ -40,11 +40,11 @@ public RedisTemplate redisTemplate(RedisConnectionFactory redisCon } @Bean - public RedisTemplate redisRankDtoTemplate(RedisConnectionFactory redisConnectionFactory){ - RedisTemplate redisTemplate = new RedisTemplate<>(); + public RedisTemplate redisTopRankingProjectTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer()); - redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(GetProjectRankingResponse.class)); + redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(GetProjectDetailResponse.class)); return redisTemplate; } @@ -56,7 +56,6 @@ public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectio .serializeValuesWith(RedisSerializationContext .SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); - return RedisCacheManager .RedisCacheManagerBuilder .fromConnectionFactory(redisConnectionFactory) From 429c91962e06168452472e342a3a96bdf5fe5744 Mon Sep 17 00:00:00 2001 From: youKeon Date: Fri, 27 Oct 2023 23:00:02 +0900 Subject: [PATCH 10/11] =?UTF-8?q?test:=20CI=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/project/service/ProjectServiceTest.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/backend/src/test/java/com/graphy/backend/domain/project/service/ProjectServiceTest.java b/backend/src/test/java/com/graphy/backend/domain/project/service/ProjectServiceTest.java index 6bb0d6c7..b51e4950 100644 --- a/backend/src/test/java/com/graphy/backend/domain/project/service/ProjectServiceTest.java +++ b/backend/src/test/java/com/graphy/backend/domain/project/service/ProjectServiceTest.java @@ -30,6 +30,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.redis.core.HashOperations; import java.util.ArrayList; import java.util.Arrays; @@ -56,6 +57,8 @@ class ProjectServiceTest extends MockTest { @Mock private CustomUserDetailsService customUserDetailsService; + @Mock + HashOperations hashOperations; @InjectMocks private ProjectService projectService; @@ -86,14 +89,15 @@ void updateProject() throws Exception { Tag tag1 = Tag.builder().tech("Vue").build(); Tag tag2 = Tag.builder().tech("Java").build(); - //when when(projectRepository.findById(project.getId())).thenReturn(Optional.of(project)); when(tagService.findTagListByName(techTags)).thenReturn(new Tags(List.of(tag1, tag2))); + when(redisRankingTemplate.opsForHash()).thenReturn(hashOperations); UpdateProjectResponse result = projectService.modifyProject(project.getId(), request); - assertThat(result.getProjectName()).isEqualTo(project.getProjectName()); + //then + assertThat(result.getProjectName()).isEqualTo("afterUpdate"); // 수정된 부분 assertThat(result.getDescription()).isEqualTo(project.getDescription()); assertThat(result.getThumbNail()).isEqualTo(project.getThumbNail()); assertThat(result.getTechTags()).isEqualTo(new ArrayList<>(Arrays.asList("Vue", "Java"))); @@ -177,6 +181,7 @@ void getProjects() throws Exception { @DisplayName("프로젝트 삭제") void deleteProject() throws Exception { //when + when(redisRankingTemplate.opsForHash()).thenReturn(hashOperations); projectService.removeProject(1L); //then From bcb6e056ae52dd0a81072f81a0276fe123469be3 Mon Sep 17 00:00:00 2001 From: youKeon Date: Fri, 27 Oct 2023 23:05:53 +0900 Subject: [PATCH 11/11] =?UTF-8?q?test:=20CI=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/project/service/ProjectServiceTest.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/backend/src/test/java/com/graphy/backend/domain/project/service/ProjectServiceTest.java b/backend/src/test/java/com/graphy/backend/domain/project/service/ProjectServiceTest.java index b51e4950..a62a99f7 100644 --- a/backend/src/test/java/com/graphy/backend/domain/project/service/ProjectServiceTest.java +++ b/backend/src/test/java/com/graphy/backend/domain/project/service/ProjectServiceTest.java @@ -11,10 +11,7 @@ import com.graphy.backend.domain.project.dto.request.CreateProjectRequest; import com.graphy.backend.domain.project.dto.request.GetProjectsRequest; import com.graphy.backend.domain.project.dto.request.UpdateProjectRequest; -import com.graphy.backend.domain.project.dto.response.CreateProjectResponse; -import com.graphy.backend.domain.project.dto.response.GetProjectInfoResponse; -import com.graphy.backend.domain.project.dto.response.GetProjectResponse; -import com.graphy.backend.domain.project.dto.response.UpdateProjectResponse; +import com.graphy.backend.domain.project.dto.response.*; import com.graphy.backend.domain.project.repository.ProjectRepository; import com.graphy.backend.global.common.dto.PageRequest; import com.graphy.backend.global.error.ErrorCode; @@ -31,6 +28,7 @@ import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; import java.util.ArrayList; import java.util.Arrays; @@ -57,6 +55,10 @@ class ProjectServiceTest extends MockTest { @Mock private CustomUserDetailsService customUserDetailsService; + + @Mock + private RedisTemplate redisRankingTemplate; // RedisTemplate 모킹 + @Mock HashOperations hashOperations;