Skip to content

Commit

Permalink
서포터 피드백 작성 유무 반환 추가, 러너 게시글 조회수 flyway 컬럼명 수정, 서포터 지원자 수 조회시 dto 사용하도…
Browse files Browse the repository at this point in the history
…록 수정 (#555)

* feat: 러너 게시글의 서포터 피드백 유무 필드(컬럼) 구현

* test: 러너 게시글에 서포터 피드백 유무 컬럼 추가 후 테스트 변경

* feat: 러너 게시글 응답에 서포터 피드백 유무 정보 추가

* chore: 러너 게시글에 서포터 피드백 유무 flyway 테이블 컬럼 추가

* chore: 러너 게시글 조회수 컬럼명 flyway 수정 추가

* style: IsReviewed get 메서드 네이밍 get으로 통일되게 수정

* fix: IsReviewed 내부 값 반환 수정

* test: 테스트 내부 entityManager 사용 후 close 하도록 수정

* refactor: 러너 게시글 식별자값 목록으로 서포터 지원자 수 조회시 매핑된 서포터 지원자 수 객체를 반환하도록 레포지터리 메서드 변경

* refactor: 매핑된 서포터 지원자 수 객체를 사용하도록 서비스, 컨트롤러 변경

* style: 공백 및 개행 스타일 수정

* style: toString 메서드 삭제

* refactor: dto 클래스를 레코드로 변경

* test: entityManager flush, close 순서 변경
  • Loading branch information
hyena0608 authored Sep 20, 2023
1 parent 0501fb6 commit b9289a2
Show file tree
Hide file tree
Showing 30 changed files with 333 additions and 105 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import touch.baton.domain.runnerpost.vo.CuriousContents;
import touch.baton.domain.runnerpost.vo.Deadline;
import touch.baton.domain.runnerpost.vo.ImplementedContents;
import touch.baton.domain.runnerpost.vo.IsReviewed;
import touch.baton.domain.runnerpost.vo.PostscriptContents;
import touch.baton.domain.runnerpost.vo.PullRequestUrl;
import touch.baton.domain.runnerpost.vo.ReviewStatus;
Expand All @@ -37,7 +38,10 @@
import static jakarta.persistence.FetchType.LAZY;
import static jakarta.persistence.GenerationType.IDENTITY;
import static lombok.AccessLevel.PROTECTED;
import static touch.baton.domain.runnerpost.vo.ReviewStatus.*;
import static touch.baton.domain.runnerpost.vo.ReviewStatus.DONE;
import static touch.baton.domain.runnerpost.vo.ReviewStatus.IN_PROGRESS;
import static touch.baton.domain.runnerpost.vo.ReviewStatus.NOT_STARTED;
import static touch.baton.domain.runnerpost.vo.ReviewStatus.OVERDUE;

@Getter
@NoArgsConstructor(access = PROTECTED)
Expand Down Expand Up @@ -73,6 +77,9 @@ public class RunnerPost extends BaseEntity {
@Column(nullable = false)
private ReviewStatus reviewStatus = ReviewStatus.NOT_STARTED;

@Embedded
private IsReviewed isReviewed;

@ManyToOne(fetch = LAZY)
@JoinColumn(name = "runner_id", foreignKey = @ForeignKey(name = "fk_runner_post_to_runner"), nullable = false)
private Runner runner;
Expand All @@ -93,11 +100,12 @@ private RunnerPost(final Title title,
final Deadline deadline,
final WatchedCount watchedCount,
final ReviewStatus reviewStatus,
final IsReviewed isReviewed,
final Runner runner,
final Supporter supporter,
final RunnerPostTags runnerPostTags
) {
this(null, title, implementedContents, curiousContents, postscriptContents, pullRequestUrl, deadline, watchedCount, reviewStatus, runner, supporter, runnerPostTags);
this(null, title, implementedContents, curiousContents, postscriptContents, pullRequestUrl, deadline, watchedCount, reviewStatus, isReviewed, runner, supporter, runnerPostTags);
}

private RunnerPost(final Long id,
Expand All @@ -109,11 +117,12 @@ private RunnerPost(final Long id,
final Deadline deadline,
final WatchedCount watchedCount,
final ReviewStatus reviewStatus,
final IsReviewed isReviewed,
final Runner runner,
final Supporter supporter,
final RunnerPostTags runnerPostTags
) {
validateNotNull(title, implementedContents, curiousContents, postscriptContents, pullRequestUrl, deadline, watchedCount, reviewStatus, runner, runnerPostTags);
validateNotNull(title, implementedContents, curiousContents, postscriptContents, pullRequestUrl, deadline, watchedCount, reviewStatus, isReviewed, runner, runnerPostTags);
this.id = id;
this.title = title;
this.implementedContents = implementedContents;
Expand All @@ -123,6 +132,7 @@ private RunnerPost(final Long id,
this.deadline = deadline;
this.watchedCount = watchedCount;
this.reviewStatus = reviewStatus;
this.isReviewed = isReviewed;
this.runner = runner;
this.supporter = supporter;
this.runnerPostTags = runnerPostTags;
Expand All @@ -143,10 +153,11 @@ public static RunnerPost newInstance(final String title,
.postscriptContents(new PostscriptContents(postscriptContents))
.pullRequestUrl(new PullRequestUrl(pullRequestUrl))
.deadline(new Deadline(deadline))
.runner(runner)
.runnerPostTags(new RunnerPostTags(new ArrayList<>()))
.watchedCount(WatchedCount.zero())
.reviewStatus(NOT_STARTED)
.isReviewed(IsReviewed.notReviewed())
.runner(runner)
.runnerPostTags(new RunnerPostTags(new ArrayList<>()))
.build();
}

Expand All @@ -158,6 +169,7 @@ private void validateNotNull(final Title title,
final Deadline deadline,
final WatchedCount watchedCount,
final ReviewStatus reviewStatus,
final IsReviewed isReviewed,
final Runner runner,
final RunnerPostTags runnerPostTags
) {
Expand Down Expand Up @@ -185,6 +197,9 @@ private void validateNotNull(final Title title,
if (Objects.isNull(reviewStatus)) {
throw new RunnerPostDomainException("RunnerPost 의 reviewStatus 는 null 일 수 없습니다.");
}
if (Objects.isNull(isReviewed)) {
throw new RunnerPostDomainException("RunnerPost 의 isReviewed 는 null 일 수 없습니다.");
}
if (Objects.isNull(runner)) {
throw new RunnerPostDomainException("RunnerPost 의 runner 는 null 일 수 없습니다.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
import touch.baton.domain.common.response.PageResponse;
import touch.baton.domain.runnerpost.RunnerPost;
import touch.baton.domain.runnerpost.controller.response.RunnerPostResponse;
import touch.baton.domain.runnerpost.repository.dto.ApplicantCountMappingDto;
import touch.baton.domain.runnerpost.service.RunnerPostReadService;
import touch.baton.domain.runnerpost.service.RunnerPostService;
import touch.baton.domain.runnerpost.vo.ReviewStatus;

import java.util.List;
import java.util.stream.IntStream;

import static org.springframework.data.domain.Sort.Direction.DESC;

Expand All @@ -38,20 +38,14 @@ public ResponseEntity<PageResponse<RunnerPostResponse.Simple>> readRunnerPostsBy
@RequestParam final ReviewStatus reviewStatus
) {
final Page<RunnerPost> pageRunnerPosts = getPageRunnerPosts(pageable, tagName, reviewStatus);
final ApplicantCountMappingDto applicantCountMapping = getApplicantCountMapping(pageRunnerPosts);

final List<Long> foundRunnerPostIds = pageRunnerPosts.stream()
.map(RunnerPost::getId)
.toList();
final List<RunnerPostResponse.Simple> responses = pageRunnerPosts.getContent().stream()
.map(runnerPost -> {
final Long foundApplicantCount = applicantCountMapping.getApplicantCountByRunnerPostId(runnerPost.getId());

final List<Long> foundApplicantCounts = runnerPostReadService.readApplicantCountsByRunnerPostIds(foundRunnerPostIds);
final List<RunnerPostResponse.Simple> responses = IntStream.range(0, foundApplicantCounts.size())
.mapToObj(index -> {
final RunnerPost current = pageRunnerPosts.getContent().get(index);
final Long applicantCount = foundApplicantCounts.get(index);

return RunnerPostResponse.Simple.from(current, applicantCount);
}
).toList();
return RunnerPostResponse.Simple.from(runnerPost, foundApplicantCount);
}).toList();

final PageImpl<RunnerPostResponse.Simple> pageResponse
= new PageImpl<>(responses, pageable, pageRunnerPosts.getTotalElements());
Expand All @@ -66,4 +60,12 @@ private Page<RunnerPost> getPageRunnerPosts(final Pageable pageable, final Strin

return runnerPostReadService.readRunnerPostByTagNameAndReviewStatus(pageable, tagName, reviewStatus);
}

private ApplicantCountMappingDto getApplicantCountMapping(final Page<RunnerPost> pageRunnerPosts) {
final List<Long> foundRunnerPostIds = pageRunnerPosts.stream()
.map(RunnerPost::getId)
.toList();

return runnerPostReadService.readApplicantCountMappingByRunnerPostIds(foundRunnerPostIds);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,16 @@ public static Mine from(final RunnerPost runnerPost) {
);
}
}

public record SimpleInMyPage(Long runnerPostId,
Long supporterId,
String title,
LocalDateTime deadline,
List<String> tags,
int watchedCount,
long applicantCount,
String reviewStatus
String reviewStatus,
boolean isReviewed

) {

Expand All @@ -136,7 +138,8 @@ public static SimpleInMyPage from(final RunnerPost runnerPost,
convertToTags(runnerPost),
runnerPost.getWatchedCount().getValue(),
applicantCount,
runnerPost.getReviewStatus().name()
runnerPost.getReviewStatus().name(),
runnerPost.getIsReviewed().getValue()
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,43 @@
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import touch.baton.domain.runnerpost.RunnerPost;
import touch.baton.domain.runnerpost.repository.dto.ApplicantCountDto;
import touch.baton.domain.runnerpost.repository.dto.ApplicantCountMappingDto;
import touch.baton.domain.runnerpost.vo.ReviewStatus;
import touch.baton.domain.tag.vo.TagReducedName;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public interface RunnerPostReadRepository extends JpaRepository<RunnerPost, Long> {

@Query("""
select coalesce(count(srp.id), 0)
default ApplicantCountMappingDto findApplicantCountMappingByRunnerPostIds(final List<Long> runnerPostIds) {
final Map<Long, Long> applicantCountMapping = countApplicantsByRunnerPostIds(runnerPostIds)
.stream()
.collect(Collectors.toMap(
ApplicantCountDto::runnerPostId,
ApplicantCountDto::applicantCount
));

return new ApplicantCountMappingDto(applicantCountMapping);
}

@Query(value = """
select new touch.baton.domain.runnerpost.repository.dto.ApplicantCountDto(rp.id, count(srp.id))
from RunnerPost rp
left outer join fetch SupporterRunnerPost srp on srp.runnerPost.id = rp.id
where rp.id in :runnerPostIds
group by rp.id
""")
List<Long> countApplicantsByRunnerPostIds(@Param("runnerPostIds") final List<Long> runnerPostIds);
List<ApplicantCountDto> countApplicantsByRunnerPostIds(@Param("runnerPostIds") final List<Long> runnerPostIds);

@Query("""
select rp
from RunnerPost rp
join fetch Runner r on r.id = rp.runner.id
join fetch Member m on m.id = r.member.id
join fetch RunnerPostTag rpt on rpt.runnerPost.id = rp.id
where rpt.tag.tagReducedName = :tagReducedName
and rp.reviewStatus = :reviewStatus
""")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package touch.baton.domain.runnerpost.repository.dto;

public record ApplicantCountDto(Long runnerPostId, Long applicantCount) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package touch.baton.domain.runnerpost.repository.dto;

import java.util.Map;

public record ApplicantCountMappingDto(Map<Long, Long> applicantCounts) {

public Long getApplicantCountByRunnerPostId(final Long runnerPostId) {
return applicantCounts.get(runnerPostId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.springframework.transaction.annotation.Transactional;
import touch.baton.domain.runnerpost.RunnerPost;
import touch.baton.domain.runnerpost.repository.RunnerPostReadRepository;
import touch.baton.domain.runnerpost.repository.dto.ApplicantCountMappingDto;
import touch.baton.domain.runnerpost.vo.ReviewStatus;
import touch.baton.domain.tag.vo.TagReducedName;

Expand All @@ -28,7 +29,7 @@ public Page<RunnerPost> readRunnerPostByTagNameAndReviewStatus(final Pageable pa
return runnerPostReadRepository.findByTagReducedNameAndReviewStatus(pageable, tagReducedName, reviewStatus);
}

public List<Long> readApplicantCountsByRunnerPostIds(final List<Long> runnerPostIds) {
return runnerPostReadRepository.countApplicantsByRunnerPostIds(runnerPostIds);
public ApplicantCountMappingDto readApplicantCountMappingByRunnerPostIds(final List<Long> runnerPostIds) {
return runnerPostReadRepository.findApplicantCountMappingByRunnerPostIds(runnerPostIds);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package touch.baton.domain.runnerpost.vo;

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.ColumnDefault;

import static lombok.AccessLevel.PROTECTED;

@EqualsAndHashCode
@NoArgsConstructor(access = PROTECTED)
@Embeddable
public class IsReviewed {

@ColumnDefault(value = "false")
@Column(name = "is_reviewed", nullable = false)
private boolean value = false;

private IsReviewed(final boolean value) {
this.value = value;
}

public static IsReviewed notReviewed() {
return new IsReviewed(false);
}

public static IsReviewed reviewed() {
return new IsReviewed(true);
}

public boolean getValue() {
return value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE runner_post ADD COLUMN is_reviewed BOOLEAN DEFAULT FALSE;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE runner_post CHANGE COLUMN watch_count watched_count INT DEFAULT 0 NOT NULL;
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
package touch.baton.assure.repository;

import touch.baton.domain.runnerpost.repository.RunnerPostReadRepository;
import touch.baton.domain.runnerpost.repository.dto.ApplicantCountDto;

import java.util.List;

public interface TestRunnerPostReadRepository extends RunnerPostReadRepository {

default Long countApplicantByRunnerPostId(final Long runnerPostId) {
final List<Long> foundApplicants = countApplicantsByRunnerPostIds(List.of(runnerPostId));
final List<ApplicantCountDto> foundApplicants = countApplicantsByRunnerPostIds(List.of(runnerPostId));
if (foundApplicants.isEmpty()) {
throw new IllegalArgumentException("테스트에서 러너 게시글 식별자값으로 서포터 지원자 수 조회에 실패하였습니다.");
}

return foundApplicants.get(0);
return foundApplicants.get(0).applicantCount();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ private RunnerPostAssuredSupport() {
러너_게시글_태그_목록,
조회수,
지원한_서포터_수,
리뷰_상태.name()
리뷰_상태.name(),
러너_게시글.getIsReviewed().getValue()
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import touch.baton.domain.runner.Runner;
import touch.baton.domain.runnerpost.RunnerPost;
import touch.baton.domain.runnerpost.vo.Deadline;
import touch.baton.domain.runnerpost.vo.IsReviewed;
import touch.baton.domain.runnerpost.vo.ReviewStatus;
import touch.baton.fixture.domain.MemberFixture;
import touch.baton.fixture.domain.RunnerFixture;
Expand All @@ -19,7 +20,9 @@
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static touch.baton.domain.runnerpost.vo.ReviewStatus.*;
import static touch.baton.domain.runnerpost.vo.ReviewStatus.DONE;
import static touch.baton.domain.runnerpost.vo.ReviewStatus.NOT_STARTED;
import static touch.baton.domain.runnerpost.vo.ReviewStatus.OVERDUE;
import static touch.baton.fixture.vo.DeadlineFixture.deadline;

class ScheduleRunnerPostRepositoryTest extends RepositoryTestConfig {
Expand Down Expand Up @@ -68,8 +71,8 @@ void updateAllPassedDeadline_fail_when_reviewStatus_is_DONE() {
final Deadline passedDeadlineOne = deadline(LocalDateTime.now().minusHours(10));
final Deadline passedDeadlineTwo = deadline(LocalDateTime.now().minusHours(5));
final ReviewStatus expectedReviewStatus = DONE;
final RunnerPost runnerPostOne = RunnerPostFixture.create(runner, passedDeadlineOne, expectedReviewStatus);
final RunnerPost runnerPostTwo = RunnerPostFixture.create(runner, passedDeadlineTwo, expectedReviewStatus);
final RunnerPost runnerPostOne = RunnerPostFixture.create(runner, passedDeadlineOne, expectedReviewStatus, IsReviewed.notReviewed());
final RunnerPost runnerPostTwo = RunnerPostFixture.create(runner, passedDeadlineTwo, expectedReviewStatus, IsReviewed.notReviewed());
em.persist(runnerPostOne);
em.persist(runnerPostTwo);

Expand All @@ -90,8 +93,8 @@ void updateAllPassedDeadline_fail_when_deadline_is_not_passed() {
final Deadline passedDeadlineOne = deadline(LocalDateTime.now().plusHours(10));
final Deadline passedDeadlineTwo = deadline(LocalDateTime.now().plusHours(5));
final ReviewStatus expectedReviewStatus = NOT_STARTED;
final RunnerPost runnerPostOne = RunnerPostFixture.create(runner, passedDeadlineOne, expectedReviewStatus);
final RunnerPost runnerPostTwo = RunnerPostFixture.create(runner, passedDeadlineTwo, expectedReviewStatus);
final RunnerPost runnerPostOne = RunnerPostFixture.create(runner, passedDeadlineOne, expectedReviewStatus, IsReviewed.notReviewed());
final RunnerPost runnerPostTwo = RunnerPostFixture.create(runner, passedDeadlineTwo, expectedReviewStatus, IsReviewed.notReviewed());
em.persist(runnerPostOne);
em.persist(runnerPostTwo);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@
import touch.baton.domain.runnerpost.service.RunnerPostService;
import touch.baton.domain.runnerpost.vo.Deadline;
import touch.baton.domain.runnerpost.vo.ReviewStatus;
import touch.baton.domain.tag.RunnerPostTag;
import touch.baton.domain.tag.Tag;
import touch.baton.domain.tag.repository.TagRepository;
import touch.baton.domain.tag.service.TagService;
import touch.baton.fixture.domain.MemberFixture;
import touch.baton.fixture.domain.RunnerFixture;
import touch.baton.fixture.domain.RunnerPostFixture;
Expand Down Expand Up @@ -172,6 +169,7 @@ void readRunnerMyPage() throws Exception {
fieldWithPath("data.[].watchedCount").type(NUMBER).description("러너 게시글의 조회수"),
fieldWithPath("data.[].applicantCount").type(NUMBER).description("러너 게시글에 신청한 서포터 수"),
fieldWithPath("data.[].reviewStatus").type(STRING).description("러너 게시글 리뷰 상태"),
fieldWithPath("data.[].isReviewed").type(BOOLEAN).description("러너 게시글을 리뷰해준 서포터에 대한 피드백 유무"),
fieldWithPath("pageInfo.isFirst").type(BOOLEAN).description("첫 번째 페이지인지"),
fieldWithPath("pageInfo.isLast").type(BOOLEAN).description("마지막 페이지 인지"),
fieldWithPath("pageInfo.hasNext").type(BOOLEAN).description("다음 페이지가 있는지"),
Expand Down
Loading

0 comments on commit b9289a2

Please sign in to comment.