Skip to content

Commit

Permalink
[STMT-273] 스터디 활동 목록 간략 조회 API 구현 (#130)
Browse files Browse the repository at this point in the history
* 🩹 [STMT-261] 간략 활동 리스트 조회 api: ActivityListDetailedQuery를 클래스로 전환 및 생성 방법 수정

* ✨ [STMT-273] 조건에 따른 활동 간략 조회 메서드 구현

* ✨ [STMT-273] 미참여 상태의 활동에서 반환되는 null을 처리하기 위한 common status 클래스 구현 및 converter 수정

* ✨ [STMT-273] application layer: 페이지네이션과 페이지네이션을 적용하지 않는 메서드 구현

* ✨ [STMT-273] 활동 간략 목록을 조회하는 api 구현

* 🔥 [STMT-273] GlobalExceptionHandler 사용하지 않는 주석 제거

* ✨ [STMT-273] 요청된 카테고리가 없는 경우 각 활동 유형에 맞는 date between 기준을 반환하도록 구현

* ✨ [STMT-273] 차순위로 생성일자 내림차순으로 정렬

* ✅ [STMT-273] 테스트용 활동 더미데이터 추가

* ✨ [STMT-273] 요청에서 시작일이 종료일보다 이후일때 예외 처리

* 🥅 [STMT-273] page, size min 제약 추가 및 전역 예외처리 추가

* ✅ [STMT-273] 활동 간략 목록 API 테스트 케이스 작성

* 📝 [STMT-273] 스터디 활동 간략 목록 조회 API 명세서 작성

* 🎨 [STMT-273] 코드 포맷팅

* 🩹 [STMT-273] api 명세서 오탈자 수정

* 🩹 [STMT-273] 테스트 케이스 제목 말투 통일

* 🩹 [STMT-273] 테스트 포맷팅
  • Loading branch information
05AM authored Jun 17, 2024
1 parent 38bb56f commit 838459a
Show file tree
Hide file tree
Showing 25 changed files with 1,101 additions and 347 deletions.
50 changes: 50 additions & 0 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,8 @@ include::{snippets}/create-activity/fail/not-exists-study/response-fields.adoc[]

스터디 활동 상세 목록을 조회하는 API입니다.

* 정렬 기준: 생성일

==== GET /api/v1/studies/activities/detail
===== 요청
include::{snippets}/get-activity-details-by-condition/success/http-request.adoc[]
Expand All @@ -507,6 +509,54 @@ include::{snippets}/get-activity-details-by-condition/fail/study-not-found/respo
include::{snippets}/get-activity-details-by-condition/fail/study-not-found/response-fields.adoc[]


=== 스터디 활동 간략 목록 조회

스터디 활동 간략 목록을 조회하는 API입니다. 페이지네이션과 단순 조회를 모두 지원합니다.

* 날짜 기준
- 활동 유형 `MEET`: 활동 시작일
- 활동 유형 `ASSIGNMENT`: 활동 종료일
- 활동 유형 `DEFAULT`: 생성일

* 정렬 기준
- 활동 유형 `MEET`: 활동 시작일
- 활동 유형 `ASSIGNMENT`: 활동 종료일
- 활동 유형 `DEFAULT`: 생성일
- 활동 유형 조건이 없는 경우: 생성일

==== GET /api/v1/studies/activities/brief
===== 요청
include::{snippets}/get-activity-briefs-by-condition/success/http-request.adoc[]
include::{snippets}/get-activity-briefs-by-condition/success/request-headers.adoc[]
include::{snippets}/get-activity-briefs-by-condition/success/query-parameters.adoc[]

===== 응답 성공 (200)
include::{snippets}/get-activity-briefs-by-condition/success/response-body.adoc[]
include::{snippets}/get-activity-briefs-by-condition/success/response-fields.adoc[]

===== 응답 실패 (400)
.불완전한 페이지네이션 요청변수로 요청하는 경우
include::{snippets}/get-activity-briefs-by-condition/fail/pagination-incomplete/response-body.adoc[]
include::{snippets}/get-activity-briefs-by-condition/fail/pagination-incomplete/response-fields.adoc[]

.0보다 작은 페이지네이션 요청변수로 요청하는 경우
include::{snippets}/get-activity-briefs-by-condition/fail/pagination-invalid/response-body.adoc[]
include::{snippets}/get-activity-briefs-by-condition/fail/pagination-invalid/response-fields.adoc[]

.유효하지 않은 활동 유형을 요청한 경우
include::{snippets}/get-activity-briefs-by-condition/fail/activity-category-invalid/response-body.adoc[]
include::{snippets}/get-activity-briefs-by-condition/fail/activity-category-invalid/response-fields.adoc[]

.시작기준일이 종료기준일보다 이후인 경우
include::{snippets}/get-activity-briefs-by-condition/fail/period_invalid/response-body.adoc[]
include::{snippets}/get-activity-briefs-by-condition/fail/period_invalid/response-fields.adoc[]

===== 응답 실패 (404)
.존재하지 않는 스터디 ID를 요청한 경우
include::{snippets}/get-activity-briefs-by-condition/fail/study-not-found/response-body.adoc[]
include::{snippets}/get-activity-briefs-by-condition/fail/study-not-found/response-fields.adoc[]


=== 스터디 활동 단일 조회

스터디 활동을 단일 조회하는 API입니다.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
package com.stumeet.server.activity.adapter.in;

import java.time.LocalDateTime;

import com.stumeet.server.activity.adapter.in.response.ActivityDetailResponse;
import com.stumeet.server.activity.adapter.in.response.ActivityListBriefResponses;
import com.stumeet.server.activity.adapter.in.response.ActivityListDetailedPageResponses;
import com.stumeet.server.activity.application.port.in.ActivityQueryUseCase;
import com.stumeet.server.activity.application.port.in.query.ActivityListBriefQuery;
import com.stumeet.server.activity.application.port.in.query.ActivityListDetailedQuery;
import com.stumeet.server.common.annotation.WebAdapter;
import com.stumeet.server.common.auth.model.LoginMember;
import com.stumeet.server.common.model.ApiResponse;
import com.stumeet.server.common.response.SuccessCode;

import jakarta.validation.constraints.Min;
import lombok.RequiredArgsConstructor;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
Expand All @@ -22,35 +28,69 @@
@WebAdapter
@RequestMapping("/api/v1")
@RequiredArgsConstructor
@Validated
public class ActivityQueryApi {

private final ActivityQueryUseCase activityQueryUseCase;

@GetMapping("/studies/{studyId}/activities/{activityId}")
public ResponseEntity<ApiResponse<ActivityDetailResponse>> getById(
@PathVariable Long studyId,
@PathVariable Long activityId,
@AuthenticationPrincipal LoginMember member
@PathVariable Long studyId,
@PathVariable Long activityId,
@AuthenticationPrincipal LoginMember member
) {
ActivityDetailResponse response = activityQueryUseCase.getById(studyId, activityId, member.getId());
return ResponseEntity.status(HttpStatus.OK)
.body(ApiResponse.success(SuccessCode.GET_SUCCESS, response));
.body(ApiResponse.success(SuccessCode.GET_SUCCESS, response));
}

@GetMapping("/studies/activities/detail")
public ResponseEntity<ApiResponse<ActivityListDetailedPageResponses>> getDetailsByCondition(
@AuthenticationPrincipal LoginMember member,
@RequestParam Integer size,
@RequestParam Integer page,
@RequestParam(required = false) Boolean isNotice,
@RequestParam(required = false) Long studyId,
@RequestParam(required = false) String category
@AuthenticationPrincipal LoginMember member,
@RequestParam @Min(value = 0) Integer size,
@RequestParam @Min(value = 0) Integer page,
@RequestParam(required = false) Boolean isNotice,
@RequestParam(required = false) Long studyId,
@RequestParam(required = false) String category
) {
ActivityListDetailedQuery query =
ActivityListDetailedQuery.of(size, page, isNotice, member.getId(), studyId, category);
ActivityListDetailedQuery query = ActivityListDetailedQuery.builder()
.size(size)
.page(page)
.isNotice(isNotice)
.studyId(studyId)
.memberId(member.getId())
.categoryName(category)
.build();
ActivityListDetailedPageResponses response = activityQueryUseCase.getDetails(query);

return ResponseEntity.status(HttpStatus.OK)
.body(ApiResponse.success(SuccessCode.GET_SUCCESS, response));
.body(ApiResponse.success(SuccessCode.GET_SUCCESS, response));
}

@GetMapping("/studies/activities/brief")
public ResponseEntity<ApiResponse<ActivityListBriefResponses>> getBriefsByCondition(
@AuthenticationPrincipal LoginMember member,
@RequestParam(required = false) @Min(value = 0) Integer size,
@RequestParam(required = false) @Min(value = 0) Integer page,
@RequestParam(required = false) Boolean isNotice,
@RequestParam(required = false) Long studyId,
@RequestParam(required = false) String category,
@RequestParam(required = false) LocalDateTime fromDate,
@RequestParam(required = false) LocalDateTime toDate
) {
ActivityListBriefQuery query = ActivityListBriefQuery.builder()
.size(size)
.page(page)
.isNotice(isNotice)
.studyId(studyId)
.memberId(member.getId())
.categoryName(category)
.fromDate(fromDate)
.toDate(toDate)
.build();
ActivityListBriefResponses response = activityQueryUseCase.getBriefs(query);

return ResponseEntity.status(HttpStatus.OK)
.body(ApiResponse.success(SuccessCode.GET_SUCCESS, response));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.stumeet.server.activity.adapter.in.response;

import java.time.LocalDateTime;

import com.querydsl.core.annotations.QueryProjection;
import com.stumeet.server.activity.domain.model.ActivityStatus;

import lombok.Builder;

@Builder
public record ActivityListBriefResponse(
Long id,
String category,
String title,
LocalDateTime startDate,
LocalDateTime endDate,
String location,
ActivityStatus status,
LocalDateTime createdAt
) {
@QueryProjection
public ActivityListBriefResponse {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.stumeet.server.activity.adapter.in.response;

import java.util.List;

import lombok.Builder;

@Builder
public record ActivityListBriefResponses(
List<ActivityListBriefResponse> items,
PageInfoResponse pageInfo
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

@Builder
public record ActivityListDetailedPageResponses(
List<ActivityListDetailedPageResponse> items,
PageInfoResponse pageInfo
List<ActivityListDetailedPageResponse> items,
PageInfoResponse pageInfo
) {
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.stumeet.server.activity.adapter.out.mapper;

import com.stumeet.server.activity.domain.model.ActivityStatus;
import com.stumeet.server.activity.domain.model.CommonStatus;

import jakarta.persistence.AttributeConverter;

public class ActivityStatusConverter implements AttributeConverter<ActivityStatus, String> {
Expand All @@ -11,6 +13,9 @@ public String convertToDatabaseColumn(ActivityStatus activityStatus) {

@Override
public ActivityStatus convertToEntityAttribute(String s) {
if (s == null) {
return CommonStatus.NON_PARTICIPATION;
}
return ActivityStatus.findByStatus(s);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package com.stumeet.server.activity.adapter.out.persistence;

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

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import com.stumeet.server.activity.adapter.in.response.ActivityListBriefResponse;
import com.stumeet.server.activity.adapter.out.mapper.ActivityPersistenceMapper;
import com.stumeet.server.activity.adapter.out.model.ActivityJpaEntity;
import com.stumeet.server.activity.application.port.out.ActivityCreatePort;
Expand Down Expand Up @@ -31,17 +35,28 @@ public Activity create(Activity activity) {
@Override
public Activity getById(Long activityId) {
ActivityJpaEntity entity = jpaActivityRepository.findById(activityId)
.orElseThrow(() -> new NotExistsActivityException(activityId));
.orElseThrow(() -> new NotExistsActivityException(activityId));

return activityPersistenceMapper.toDomain(entity);
}

@Override
public Page<Activity> getDetailPagesByCondition(
Pageable pageable, Boolean isNotice, Long studyId, ActivityCategory category) {
public Page<Activity> getDetailPagesByCondition(Pageable pageable, Boolean isNotice, Long studyId, ActivityCategory category) {
Page<ActivityJpaEntity> entities =
jpaActivityRepository.findDetailPagesByCondition(pageable, isNotice, studyId, category);
jpaActivityRepository.findDetailPagesByCondition(pageable, isNotice, studyId, category);

return activityPersistenceMapper.toDomainPages(entities);
}

@Override
public Page<ActivityListBriefResponse> getPaginatedBriefsByCondition(Pageable pageable, Boolean isNotice, Long memberId,
Long studyId, ActivityCategory category, LocalDateTime startDate, LocalDateTime endDate) {
return jpaActivityRepository.findBriefsByConditionWithPagination(pageable, isNotice, memberId, studyId, category, startDate, endDate);
}

@Override
public List<ActivityListBriefResponse> getBriefsByCondition(Boolean isNotice, Long memberId, Long studyId,
ActivityCategory category, LocalDateTime startDate, LocalDateTime endDate) {
return jpaActivityRepository.findBriefsByCondition(isNotice, memberId, studyId, category, startDate, endDate);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
package com.stumeet.server.activity.adapter.out.persistence;

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

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import com.stumeet.server.activity.adapter.in.response.ActivityListBriefResponse;
import com.stumeet.server.activity.adapter.out.model.ActivityJpaEntity;
import com.stumeet.server.activity.domain.model.ActivityCategory;

public interface JpaActivityRepositoryCustom {
Page<ActivityJpaEntity> findDetailPagesByCondition(Pageable pageable, Boolean isNotice, Long studyId, ActivityCategory category);

Page<ActivityListBriefResponse> findBriefsByConditionWithPagination(Pageable pageable, Boolean isNotice, Long memberId, Long studyId, ActivityCategory category, LocalDateTime startDate, LocalDateTime endDate);

List<ActivityListBriefResponse> findBriefsByCondition(Boolean isNotice, Long memberId, Long studyId, ActivityCategory category, LocalDateTime startDate, LocalDateTime endDate);
}
Loading

0 comments on commit 838459a

Please sign in to comment.