Skip to content

Commit

Permalink
러너 및 서포터의 게시글 개수 조회 api 구현 (#646)
Browse files Browse the repository at this point in the history
* feat: 러너 식별자 값으로 러너 게시글 카운트 쿼리 작성

* refactor: secret 업데이트

* feat: Supporter 와 관련된 게시글 수 조회 repository 구현

* feat: RunnerId 와 ReviewStatus 로 러너 게시글 조회 service 구현

* feat: RunnerId 와 reviewStatus 로 로그인 된 러너의 게시글 개수 조회 api 구현

* test: 로그인한 러너와 관련된 러너 게시글 개수 조회 인수 테스트

* test: 러너와 연관된 러너 게시글 개수 조회 rest docs 테스트

* refactor: SupporterRunnerPostRepository 관련 코드 정리

* test: supporter 테스트 파일 구조 변경

* feat: 서포터 식별자 값으로 러너 게시글 개수 조회 서비스 로직 구현

* feat: 서포터 식별자 값으로 러너 게시글 개수 조회 api 구현

* test: 서포터 관련 러너 게시글 개수 조회 인수 테스트

* test: 로그인한 서포터 관련 러너 게시글 개수 조회 api 테스트

* test: 잘못된 테스트 수정

* test: 서포터가 리뷰 완료한 러너 게시글 개수 조회 api 테스트

* refactor: 서브 모듈 업데이트

* refactor: reference 타입을 primitive 타입으로 변경
  • Loading branch information
shb03323 authored Oct 11, 2023
1 parent 053be73 commit 670d33b
Show file tree
Hide file tree
Showing 28 changed files with 1,073 additions and 382 deletions.
64 changes: 63 additions & 1 deletion backend/baton/src/docs/asciidoc/RunnerPostReadApi.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,28 @@ include::{snippets}/../../build/generated-snippets/runner-post-read-with-logined

include::{snippets}/../../build/generated-snippets/runner-post-read-with-logined-runner-api-test/read-runner-post-by-logined-runner-and-review-status/response-fields.adoc[]

==== *러너 마이페이지 게시글 개수 조회 API*

===== *Http Request*

include::{snippets}/../../build/generated-snippets/runner-post-count-with-logined-runner-api-test/count-runner-post-by-logined-runner-and-review-status/http-request.adoc[]

===== *Http Request Headers*

include::{snippets}/../../build/generated-snippets/runner-post-count-with-logined-runner-api-test/count-runner-post-by-logined-runner-and-review-status/request-headers.adoc[]

===== *Http Request Query Parameters*

include::{snippets}/../../build/generated-snippets/runner-post-count-with-logined-runner-api-test/count-runner-post-by-logined-runner-and-review-status/query-parameters.adoc[]

===== *Http Response*

include::{snippets}/../../build/generated-snippets/runner-post-count-with-logined-runner-api-test/count-runner-post-by-logined-runner-and-review-status/http-response.adoc[]

===== *Http Response Fields*

include::{snippets}/../../build/generated-snippets/runner-post-count-with-logined-runner-api-test/count-runner-post-by-logined-runner-and-review-status/response-fields.adoc[]

==== *리뷰 지원한 서포터 조회 API*

===== *Http Request*
Expand Down Expand Up @@ -94,7 +116,29 @@ include::{snippets}/../../build/generated-snippets/runner-post-read-with-logined

include::{snippets}/../../build/generated-snippets/runner-post-read-with-logined-supporter-api-test/read-runner-post-by-logined-supporter-and-review-status/response-fields.adoc[]

==== *서포터 리뷰 완료한 게시글 조회 API*
==== *서포터 마이페이지 게시글 개수 조회 API*

===== *Http Request*

include::{snippets}/../../build/generated-snippets/runner-post-count-with-logined-supporter-api-test/count-runner-post-by-logined-supporter-and-review-status/http-request.adoc[]

===== *Http Request Headers*

include::{snippets}/../../build/generated-snippets/runner-post-count-with-logined-supporter-api-test/count-runner-post-by-logined-supporter-and-review-status/request-headers.adoc[]

===== *Http Request Query Parameters*

include::{snippets}/../../build/generated-snippets/runner-post-count-with-logined-supporter-api-test/count-runner-post-by-logined-supporter-and-review-status/query-parameters.adoc[]

===== *Http Response*

include::{snippets}/../../build/generated-snippets/runner-post-count-with-logined-supporter-api-test/count-runner-post-by-logined-supporter-and-review-status/http-response.adoc[]

===== *Http Response Fields*

include::{snippets}/../../build/generated-snippets/runner-post-count-with-logined-supporter-api-test/count-runner-post-by-logined-supporter-and-review-status/response-fields.adoc[]

==== *서포터의 리뷰 완료한 게시글 조회 API*

===== *Http Request*

Expand All @@ -112,6 +156,24 @@ include::{snippets}/../../build/generated-snippets/runner-post-read-of-supporter

include::{snippets}/../../build/generated-snippets/runner-post-read-of-supporter-by-guest-api-test/read-runner-post-by-supporter-id-and-review-status/response-fields.adoc[]

==== *서포터의 리뷰 완료한 게시글 개수 조회 API*

===== *Http Request*

include::{snippets}/../../build/generated-snippets/runner-post-count-of-supporter-by-guest-api-test/count-runner-post-by-supporter-id-and-review-status/http-request.adoc[]

===== *Http Request Query Parameters*

include::{snippets}/../../build/generated-snippets/runner-post-count-of-supporter-by-guest-api-test/count-runner-post-by-supporter-id-and-review-status/query-parameters.adoc[]

===== *Http Response*

include::{snippets}/../../build/generated-snippets/runner-post-count-of-supporter-by-guest-api-test/count-runner-post-by-supporter-id-and-review-status/http-response.adoc[]

===== *Http Response Fields*

include::{snippets}/../../build/generated-snippets/runner-post-count-of-supporter-by-guest-api-test/count-runner-post-by-supporter-id-and-review-status/response-fields.adoc[]

==== *태그 이름과 리뷰 상태를 조건으로 러너 게시글 페이징 조회 API*

===== *Http Request*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
package touch.baton.domain.member.command.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import touch.baton.domain.member.command.SupporterRunnerPost;

import java.util.Optional;

public interface SupporterRunnerPostCommandRepository extends JpaRepository<SupporterRunnerPost, Long> {

@Query("""
select count(1)
from SupporterRunnerPost srp
group by srp.runnerPost.id
having srp.runnerPost.id = :runnerPostId
""")
Optional<Long> countByRunnerPostId(@Param("runnerPostId") final Long runnerPostId);

boolean existsByRunnerPostId(final Long runnerPostId);

boolean existsByRunnerPostIdAndSupporterId(final Long runnerPostId, final Long supporterId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,6 @@

public interface SupporterRunnerPostQueryRepository extends JpaRepository<SupporterRunnerPost, Long> {

@Query("""
select count(1)
from SupporterRunnerPost srp
group by srp.runnerPost.id
having srp.runnerPost.id in (:runnerPostIds)
""")
List<Long> countByRunnerPostIdIn(@Param("runnerPostIds") final List<Long> runnerPostIds);

@Query("""
select count(1)
from SupporterRunnerPost srp
Expand All @@ -26,20 +18,6 @@ select count(1)
""")
Optional<Long> countByRunnerPostId(@Param("runnerPostId") final Long runnerPostId);

@Query("""
select case when exists (
select 1 from SupporterRunnerPost srp
where srp.runnerPost.id = rp.id)
then (
select count(srp.id) from SupporterRunnerPost srp
where srp.runnerPost.id = rp.id
) else 0 end
from RunnerPost rp
where rp.id in :runnerPostIds
order by rp.id desc
""")
List<Long> countByRunnerPostIds(@Param("runnerPostIds") final List<Long> runnerPostIds);

@Query("""
select (count(1) >= 1)
from SupporterRunnerPost srp
Expand All @@ -49,11 +27,13 @@ select count(srp.id) from SupporterRunnerPost srp
""")
boolean existsByRunnerPostIdAndMemberId(@Param("runnerPostId") final Long runnerPostId, @Param("memberId") final Long memberId);

boolean existsByRunnerPostId(final Long runnerPostId);

void deleteBySupporterIdAndRunnerPostId(final Long supporterId, final Long runnerPostId);

boolean existsByRunnerPostIdAndSupporterId(final Long runnerPostId, final Long supporterId);

List<SupporterRunnerPost> readByRunnerPostId(final Long runnerPostId);

@Query("""
select count(1)
from SupporterRunnerPost srp
join fetch RunnerPost rp on rp.id = srp.runnerPost.id
where rp.reviewStatus = 'NOT_STARTED' and srp.supporter.id = :supporterId
""")
long countRunnerPostBySupporterIdByReviewStatusNotStarted(@Param("supporterId") final Long supporterId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,7 @@ public Long createRunnerPostApplicant(final Supporter supporter,
final Long runnerPostId
) {
final RunnerPost foundRunnerPost = getRunnerPostOrThrowException(runnerPostId);
final boolean isApplicantHistoryExist = supporterRunnerPostCommandRepository.existsByRunnerPostIdAndSupporterId(runnerPostId, supporter.getId());
if (isApplicantHistoryExist) {
if (isApplySupporter(foundRunnerPost, supporter)) {
throw new RunnerPostBusinessException("Supporter 는 이미 해당 RunnerPost 에 리뷰 신청을 한 이력이 있습니다.");
}

Expand Down Expand Up @@ -160,7 +159,7 @@ public void updateRunnerPostAppliedSupporter(final Runner runner,
final RunnerPost foundRunnerPost = runnerPostCommandRepository.findById(runnerPostId)
.orElseThrow(() -> new RunnerPostBusinessException("RunnerPost 의 식별자값으로 러너 게시글을 조회할 수 없습니다."));

if (isApplySupporter(runnerPostId, foundApplySupporter)) {
if (!isApplySupporter(foundRunnerPost, foundApplySupporter)) {
throw new RunnerPostBusinessException("게시글에 리뷰를 제안한 서포터가 아닙니다.");
}
if (foundRunnerPost.isNotOwner(runner)) {
Expand All @@ -172,7 +171,7 @@ public void updateRunnerPostAppliedSupporter(final Runner runner,
eventPublisher.publishEvent(new RunnerPostAssignSupporterEvent(foundRunnerPost.getId()));
}

private boolean isApplySupporter(final Long runnerPostId, final Supporter foundSupporter) {
return !supporterRunnerPostCommandRepository.existsByRunnerPostIdAndSupporterId(runnerPostId, foundSupporter.getId());
private boolean isApplySupporter(final RunnerPost runnerPost, final Supporter supporter) {
return supporterRunnerPostCommandRepository.existsByRunnerPostIdAndSupporterId(runnerPost.getId(), supporter.getId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public ResponseEntity<RunnerPostResponse.Detail> readByRunnerPostId(
@PathVariable final Long runnerPostId
) {
final RunnerPost foundRunnerPost = runnerPostQueryService.readByRunnerPostId(runnerPostId);
final long applicantCount = runnerPostQueryService.readCountByRunnerPostId(foundRunnerPost.getId());
final long applicantCount = runnerPostQueryService.countApplicantsByRunnerPostId(foundRunnerPost.getId());
final boolean isApplicantHistoryExist = runnerPostQueryService.existsRunnerPostApplicantByRunnerPostIdAndMemberId(runnerPostId, member.getId());

runnerPostQueryService.increaseWatchedCount(foundRunnerPost);
Expand All @@ -70,6 +70,14 @@ public ResponseEntity<PageResponse<RunnerPostResponse.Simple>> readRunnerPostByS
return ResponseEntity.ok(runnerPostQueryService.pageRunnerPostBySupporterIdAndReviewStatus(pageParams, supporterId, ReviewStatus.DONE));
}

@GetMapping("/search/count")
public ResponseEntity<RunnerPostResponse.Count> countRunnerPostBySupporterIdAndReviewStatusDone(
@RequestParam final Long supporterId
) {
final long count = runnerPostQueryService.countRunnerPostBySupporterIdAndReviewStatus(supporterId, ReviewStatus.DONE);
return ResponseEntity.ok(RunnerPostResponse.Count.from(count));
}

@GetMapping("/{runnerPostId}/supporters")
public ResponseEntity<SupporterRunnerPostResponses.Detail> readSupporterRunnerPostsByRunnerPostId(
@AuthRunnerPrincipal final Runner runner,
Expand All @@ -91,11 +99,30 @@ public ResponseEntity<PageResponse<RunnerPostResponse.Simple>> readRunnerPostByL
return ResponseEntity.ok(runnerPostQueryService.pageRunnerPostBySupporterIdAndReviewStatus(pageParams, supporter.getId(), reviewStatus));
}

@GetMapping("/me/runner") public ResponseEntity<PageResponse<RunnerPostResponse.SimpleByRunner>> readRunnerPostByLoginedRunnerAndReviewStatus(
@GetMapping("/me/runner")
public ResponseEntity<PageResponse<RunnerPostResponse.SimpleByRunner>> readRunnerPostByLoginedRunnerAndReviewStatus(
@AuthRunnerPrincipal final Runner runner,
@Valid @ModelAttribute final PageParams pageParams,
@RequestParam(required = false) final ReviewStatus reviewStatus
) {
return ResponseEntity.ok(runnerPostQueryService.pageRunnerPostByRunnerIdAndReviewStatus(pageParams, runner.getId(), reviewStatus));
}

@GetMapping("/me/supporter/count")
public ResponseEntity<RunnerPostResponse.Count> countRunnerPostByLoginedSupporterAndReviewStatus(
@AuthSupporterPrincipal final Supporter supporter,
@RequestParam final ReviewStatus reviewStatus
) {
final long runnerPostCount = runnerPostQueryService.countRunnerPostBySupporterIdAndReviewStatus(supporter.getId(), reviewStatus);
return ResponseEntity.ok(RunnerPostResponse.Count.from(runnerPostCount));
}

@GetMapping("/me/runner/count")
public ResponseEntity<RunnerPostResponse.Count> countRunnerPostByLoginedRunnerAndReviewStatus(
@AuthRunnerPrincipal final Runner runner,
@RequestParam final ReviewStatus reviewStatus
) {
final long runnerPostCount = runnerPostQueryService.countRunnerPostByRunnerIdAndReviewStatus(runner.getId(), reviewStatus);
return ResponseEntity.ok(RunnerPostResponse.Count.from(runnerPostCount));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,13 @@ public Long extractId() {
}
}

public record Count(Long count) {

public static Count from(final long count) {
return new Count(count);
}
}

private static List<String> convertToTags(final RunnerPost runnerPost) {
return runnerPost.getRunnerPostTags()
.getRunnerPostTags()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.springframework.data.repository.query.Param;
import touch.baton.domain.runnerpost.command.RunnerPost;
import touch.baton.domain.runnerpost.command.repository.dto.RunnerPostApplicantCountDto;
import touch.baton.domain.runnerpost.command.vo.ReviewStatus;

import java.util.List;
import java.util.Optional;
Expand Down Expand Up @@ -39,4 +40,18 @@ public interface RunnerPostQueryRepository extends JpaRepository<RunnerPost, Lon
group by rp.id
""")
List<RunnerPostApplicantCountDto> countApplicantsByRunnerPostIds(@Param("runnerPostIds") final List<Long> runnerPostIds);

@Query("""
select count(1)
from RunnerPost rp
where rp.runner.id = :runnerId and rp.reviewStatus = :reviewStatus
""")
long countByRunnerIdAndReviewStatus(@Param("runnerId") final Long runnerId, @Param("reviewStatus") final ReviewStatus reviewStatus);

@Query("""
select count(1)
from RunnerPost rp
where rp.supporter.id = :supporterId and rp.reviewStatus = :reviewStatus
""")
long countBySupporterIdAndReviewStatus(@Param("supporterId") final Long supporterId, @Param("reviewStatus") final ReviewStatus reviewStatus);
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,21 @@ public boolean existsRunnerPostApplicantByRunnerPostIdAndMemberId(final Long run
return supporterRunnerPostQueryRepository.existsByRunnerPostIdAndMemberId(runnerPostId, memberId);
}

public long readCountByRunnerPostId(final Long runnerPostId) {
public long countApplicantsByRunnerPostId(final Long runnerPostId) {
return supporterRunnerPostQueryRepository.countByRunnerPostId(runnerPostId).orElse(0L);
}

public long countRunnerPostByRunnerIdAndReviewStatus(final Long runnerId, final ReviewStatus reviewStatus) {
return runnerPostQueryRepository.countByRunnerIdAndReviewStatus(runnerId, reviewStatus);
}

public long countRunnerPostBySupporterIdAndReviewStatus(final Long supporterId, final ReviewStatus reviewStatus) {
if (reviewStatus.isNotStarted()) {
return supporterRunnerPostQueryRepository.countRunnerPostBySupporterIdByReviewStatusNotStarted(supporterId);
}
return runnerPostQueryRepository.countBySupporterIdAndReviewStatus(supporterId, reviewStatus);
}

@Transactional
public void increaseWatchedCount(final RunnerPost runnerPost) {
runnerPost.increaseWatchedCount();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import touch.baton.domain.member.query.repository.SupporterRunnerPostQueryRepository;

@Profile("test")

public interface TestSupporterRunnerPostQueryRepository extends SupporterRunnerPostQueryRepository {

default Long getApplicantCountByRunnerPostId(final Long runnerPostId) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package touch.baton.assure.runnerpost.query.count.runner;

import org.junit.jupiter.api.Test;
import touch.baton.assure.runnerpost.support.command.RunnerPostCreateSupport;
import touch.baton.assure.runnerpost.support.query.count.runner.RunnerPostCountByRunnerSupport;
import touch.baton.config.AssuredTestConfig;
import touch.baton.config.infra.auth.oauth.authcode.FakeAuthCodes;
import touch.baton.domain.runnerpost.command.vo.ReviewStatus;
import touch.baton.domain.runnerpost.query.controller.response.RunnerPostResponse;

import java.time.LocalDateTime;
import java.util.List;

import static touch.baton.assure.runnerpost.support.command.RunnerPostCreateSupport.러너_게시글_생성_요청;
import static touch.baton.assure.runnerpost.support.query.count.runner.RunnerPostCountByRunnerSupport.러너_게시글_개수_응답;

@SuppressWarnings("NonAsciiCharacters")
class RunnerPostCountByRunnerAssuredTest extends AssuredTestConfig {

@Test
void 로그인한_러너와_연관된_러너_게시글_개수_조회에_성공한다() {
// given
final String 디투_액세스_토큰 = oauthLoginTestManager.소셜_회원입을_진행한__액세스_토큰을_반환한다(FakeAuthCodes.ditooAuthCode());
final String 헤나_액세스_토큰 = oauthLoginTestManager.소셜_회원입을_진행한__액세스_토큰을_반환한다(FakeAuthCodes.hyenaAuthCode());
러너_게시글_생성에_성공한다(디투_액세스_토큰);
러너_게시글_생성에_성공한다(헤나_액세스_토큰);

// when
final RunnerPostResponse.Count 기대된_러너_게시글_개수 = 러너_게시글_개수_응답(1);

// then
RunnerPostCountByRunnerSupport
.클라이언트_요청()
.액세스_토큰으로_로그인한다(디투_액세스_토큰)
.리뷰_상태로_로그인한_러너와_연관된_러너_게시글_개수를_조회한다(ReviewStatus.NOT_STARTED)

.서버_응답()
.로그인한_러너와_연관된_러너_게시글_개수_조회_성공을_검증한다(기대된_러너_게시글_개수);
}

private void 러너_게시글_생성에_성공한다(final String 액세스_토큰) {
RunnerPostCreateSupport
.클라이언트_요청()
.액세스_토큰으로_로그인한다(액세스_토큰)
.러너_게시글_등록_요청한다(
러너_게시글_생성_요청(
"테스트용_러너_게시글_제목",
List.of("자바", "스프링"),
"https://test-pull-request.com",
LocalDateTime.now().plusHours(100),
"테스트용_러너_게시글_구현_내용",
"테스트용_러너_게시글_궁금한_내용",
"테스트용_러너_게시글_참고_사항"
)
)

.서버_응답()
.러너_게시글_생성_성공을_검증한다();
}
}
Loading

0 comments on commit 670d33b

Please sign in to comment.