Skip to content

Commit

Permalink
Merge branch 'develop' into refactor#157
Browse files Browse the repository at this point in the history
  • Loading branch information
s-hwan authored Nov 13, 2024
2 parents cb39606 + c7f8043 commit 5fd745a
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.gamzabat.algohub.feature.notice.domain;

import com.gamzabat.algohub.feature.user.domain.User;

import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor
public class NoticeRead {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "notice_id")
private Notice notice;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;

@Builder
public NoticeRead(Notice notice, User user) {
this.notice = notice;
this.user = user;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,18 @@ public record GetNoticeResponse(String author,
String content,
String title,
String category,
String createAt) {
String createAt,
boolean isRead) {

public static GetNoticeResponse toDTO(Notice notice) {
public static GetNoticeResponse toDTO(Notice notice, boolean isRead) {
return GetNoticeResponse.builder()
.author(notice.getAuthor().getNickname())
.noticeId(notice.getId())
.title(notice.getTitle())
.content(notice.getContent())
.category(notice.getCategory())
.createAt(DateFormatUtil.formatDateTimeForNotice(notice.getCreatedAt()))
.isRead(isRead)
.build();

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.gamzabat.algohub.feature.notice.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.gamzabat.algohub.feature.notice.domain.Notice;
import com.gamzabat.algohub.feature.notice.domain.NoticeRead;
import com.gamzabat.algohub.feature.user.domain.User;

public interface NoticeReadRepository extends JpaRepository<NoticeRead, Long> {
boolean existsByNoticeAndUser(Notice notice, User user);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
import com.gamzabat.algohub.feature.group.studygroup.repository.GroupMemberRepository;
import com.gamzabat.algohub.feature.group.studygroup.repository.StudyGroupRepository;
import com.gamzabat.algohub.feature.notice.domain.Notice;
import com.gamzabat.algohub.feature.notice.domain.NoticeRead;
import com.gamzabat.algohub.feature.notice.dto.CreateNoticeRequest;
import com.gamzabat.algohub.feature.notice.dto.GetNoticeResponse;
import com.gamzabat.algohub.feature.notice.dto.UpdateNoticeRequest;
import com.gamzabat.algohub.feature.notice.exception.NoticeValidationException;
import com.gamzabat.algohub.feature.notice.repository.NoticeCommentRepository;
import com.gamzabat.algohub.feature.notice.repository.NoticeReadRepository;
import com.gamzabat.algohub.feature.notice.repository.NoticeRepository;
import com.gamzabat.algohub.feature.user.domain.User;

Expand All @@ -38,6 +40,7 @@ public class NoticeService {
private final NoticeCommentRepository noticeCommentRepository;
private final StudyGroupRepository studyGroupRepository;
private final GroupMemberRepository groupMemberRepository;
private final NoticeReadRepository noticeReadRepository;

@Transactional
public void createNotice(@AuthedUser User user, CreateNoticeRequest request) {
Expand Down Expand Up @@ -68,6 +71,8 @@ public GetNoticeResponse getNotice(@AuthedUser User user, Long noticeId) {
if (!groupMemberRepository.existsByUserAndStudyGroup(user, notice.getStudyGroup()))
throw new StudyGroupValidationException(HttpStatus.FORBIDDEN.value(), "참여하지 않은 스터디 그룹 입니다.");

markNoticeAsRead(user, notice);

log.info("success to get notice");
return GetNoticeResponse.builder()
.author(notice.getAuthor().getNickname())
Expand All @@ -76,6 +81,7 @@ public GetNoticeResponse getNotice(@AuthedUser User user, Long noticeId) {
.content(notice.getContent())
.category(notice.getCategory())
.createAt(DateFormatUtil.formatDateTimeForNotice(notice.getCreatedAt()))
.isRead(true)
.build();
}

Expand All @@ -87,7 +93,9 @@ public List<GetNoticeResponse> getNoticeList(@AuthedUser User user, Long studyGr
throw new GroupMemberValidationException(HttpStatus.FORBIDDEN.value(), "참여하지 않은 스터디 그룹입니다");

List<Notice> list = noticeRepository.findAllByStudyGroup(studyGroup);
List<GetNoticeResponse> result = list.stream().map(GetNoticeResponse::toDTO).toList();
List<GetNoticeResponse> result = list.stream().map(
notice -> GetNoticeResponse.toDTO(notice, noticeReadRepository.existsByNoticeAndUser(notice, user))
).toList();
log.info("success to get notice list");
return result;
}
Expand Down Expand Up @@ -123,4 +131,12 @@ private void validateStudyGroupExists(Notice notice) {
.orElseThrow(() -> new StudyGroupValidationException(HttpStatus.BAD_REQUEST.value(), "존재하지 않는 스터디 그룹입니다"));
}

private void markNoticeAsRead(User user, Notice notice) {
if (!noticeReadRepository.existsByNoticeAndUser(notice, user)) {
noticeReadRepository.save(
NoticeRead.builder().notice(notice).user(user).build()
);
}
log.info("success to read notice. userId: {}, noticeId: {}", user.getId(), notice.getId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/solution")
@RequestMapping("/api")
@Tag(name = "풀이 API", description = "문제 풀이 관련 API")
public class SolutionController {
private final SolutionService solutionService;

@GetMapping
@GetMapping("/solutions")
@Operation(summary = "풀이 목록 조회 API", description = "특정 문제에 대한 풀이를 모두 조회하는 API")
public ResponseEntity<Page<GetSolutionResponse>> getSolutionList(@AuthedUser User user,
@RequestParam Long problemId,
Expand All @@ -47,15 +47,15 @@ public ResponseEntity<Page<GetSolutionResponse>> getSolutionList(@AuthedUser Use
return ResponseEntity.ok().body(response);
}

@GetMapping("/{solutionId}")
@GetMapping("/solutions/{solutionId}")
@Operation(summary = "풀이 하나 조회 API", description = "특정 풀이 하나를 조회하는 API")
public ResponseEntity<GetSolutionResponse> getSolution(@AuthedUser User user,
@PathVariable Long solutionId) {
GetSolutionResponse response = solutionService.getSolution(user, solutionId);
return ResponseEntity.ok().body(response);
}

@PostMapping
@PostMapping("/solutions")
@Operation(summary = "풀이 생성 API")
public ResponseEntity<Void> createSolution(@Valid @RequestBody CreateSolutionRequest request, Errors errors) {
if (errors.hasErrors())
Expand All @@ -64,4 +64,17 @@ public ResponseEntity<Void> createSolution(@Valid @RequestBody CreateSolutionReq
return ResponseEntity.ok().build();
}

@GetMapping("/my-solutions")
@Operation(summary = "나의 풀이 목록 조회 API", description = "나의 풀이를 모두 조회하는 API")
public ResponseEntity<Page<GetSolutionResponse>> getSolutionList(@AuthedUser User user,
@RequestParam Long problemId,
@RequestParam(required = false) String language,
@RequestParam(required = false) String result,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
Pageable pageable = PageRequest.of(page, size);
Page<GetSolutionResponse> response = solutionService.getSolutionList(user, problemId, user.getNickname(),
language, result, pageable);
return ResponseEntity.ok().body(response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import com.gamzabat.algohub.feature.group.studygroup.repository.GroupMemberRepository;
import com.gamzabat.algohub.feature.group.studygroup.repository.StudyGroupRepository;
import com.gamzabat.algohub.feature.notification.enums.NotificationCategory;
import com.gamzabat.algohub.feature.notification.repository.NotificationSettingRepository;
import com.gamzabat.algohub.feature.notification.service.NotificationService;
import com.gamzabat.algohub.feature.problem.domain.Problem;
import com.gamzabat.algohub.feature.problem.repository.ProblemRepository;
Expand Down Expand Up @@ -54,7 +53,6 @@ public class SolutionService {
private final SolutionCommentRepository commentRepository;
private final RankingService rankingService;
private final RankingUpdateService rankingUpdateService;
private final NotificationSettingRepository notificationSettingRepository;

public Page<GetSolutionResponse> getSolutionList(User user, Long problemId, String nickname,
String language, String result, Pageable pageable) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ void getSolutionList_1() throws Exception {
when(solutionService.getSolutionList(any(User.class), anyLong(), isNull(), isNull(), isNull(),
any(Pageable.class))).thenReturn(pagedResponse);
// when, then
mockMvc.perform(get("/api/solution")
mockMvc.perform(get("/api/solutions")
.header("Authorization", token)
.param("problemId", String.valueOf(problemId)))
.andExpect(status().isOk())
Expand All @@ -107,7 +107,7 @@ void getSolutionListFailed_1() throws Exception {
any(Pageable.class)))
.thenThrow(new ProblemValidationException(HttpStatus.NOT_FOUND.value(), "존재하지 않는 문제 입니다."));
// when, then
mockMvc.perform(get("/api/solution")
mockMvc.perform(get("/api/solutions")
.header("Authorization", token)
.param("problemId", String.valueOf(problemId)))
.andExpect(status().isNotFound())
Expand All @@ -124,7 +124,7 @@ void getSolutionListFailed_2() throws Exception {
any(Pageable.class)))
.thenThrow(new StudyGroupValidationException(HttpStatus.NOT_FOUND.value(), "존재하지 않는 그룹 입니다."));
// when, then
mockMvc.perform(get("/api/solution")
mockMvc.perform(get("/api/solutions")
.header("Authorization", token)
.param("problemId", String.valueOf(problemId)))
.andExpect(status().isNotFound())
Expand All @@ -141,7 +141,7 @@ void getSolutionListFailed_3() throws Exception {
any(Pageable.class)))
.thenThrow(new GroupMemberValidationException(HttpStatus.FORBIDDEN.value(), "참여하지 않은 그룹 입니다."));
// when, then
mockMvc.perform(get("/api/solution")
mockMvc.perform(get("/api/solutions")
.header("Authorization", token)
.param("problemId", String.valueOf(problemId)))
.andExpect(status().isForbidden())
Expand All @@ -156,7 +156,7 @@ void getSolution_1() throws Exception {
GetSolutionResponse response = GetSolutionResponse.builder().build();
when(solutionService.getSolution(any(User.class), anyLong())).thenReturn(response);
// when, then
mockMvc.perform(get("/api/solution/{solutionId}", solutionId)
mockMvc.perform(get("/api/solutions/{solutionId}", solutionId)
.header("Authorization", token))
.andExpect(status().isOk())
.andExpect(content().string(objectMapper.writeValueAsString(response)));
Expand All @@ -170,7 +170,7 @@ void getSolutionFailed_1() throws Exception {
when(solutionService.getSolution(any(User.class), eq(solutionId)))
.thenThrow(new CannotFoundSolutionException("존재하지 않는 풀이 입니다."));
// when, then
mockMvc.perform(get("/api/solution/{solutionId}", solutionId)
mockMvc.perform(get("/api/solutions/{solutionId}", solutionId)
.header("Authorization", token))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.error").value("존재하지 않는 풀이 입니다."));
Expand All @@ -184,7 +184,7 @@ void getSolutionFailed_2() throws Exception {
when(solutionService.getSolution(any(User.class), eq(solutionId)))
.thenThrow(new CannotFoundSolutionException("존재하지 않는 풀이 입니다."));
// when, then
mockMvc.perform(get("/api/solution/{solutionId}", solutionId)
mockMvc.perform(get("/api/solutions/{solutionId}", solutionId)
.header("Authorization", token))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.error").value("존재하지 않는 풀이 입니다."));
Expand All @@ -198,7 +198,7 @@ void getSolutionFailed_3() throws Exception {
when(solutionService.getSolution(any(User.class), eq(solutionId)))
.thenThrow(new UserValidationException("해당 풀이를 확인 할 권한이 없습니다."));
// when, then
mockMvc.perform(get("/api/solution/{solutionId}", solutionId)
mockMvc.perform(get("/api/solutions/{solutionId}", solutionId)
.header("Authorization", token))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.error").value("해당 풀이를 확인 할 권한이 없습니다."));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@
import com.gamzabat.algohub.feature.group.studygroup.repository.GroupMemberRepository;
import com.gamzabat.algohub.feature.group.studygroup.repository.StudyGroupRepository;
import com.gamzabat.algohub.feature.notice.domain.Notice;
import com.gamzabat.algohub.feature.notice.domain.NoticeRead;
import com.gamzabat.algohub.feature.notice.dto.CreateNoticeRequest;
import com.gamzabat.algohub.feature.notice.dto.GetNoticeResponse;
import com.gamzabat.algohub.feature.notice.dto.UpdateNoticeRequest;
import com.gamzabat.algohub.feature.notice.exception.NoticeValidationException;
import com.gamzabat.algohub.feature.notice.repository.NoticeCommentRepository;
import com.gamzabat.algohub.feature.notice.repository.NoticeReadRepository;
import com.gamzabat.algohub.feature.notice.repository.NoticeRepository;
import com.gamzabat.algohub.feature.notice.service.NoticeService;
import com.gamzabat.algohub.feature.user.domain.User;
Expand All @@ -52,6 +54,8 @@ public class NoticeServiceTest {
GroupMemberRepository groupMemberRepository;
@Mock
private NoticeCommentRepository noticeCommentRepository;
@Mock
private NoticeReadRepository noticeReadRepository;
@Captor
private ArgumentCaptor<Notice> noticeCaptor;

Expand Down Expand Up @@ -183,6 +187,7 @@ void getNoticeSuccess_1() {
assertThat(response.category()).isEqualTo("category");
assertThat(response.createAt()).isEqualTo(DateFormatUtil.formatDateTimeForNotice(notice.getCreatedAt()));
assertThat(response.noticeId()).isEqualTo(1000L);
verify(noticeReadRepository, times(1)).save(any(NoticeRead.class));
}

@Test
Expand Down Expand Up @@ -247,6 +252,7 @@ void getNoticeListSuccess_1() {
assertThat(result.get(i).content()).isEqualTo("content" + i);
assertThat(result.get(i).title()).isEqualTo("title" + i);
assertThat(result.get(i).category()).isEqualTo("category" + i);
assertThat(result.get(i).isRead()).isFalse();
}
}

Expand Down

0 comments on commit 5fd745a

Please sign in to comment.