Skip to content

Commit

Permalink
✨ [STMT-295] 관리자 여부, 포도알 전송 가능 여부 판단 API 분리 및 멤버 상세 화면 통합 API 구현 (#153)
Browse files Browse the repository at this point in the history
* 📝 [STMT-291] 스터디 멤버 상세 조회 API 응답 성공 요청본문 명세서 추가

* ♻️ [STMT-291] 멤버 상세 정보 api에서 포도알 전송 여부 api 분리

* ✨ [STMT-291] 로그인한 멤버가 스터디 관리자 판별 API 구현

* ✨ [STMT-291] 관리자 여부, 포도알 전송 가능 여부 조회에 스터디 존재 검증 로직 추가

* ✅ [STMT-291] 관리자 여부 조회 API 테스트 작성

* ✅ [STMT-291] 멤버 포도알 전송 가능 여부 조회 API 테스트 작성

* ✨ [STMT-291] 활동 목록 간략 조회 API에 memberId 쿼리 파라미터 추가

* ✅ [STMT-291] 활동 목록 간략 조회 API memberId 쿼리 파라미터 추가 API 테스트에 반영

* ✨ [STMT-295] 스터디 멤버 상세 화면 조회 API 구현

* ✅ [STMT-295] 스터디 멤버 상세 화면 조회 API 테스트 작성

* ✨ [STMT-291] 스터디 홈 화면 조회 통합 API 구현

* ✅ [STMT-291] 스터디 홈 화면 조회 통합 API 테스트 작성

* 🩹 [STMT-291] 스터디 멤버 관리자 여부 조회 문서화 PATH 오류 수정

* 📝 [STMT-291] 포도알 전송 가능 여부, 관리자 여부, 통합 API 명세서 작성

* ✨ [STMT-291] 활동 상세 조회에 link 속성 추가
  • Loading branch information
05AM authored Sep 28, 2024
1 parent f1cfc18 commit f4dd99b
Show file tree
Hide file tree
Showing 23 changed files with 716 additions and 27 deletions.
116 changes: 112 additions & 4 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ include::{snippets}/create-study/success/request-headers.adoc[]
===== 파트
include::{snippets}/create-study/success/request-parts.adoc[]

===== 요청 본문 매개변수
===== 요청 파트 매개변수
include::{snippets}/create-study/success/request-part-request-fields.adoc[]

===== 응답 성공 (200)
Expand Down Expand Up @@ -290,7 +290,7 @@ include::{snippets}/update-study/success/request-headers.adoc[]
===== 파트
include::{snippets}/update-study/success/request-parts.adoc[]

===== 요청 본문 매개변수
===== 요청 파트 매개변수
include::{snippets}/update-study/success/request-part-request-fields.adoc[]

===== 응답 성공 (200)
Expand Down Expand Up @@ -357,6 +357,10 @@ include::{snippets}/get-study-member-detail/success/request-headers.adoc[]
====== 경로 변수
include::{snippets}/get-study-member-detail/success/path-parameters.adoc[]

===== 응답 성공 (200)
include::{snippets}/get-study-member-detail/success/response-body.adoc[]
include::{snippets}/get-study-member-detail/success/response-fields.adoc[]

===== 응답 실패 (403)
.요청자가 스터디의 멤버가 아닌 경우
include::{snippets}/get-study-member-detail/fail/is-not-study-member/response-body.adoc[]
Expand Down Expand Up @@ -500,6 +504,55 @@ include::{snippets}/delegate-admin/fail/not-admin/response-fields.adoc[]
include::{snippets}/delegate-admin/fail/member-not-joined/response-body.adoc[]
include::{snippets}/delegate-admin/fail/member-not-joined/response-fields.adoc[]

=== 관리자 여부 조회

로그인 멤버가 스터디 관리자인지 조회하는 API입니다.

==== GET /v1/api/studies/{studyId}/me/admin/check

===== 요청
include::{snippets}/get-study-member-is-admin/success/http-request.adoc[]

====== 헤더
include::{snippets}/get-study-member-is-admin/success/request-headers.adoc[]

====== 경로 변수
include::{snippets}/get-study-member-is-admin/success/path-parameters.adoc[]

===== 응답 성공 (200)
include::{snippets}/get-study-member-is-admin/success/response-body.adoc[]
include::{snippets}/get-study-member-is-admin/success/response-fields.adoc[]

===== 응답 실패 (404)
.스터디가 존재하지 않을 경우
include::{snippets}/get-study-member-is-admin/fail/study-not-found/response-body.adoc[]
include::{snippets}/get-study-member-is-admin/fail/study-not-found/response-fields.adoc[]

=== 포도알 전송 가능 여부 조회

로그인 멤버의 포도알 전송 가능 여부를 조회하는 API입니다.

==== GET /v1/api/studies/{studyId}/me/grapes/available

===== 요청
include::{snippets}/get-study-member-can-send-grape/success/http-request.adoc[]

====== 헤더
include::{snippets}/get-study-member-can-send-grape/success/request-headers.adoc[]

====== 경로 변수
include::{snippets}/get-study-member-can-send-grape/success/path-parameters.adoc[]

===== 응답 성공 (200)
include::{snippets}/get-study-member-can-send-grape/success/response-body.adoc[]
include::{snippets}/get-study-member-can-send-grape/success/response-fields.adoc[]

===== 응답 실패 (404)
.스터디가 존재하지 않을 경우
include::{snippets}/get-study-member-can-send-grape/fail/study-not-found/response-body.adoc[]
include::{snippets}/get-study-member-can-send-grape/fail/study-not-found/response-fields.adoc[]


== 파일 관리

=== Presigned URL 발급
Expand All @@ -508,7 +561,7 @@ include::{snippets}/delegate-admin/fail/member-not-joined/response-fields.adoc[]

해당 API는 1시간동안 유효합니다.

==== POST /api/v1/presigned-url
==== POST /api/v1/presigned-urls

===== 요청

Expand Down Expand Up @@ -805,4 +858,59 @@ include::{snippets}/delete-activity/fail/forbidden/response-fields.adoc[]

.스터디의 멤버가 아닌 경우
include::{snippets}/delete-activity/fail/not-joined-member/response-body.adoc[]
include::{snippets}/delete-activity/fail/not-joined-member/response-fields.adoc[]
include::{snippets}/delete-activity/fail/not-joined-member/response-fields.adoc[]

== 통합 API

=== 스터디 홈 화면 조회

스터디 홈 화면을 조회하는 API입니다.

==== GET /api/external/v1/studies/{studyId}

===== 요청
include::{snippets}/get-study-home-full/success/http-request.adoc[]

====== 헤더
include::{snippets}/get-study-home-full/success/request-headers.adoc[]

====== 경로 변수
include::{snippets}/get-study-home-full/success/path-parameters.adoc[]

===== 응답 성공 (200)
include::{snippets}/get-study-home-full/success/response-body.adoc[]
include::{snippets}/get-study-home-full/success/response-fields.adoc[]

===== 응답 실패 (404)
.스터디가 존재하지 않는 경우
include::{snippets}/get-study-home-full/fail/study-not-found/response-body.adoc[]
include::{snippets}/get-study-home-full/fail/study-not-found/response-fields.adoc[]

=== 스터디 멤버 상세 화면 조회

스터디 멤버 상세 화면을 조회하는 API입니다.

==== GET /api/external/v1/studies/{studyId}/members/{memberId}

===== 요청
include::{snippets}/get-study-member-detail-full/success/http-request.adoc[]

====== 헤더
include::{snippets}/get-study-member-detail-full/success/request-headers.adoc[]

====== 경로 변수
include::{snippets}/get-study-member-detail-full/success/path-parameters.adoc[]

===== 응답 성공 (200)
include::{snippets}/get-study-member-detail-full/success/response-body.adoc[]
include::{snippets}/get-study-member-detail-full/success/response-fields.adoc[]

===== 응답 실패 (403)
.해당 스터디에 가입한 멤버가 요청한 것이 아닌 경우
include::{snippets}/get-study-member-detail-full/fail/is-not-study-member/response-body.adoc[]
include::{snippets}/get-study-member-detail-full/fail/is-not-study-member/response-fields.adoc[]

===== 응답 실패 (404)
.스터디가 존재하지 않는 경우
include::{snippets}/get-study-member-detail-full/fail/study-not-found/response-body.adoc[]
include::{snippets}/get-study-member-detail-full/fail/study-not-found/response-fields.adoc[]
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public ResponseEntity<ApiResponse<ActivityListBriefResponses>> getBriefsByCondit
@RequestParam(required = false) @Min(value = 0) Integer page,
@RequestParam(required = false) Boolean isNotice,
@RequestParam(required = false) Long studyId,
@RequestParam(required = false) Long memberId,
@RequestParam(required = false) String category,
@RequestParam(required = false) LocalDateTime fromDate,
@RequestParam(required = false) LocalDateTime toDate
Expand All @@ -83,7 +84,7 @@ public ResponseEntity<ApiResponse<ActivityListBriefResponses>> getBriefsByCondit
.page(page)
.isNotice(isNotice)
.studyId(studyId)
.memberId(member.getId())
.memberId(memberId != null ? memberId : member.getId())
.categoryName(category)
.fromDate(fromDate)
.toDate(toDate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public record ActivityDetailResponse(
LocalDateTime startDate,
LocalDateTime endDate,
String location,
String link,
LocalDateTime createdAt,
boolean isAuthor,
boolean isAdmin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public Activity toDomain(ActivityJpaEntity entity) {
.startDate(entity.getStartDate())
.endDate(entity.getEndDate())
.location(entity.getLocation())
.link(entity.getLink())
.createdAt(entity.getCreatedAt())
.location(entity.getLocation())
.build();
Expand Down Expand Up @@ -65,6 +66,7 @@ public ActivityJpaEntity toEntity(Activity domain) {
.startDate(domain.getStartDate())
.endDate(domain.getEndDate())
.location(domain instanceof Meet meet ? meet.getLocation() : null)
.link(domain.getLink())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,8 @@ public class ActivityJpaEntity extends BaseTimeEntity {
@Column(name = "location")
@Comment("활동 장소")
private String location;

@Column(name = "link")
@Comment("링크")
private String link;
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public ActivityDetailResponse toDetailResponse(
.startDate(activity.getStartDate())
.endDate(activity.getEndDate())
.location(activity.getLocation())
.link(activity.getLink())
.createdAt(activity.getCreatedAt())
.isAuthor(isAuthor)
.isAdmin(isAdmin)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.stumeet.server.bff.adapter.in.app;

import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.stumeet.server.activity.adapter.in.response.ActivityListDetailedPageResponse;
import com.stumeet.server.activity.application.port.in.ActivityQueryUseCase;
import com.stumeet.server.activity.application.port.in.query.ActivityListDetailedQuery;
import com.stumeet.server.bff.adapter.in.app.response.StudyDetailFullResponse;
import com.stumeet.server.common.auth.model.LoginMember;
import com.stumeet.server.common.model.ApiResponse;
import com.stumeet.server.common.response.SuccessCode;
import com.stumeet.server.study.adapter.in.web.response.StudyDetailResponse;
import com.stumeet.server.study.application.port.in.StudyQueryUseCase;
import com.stumeet.server.studymember.application.port.in.StudyMemberQueryUseCase;

import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("/api/external/v1")
@RequiredArgsConstructor
public class StudyHubApi {

private final StudyQueryUseCase studyQueryUseCase;
private final StudyMemberQueryUseCase studyMemberQueryUseCase;
private final ActivityQueryUseCase activityQueryUseCase;

@GetMapping("/studies/{studyId}")
public ResponseEntity<ApiResponse<StudyDetailFullResponse>> getStudyDetailFull(
@AuthenticationPrincipal LoginMember member,
@PathVariable(name = "studyId") Long studyId
) {
StudyDetailResponse studyDetailResponse = studyQueryUseCase.getStudyDetailById(studyId);

ActivityListDetailedQuery noticeQuery = getNoticeQuery(member.getId(), studyId);
ActivityListDetailedPageResponse activityNotice = activityQueryUseCase.getDetails(noticeQuery)
.items()
.getFirst();

boolean isAdmin = studyMemberQueryUseCase.isMemberAdmin(studyId, member.getId()).isAdmin();
boolean canSendGrape = studyMemberQueryUseCase.canStudyMemberSendGrape(studyId, member.getId()).canSendGrape();

StudyDetailFullResponse response = new StudyDetailFullResponse(
studyDetailResponse,
activityNotice,
isAdmin,
canSendGrape
);

return ResponseEntity.ok(
ApiResponse.success(SuccessCode.GET_SUCCESS, response)
);
}

private ActivityListDetailedQuery getNoticeQuery(Long memberId, Long studyId) {
return ActivityListDetailedQuery.builder()
.memberId(memberId)
.studyId(studyId)
.page(0)
.size(1)
.isNotice(true)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.stumeet.server.bff.adapter.in.app;

import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.stumeet.server.bff.adapter.in.app.response.StudyMemberDetailFullResponse;
import com.stumeet.server.common.auth.model.LoginMember;
import com.stumeet.server.common.model.ApiResponse;
import com.stumeet.server.common.response.SuccessCode;
import com.stumeet.server.studymember.application.port.in.StudyMemberQueryUseCase;
import com.stumeet.server.studymember.application.port.in.response.StudyMemberDetailResponse;

import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("/api/external/v1")
@RequiredArgsConstructor
public class StudyMemberHubApi {

private final StudyMemberQueryUseCase studyMemberQueryUseCase;

@GetMapping("/studies/{studyId}/members/{memberId}")
public ResponseEntity<ApiResponse<StudyMemberDetailFullResponse>> getStudyMemberDetailFull(
@AuthenticationPrincipal LoginMember member,
@PathVariable Long studyId,
@PathVariable Long memberId
) {
StudyMemberDetailResponse studyMemberDetail = studyMemberQueryUseCase.getStudyMemberDetail(studyId, memberId, member.getId());
boolean isAdmin = studyMemberQueryUseCase.isMemberAdmin(studyId, memberId).isAdmin();
boolean canSendGrape = studyMemberQueryUseCase.canStudyMemberSendGrape(studyId, memberId).canSendGrape();

StudyMemberDetailFullResponse response = new StudyMemberDetailFullResponse(
studyMemberDetail,
isAdmin,
canSendGrape
);

return ResponseEntity.ok(
ApiResponse.success(SuccessCode.GET_SUCCESS, response)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.stumeet.server.bff.adapter.in.app.response;

import com.stumeet.server.activity.adapter.in.response.ActivityListDetailedPageResponse;
import com.stumeet.server.study.adapter.in.web.response.StudyDetailResponse;

public record StudyDetailFullResponse(
StudyDetailResponse studyDetail,
ActivityListDetailedPageResponse activityNotice,
Boolean isAdmin,
Boolean canSendGrape
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.stumeet.server.bff.adapter.in.app.response;

import com.stumeet.server.studymember.application.port.in.response.StudyMemberDetailResponse;

public record StudyMemberDetailFullResponse(
StudyMemberDetailResponse studyMemberDetailResponse,
Boolean isAdmin,
Boolean canSendGrape
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public JoinedStudiesResponse getJoinedStudiesByStatus(GetJoinedStudyCommand comm
return studyUseCaseMapper.toMyStudiesResponse(activeJoinedStudies);
}

// TODO: 완료된 스터디 조회의 경우
// case FINISHED -> {
//
// }
Expand Down
Loading

0 comments on commit f4dd99b

Please sign in to comment.