Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release v1.0.4 #31

Merged
merged 2 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions server/src/docs/asciidoc/exam-like.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
== Exam Like API

=== 시험 좋아요 등록

operation::exam-like-document-test/like[]

=== 시험 좋아요 취소

operation::exam-like-document-test/unlike[]
9 changes: 7 additions & 2 deletions 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 All @@ -38,4 +42,5 @@ operation::exam-document-test/update-title[]

=== 시험 설명 수정

operation::exam-document-test/update-description[]
operation::exam-document-test/update-description[]

4 changes: 4 additions & 0 deletions server/src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
= Fluffy API 명세서

include::auth.adoc[]

include::oauth2.adoc[]

include::exam.adoc[]
include::exam-like.adoc[]

include::submission.adoc[]
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
38 changes: 38 additions & 0 deletions server/src/main/java/com/fluffy/exam/api/ExamLikeController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.fluffy.exam.api;

import com.fluffy.exam.application.ExamLikeService;
import com.fluffy.global.web.Accessor;
import com.fluffy.global.web.Auth;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class ExamLikeController {

private final ExamLikeService examLikeService;

@PostMapping("/api/v1/exams/{examId}/like")
public ResponseEntity<Void> like(
@PathVariable Long examId,
@Auth Accessor accessor
) {
examLikeService.like(examId, accessor);

return ResponseEntity.ok().build();
}

@DeleteMapping("/api/v1/exams/{examId}/like")
public ResponseEntity<Void> unlike(
@PathVariable Long examId,
@Auth Accessor accessor
) {
examLikeService.unlike(examId, accessor);

return ResponseEntity.ok().build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.fluffy.exam.application;

import com.fluffy.global.web.Accessor;
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;

@Service
@RequiredArgsConstructor
public class ExamLikeService {

private final LikeService likeService;

@Transactional
public Long like(Long examId, Accessor accessor) {
return likeService.like(new Like(LikeTarget.EXAM, examId), accessor.id());
}

@Transactional
public Long unlike(Long examId, Accessor accessor) {
return likeService.removeLike(new Like(LikeTarget.EXAM, examId), accessor.id());
}
}
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
Loading
Loading