Skip to content

Commit

Permalink
시험 목록과 시험 상세 페이지에서 좋아요 수를 확인할 수 있다. (#30)
Browse files Browse the repository at this point in the history
* feat: 시험 목록, 나의 제출된 시험 조회 시 좋아요 수 포함

* refactor: 내가 제출한 시험에 좋아요 수 는 제거

* feat: 시험 상세 요약 조회 시 로그인 여부에 따라 좋아요 여부 조회

* refactor: ExamDetail과 ExamDetailSummary로 응답 분리

* feat: client like, unlike api 요청 구현

* refactor: 사용하지 않는 interface 제거

* feat: like, unlike mutation 구현

* feat: useExamLikeManager를 통한 좋아요 토글 hook 구현

* refactor: remove select updatedAt

* feat: add pagination query index

* refactor: ddl 컬럼 이름 변경 및 인덱스 수정

* refactor: 전체 시험 목록과 내 시험 목록 interface 분리

* refactor: exam querydsl leftjoin이 필요한 곳 수정

* refactor: exam querydsl submission 부분 join으로 변경

* fix: submitted exam ids group by exam id!

* refactor: 문항 수 아이콘으로 변경

* feat: useExamLikeeManager 낙관적 업데이트

* feat: 좋아요 invalidate queries에 디바운스 300ms

* feat: exam intro의 좋아요 버튼

* refactor: LikeService 코드 구조 변경
  • Loading branch information
alstn113 authored Jan 20, 2025
1 parent af6c417 commit 2b6204f
Show file tree
Hide file tree
Showing 37 changed files with 3,215 additions and 898 deletions.
6 changes: 5 additions & 1 deletion server/src/docs/asciidoc/exam.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ operation::exam-document-test/get-published-exam-summaries[]

operation::exam-document-test/get-my-exam-summaries[]

=== 시험 상세 요약 조회

operation::exam-document-test/get-exam-detail-summary[]

=== 시험 상세 조회

operation::exam-document-test/get-exam-detail[]
Expand All @@ -16,7 +20,7 @@ operation::exam-document-test/get-exam-detail[]

operation::exam-document-test/get-exam-detail-with-answers[]

=== 출제된 시험 요약 목록 조회
=== 내가 제출한 시험 요약 목록 조회

operation::exam-document-test/get-submitted-exam-summaries[]

Expand Down
12 changes: 12 additions & 0 deletions server/src/main/java/com/fluffy/DataInitializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
import com.fluffy.exam.domain.ExamRepository;
import com.fluffy.exam.domain.Question;
import com.fluffy.exam.domain.QuestionOption;
import com.fluffy.reaction.domain.LikeTarget;
import com.fluffy.reaction.domain.Reaction;
import com.fluffy.reaction.domain.ReactionRepository;
import com.fluffy.reaction.domain.ReactionType;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.ApplicationArguments;
Expand All @@ -23,6 +27,7 @@ public class DataInitializer implements ApplicationRunner {

private final MemberRepository memberRepository;
private final ExamRepository examRepository;
private final ReactionRepository reactionRepository;

@Override
public void run(ApplicationArguments args) {
Expand Down Expand Up @@ -288,5 +293,12 @@ private void init() {
));
exam6.publish();
examRepository.save(exam6);

List<Long> memberIds = List.of(member1.getId(), member2.getId(), member3.getId());
for (Long memberId : memberIds) {
reactionRepository.save(new Reaction(LikeTarget.EXAM.name(), exam1.getId(), memberId, ReactionType.LIKE));
reactionRepository.save(new Reaction(LikeTarget.EXAM.name(), exam2.getId(), memberId, ReactionType.LIKE));
reactionRepository.save(new Reaction(LikeTarget.EXAM.name(), exam3.getId(), memberId, ReactionType.LIKE));
}
}
}
15 changes: 13 additions & 2 deletions server/src/main/java/com/fluffy/exam/api/ExamController.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
import com.fluffy.exam.application.ExamService;
import com.fluffy.exam.application.response.CreateExamResponse;
import com.fluffy.exam.application.response.ExamDetailResponse;
import com.fluffy.exam.application.response.ExamDetailSummaryResponse;
import com.fluffy.exam.application.response.ExamWithAnswersResponse;
import com.fluffy.exam.domain.ExamStatus;
import com.fluffy.exam.domain.dto.ExamSummaryDto;
import com.fluffy.exam.domain.dto.MyExamSummaryDto;
import com.fluffy.exam.domain.dto.SubmittedExamSummaryDto;
import com.fluffy.global.response.PageResponse;
import com.fluffy.global.web.Accessor;
Expand Down Expand Up @@ -50,18 +52,27 @@ public ResponseEntity<PageResponse<ExamSummaryDto>> getPublishedExamSummaries(
}

@GetMapping("/api/v1/exams/mine")
public ResponseEntity<PageResponse<ExamSummaryDto>> getMyExamSummaries(
public ResponseEntity<PageResponse<MyExamSummaryDto>> getMyExamSummaries(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(value = "status", defaultValue = "draft") ExamStatus status,
@Auth Accessor accessor
) {
Pageable pageable = PageRequest.of(page, size);
PageResponse<ExamSummaryDto> response = examQueryService.getMyExamSummaries(pageable, status, accessor);
PageResponse<MyExamSummaryDto> response = examQueryService.getMyExamSummaries(pageable, status, accessor);

return ResponseEntity.ok(response);
}

@GetMapping("/api/v1/exams/{examId}/summary")
public ResponseEntity<ExamDetailSummaryResponse> getExamDetailSummary(
@PathVariable Long examId,
@Auth(required = false) Accessor accessor
) {
ExamDetailSummaryResponse response = examQueryService.getExamDetailSummary(examId, accessor);

return ResponseEntity.ok(response);
}

@GetMapping("/api/v1/exams/{examId}")
public ResponseEntity<ExamDetailResponse> getExamDetail(@PathVariable Long examId) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.fluffy.exam.application;

import com.fluffy.global.web.Accessor;
import com.fluffy.reaction.application.Like;
import com.fluffy.reaction.application.LikeService;
import com.fluffy.reaction.application.LikeTarget;
import com.fluffy.reaction.domain.Like;
import com.fluffy.reaction.domain.LikeService;
import com.fluffy.reaction.domain.LikeTarget;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand Down
17 changes: 17 additions & 0 deletions server/src/main/java/com/fluffy/exam/application/ExamMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import com.fluffy.exam.application.response.CreateExamResponse;
import com.fluffy.exam.application.response.ExamDetailResponse;
import com.fluffy.exam.application.response.ExamDetailSummaryResponse;
import com.fluffy.exam.application.response.ExamWithAnswersResponse;
import com.fluffy.exam.domain.Exam;
import com.fluffy.exam.domain.dto.ExamDetailSummaryDto;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
Expand Down Expand Up @@ -47,4 +49,19 @@ public ExamWithAnswersResponse toWithAnswersResponse(Exam exam) {
exam.getUpdatedAt()
);
}

public ExamDetailSummaryResponse toDetailSummaryResponse(ExamDetailSummaryDto dto, boolean isLiked) {
return new ExamDetailSummaryResponse(
dto.getId(),
dto.getTitle(),
dto.getDescription(),
dto.getStatus().name(),
dto.getAuthor(),
dto.getQuestionCount(),
dto.getLikeCount(),
isLiked,
dto.getCreatedAt(),
dto.getUpdatedAt()
);
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
package com.fluffy.exam.application;

import com.fluffy.exam.application.response.ExamDetailResponse;
import com.fluffy.exam.application.response.ExamDetailSummaryResponse;
import com.fluffy.exam.application.response.ExamWithAnswersResponse;
import com.fluffy.exam.domain.Exam;
import com.fluffy.exam.domain.ExamRepository;
import com.fluffy.exam.domain.ExamStatus;
import com.fluffy.exam.domain.dto.ExamDetailSummaryDto;
import com.fluffy.exam.domain.dto.ExamSummaryDto;
import com.fluffy.exam.domain.dto.MyExamSummaryDto;
import com.fluffy.exam.domain.dto.SubmittedExamSummaryDto;
import com.fluffy.global.exception.ForbiddenException;
import com.fluffy.global.exception.NotFoundException;
import com.fluffy.global.response.PageResponse;
import com.fluffy.global.web.Accessor;
import com.fluffy.reaction.domain.Like;
import com.fluffy.reaction.domain.LikeQueryService;
import com.fluffy.reaction.domain.LikeTarget;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
Expand All @@ -22,6 +29,7 @@ public class ExamQueryService {

private final ExamRepository examRepository;
private final ExamMapper examMapper;
private final LikeQueryService likeQueryService;

@Transactional(readOnly = true)
public PageResponse<ExamSummaryDto> getPublishedExamSummaries(Pageable pageable) {
Expand All @@ -31,12 +39,21 @@ public PageResponse<ExamSummaryDto> getPublishedExamSummaries(Pageable pageable)
}

@Transactional(readOnly = true)
public PageResponse<ExamSummaryDto> getMyExamSummaries(Pageable pageable, ExamStatus status, Accessor accessor) {
Page<ExamSummaryDto> summaries = examRepository.findMyExamSummaries(pageable, status, accessor.id());
public PageResponse<MyExamSummaryDto> getMyExamSummaries(Pageable pageable, ExamStatus status, Accessor accessor) {
Page<MyExamSummaryDto> summaries = examRepository.findMyExamSummaries(pageable, status, accessor.id());

return PageResponse.of(summaries);
}

@Transactional(readOnly = true)
public ExamDetailSummaryResponse getExamDetailSummary(Long examId, Accessor accessor) {
ExamDetailSummaryDto dto = examRepository.findExamDetailSummary(examId)
.orElseThrow(() -> new NotFoundException("시험을 찾을 수 없습니다."));
boolean isLiked = likeQueryService.isLiked(new Like(LikeTarget.EXAM, examId), accessor.id());

return examMapper.toDetailSummaryResponse(dto, isLiked);
}

@Transactional(readOnly = true)
public ExamDetailResponse getExamDetail(Long examId) {
Exam exam = examRepository.findByIdOrThrow(examId);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.fluffy.exam.application.response;

import com.fluffy.exam.domain.dto.AuthorDto;
import java.time.LocalDateTime;

public record ExamDetailSummaryResponse(
Long id,
String title,
String description,
String status,
AuthorDto author,
Long questionCount,
Long likeCount,
boolean isLiked,
LocalDateTime createdAt,
LocalDateTime updatedAt
) {
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package com.fluffy.exam.domain;

import com.fluffy.exam.domain.dto.ExamDetailSummaryDto;
import com.fluffy.exam.domain.dto.ExamSummaryDto;
import com.fluffy.exam.domain.dto.MyExamSummaryDto;
import com.fluffy.exam.domain.dto.SubmittedExamSummaryDto;
import java.util.Optional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

public interface ExamRepositoryCustom {

Page<ExamSummaryDto> findPublishedExamSummaries(Pageable pageable);

Page<ExamSummaryDto> findMyExamSummaries(Pageable pageable, ExamStatus status, Long memberId);
Page<MyExamSummaryDto> findMyExamSummaries(Pageable pageable, ExamStatus status, Long memberId);

Page<SubmittedExamSummaryDto> findSubmittedExamSummaries(Pageable pageable, Long memberId);

Optional<ExamDetailSummaryDto> findExamDetailSummary(Long examId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.fluffy.exam.domain.dto;

import com.fluffy.exam.domain.ExamStatus;
import com.querydsl.core.annotations.QueryProjection;
import java.time.LocalDateTime;
import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ExamDetailSummaryDto {

Long id;
String title;
String description;
ExamStatus status;
AuthorDto author;
Long questionCount;
Long likeCount;
LocalDateTime createdAt;
LocalDateTime updatedAt;

@QueryProjection
public ExamDetailSummaryDto(
Long id,
String title,
String description,
ExamStatus status,
AuthorDto author,
Long questionCount,
Long likeCount,
LocalDateTime createdAt,
LocalDateTime updatedAt
) {
this.id = id;
this.title = title;
this.description = description;
this.status = status;
this.author = author;
this.questionCount = questionCount;
this.likeCount = likeCount;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class ExamSummaryDto {
private ExamStatus status;
private AuthorDto author;
private Long questionCount;
private Long likeCount;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;

Expand All @@ -28,6 +29,7 @@ public ExamSummaryDto(
ExamStatus status,
AuthorDto author,
Long questionCount,
Long likeCount,
LocalDateTime createdAt,
LocalDateTime updatedAt
) {
Expand All @@ -37,6 +39,7 @@ public ExamSummaryDto(
this.status = status;
this.author = author;
this.questionCount = questionCount;
this.likeCount = likeCount;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.fluffy.exam.domain.dto;

import com.fluffy.exam.domain.ExamStatus;
import com.querydsl.core.annotations.QueryProjection;
import java.time.LocalDateTime;
import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class MyExamSummaryDto {

private Long id;
private String title;
private String description;
private ExamStatus status;
private AuthorDto author;
private Long questionCount;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;

@QueryProjection
public MyExamSummaryDto(
Long id,
String title,
String description,
ExamStatus status,
AuthorDto author,
Long questionCount,
LocalDateTime createdAt,
LocalDateTime updatedAt
) {
this.id = id;
this.title = title;
this.description = description;
this.status = status;
this.author = author;
this.questionCount = questionCount;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
}

Loading

0 comments on commit 2b6204f

Please sign in to comment.