Skip to content

Commit

Permalink
Merge pull request #77 from Cafegory/feature-76
Browse files Browse the repository at this point in the history
[BUILD SUCCESS] 카공 모집글 수정 api
  • Loading branch information
donghyun0304 authored Apr 22, 2024
2 parents b9ba037 + 25acb78 commit 28f96f8
Show file tree
Hide file tree
Showing 9 changed files with 422 additions and 5 deletions.
15 changes: 15 additions & 0 deletions src/main/java/com/example/demo/controller/StudyOnceController.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.example.demo.dto.study.StudyOnceJoinResult;
import com.example.demo.dto.study.StudyOnceSearchRequest;
import com.example.demo.dto.study.StudyOnceSearchResponse;
import com.example.demo.dto.study.StudyOnceUpdateRequest;
import com.example.demo.dto.study.UpdateAttendanceRequest;
import com.example.demo.dto.study.UpdateAttendanceResponse;
import com.example.demo.exception.CafegoryException;
Expand Down Expand Up @@ -72,6 +73,20 @@ public ResponseEntity<StudyOnceSearchResponse> create(@RequestBody StudyOnceCrea
return ResponseEntity.ok(response);
}

@PatchMapping("/{studyOnceId:[0-9]+}")
public ResponseEntity<StudyOnceSearchResponse> update(@PathVariable Long studyOnceId,
@RequestBody StudyOnceUpdateRequest request,
@RequestHeader("Authorization") String authorization) {
long leaderId = cafegoryTokenManager.getIdentityId(authorization);
if (studyOnceService.doesOnlyStudyLeaderExist(studyOnceId)) {
studyOnceService.updateStudyOnce(leaderId, studyOnceId, request, LocalDateTime.now());
} else {
studyOnceService.updateStudyOncePartially(leaderId, studyOnceId, request, LocalDateTime.now());
}
StudyOnceSearchResponse response = studyOnceService.findStudyOnce(studyOnceId, LocalDateTime.now());
return ResponseEntity.ok(response);
}

@PostMapping("/{studyOnceId:[0-9]+}")
public ResponseEntity<StudyOnceJoinResult> tryJoin(@PathVariable Long studyOnceId,
@RequestHeader("Authorization") String authorization) {
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/example/demo/domain/study/StudyMember.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,8 @@ public boolean isConflictWith(LocalDateTime start, LocalDateTime end) {
end) || studyStartDateTime.isEqual(end));
}

public boolean isLeader(Member member) {
return this.id.getMemberId().equals(member.getId());
}

}
50 changes: 49 additions & 1 deletion src/main/java/com/example/demo/domain/study/StudyOnce.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.springframework.lang.NonNull;
import org.thymeleaf.util.StringUtils;

import com.example.demo.domain.cafe.Cafe;
import com.example.demo.domain.member.Member;
import com.example.demo.exception.CafegoryException;
Expand All @@ -36,6 +39,8 @@
@Table(name = "study_once")
public class StudyOnce {

private static final int LIMIT_MEMBER_CAPACITY = 5;

@Id
@GeneratedValue
@Column(name = "study_once_id")
Expand Down Expand Up @@ -64,11 +69,13 @@ private StudyOnce(Long id, String name, Cafe cafe, LocalDateTime startDateTime,
int maxMemberCount, int nowMemberCount, boolean isEnd, boolean ableToTalk, Member leader) {
validateStartDateTime(startDateTime);
validateStudyOnceTime(startDateTime, endDateTime);
validateMaxMemberCount(maxMemberCount);
this.id = id;
this.name = name;
this.cafe = cafe;
this.startDateTime = startDateTime;
this.endDateTime = endDateTime;
validateNowMemberCountOverMaxLimit(nowMemberCount, maxMemberCount);
this.maxMemberCount = maxMemberCount;
this.nowMemberCount = nowMemberCount;
this.isEnd = isEnd;
Expand Down Expand Up @@ -97,6 +104,18 @@ private static void validateStudyOnceTime(LocalDateTime startDateTime, LocalDate
}
}

private void validateMaxMemberCount(int maxMemberCount) {
if (maxMemberCount > LIMIT_MEMBER_CAPACITY) {
throw new CafegoryException(STUDY_ONCE_LIMIT_MEMBER_CAPACITY);
}
}

private void validateNowMemberCountOverMaxLimit(int nowMemberCount, int maxMemberCount) {
if (nowMemberCount > maxMemberCount) {
throw new CafegoryException(STUDY_ONCE_CANNOT_REDUCE_BELOW_CURRENT);
}
}

public void tryJoin(Member memberThatExpectedToJoin, LocalDateTime requestTime) {
validateJoinRequestTime(requestTime);
validateDuplicateJoin(memberThatExpectedToJoin);
Expand Down Expand Up @@ -147,7 +166,7 @@ private void validateQuitRequestTime(LocalDateTime requestTime) {
}
}

public void changeCafe(Cafe cafe) {
public void changeCafe(@NonNull Cafe cafe) {
this.cafe = cafe;
cafe.getStudyOnceGroup().add(this);
}
Expand All @@ -160,4 +179,33 @@ public boolean canJoin(LocalDateTime baseDateTime) {
Duration between = Duration.between(baseDateTime, startDateTime);
return between.toSeconds() >= 60 * 60;
}

public void changeName(String name) {
if (StringUtils.isEmptyOrWhitespace(name)) {
throw new CafegoryException(STUDY_ONCE_NAME_EMPTY_OR_WHITESPACE);
}
this.name = name;
}

public void changeStudyOnceTime(LocalDateTime startDateTime, LocalDateTime endDateTime) {
validateStartDateTime(startDateTime);
validateStudyOnceTime(startDateTime, endDateTime);
this.startDateTime = startDateTime;
this.endDateTime = endDateTime;
}

public void changeMaxMemberCount(int maxMemberCount) {
validateMaxMemberCount(maxMemberCount);
validateNowMemberCountOverMaxLimit(this.nowMemberCount, maxMemberCount);
this.maxMemberCount = maxMemberCount;
}

public void changeCanTalk(boolean ableToTalk) {
this.ableToTalk = ableToTalk;
}

public boolean doesOnlyLeaderExist() {
return this.studyMembers.size() == 1 && this.studyMembers.get(0).isLeader(this.leader);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.example.demo.dto.study;

import java.time.LocalDateTime;

import org.springframework.lang.Nullable;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@AllArgsConstructor
@NoArgsConstructor
public class StudyOnceUpdateRequest {

@Nullable
private Long cafeId;
@Nullable
private String name;
@Nullable
private LocalDateTime startDateTime;
@Nullable
private LocalDateTime endDateTime;
@Nullable
private int maxMemberCount;
@Nullable
private boolean canTalk;
}
3 changes: 3 additions & 0 deletions src/main/java/com/example/demo/exception/ExceptionType.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public enum ExceptionType {
STUDY_ONCE_SHORT_DURATION(BAD_REQUEST, "카공 시간은 1시간 이상이어야 합니다."),
STUDY_ONCE_LONG_DURATION(BAD_REQUEST, "카공 시간은 5시간 미만이어야 합니다."),
STUDY_ONCE_TOO_MUCH_STUDY_IN_CAFE(BAD_REQUEST, "이 카페에 이 인원의 카공을 더이상 생성할 수 없습니다."),
STUDY_ONCE_LIMIT_MEMBER_CAPACITY(BAD_REQUEST, "카공 최대 참여 인원 수는 5명 입니다."),
STUDY_ONCE_CANNOT_REDUCE_BELOW_CURRENT(BAD_REQUEST, "카공 최대 참여 인원 수는 현재 참여 신청중인 인원보다 적을 수 없습니다."),
STUDY_ONCE_CONFLICT_TIME(CONFLICT, "해당 시간에 참여중인 카공이 이미 있습니다."),
STUDY_ONCE_DUPLICATE(CONFLICT, "이미 참여중인 카공입니다."),
STUDY_ONCE_FULL(CONFLICT, "카공 신청 가능 인원을 초과하였습니다."),
Expand All @@ -37,6 +39,7 @@ public enum ExceptionType {
STUDY_ONCE_COMMENT_PERMISSION_DENIED(FORBIDDEN, "댓글을 작성한 회원 본인만 수정 할 권한이 있습니다."),
STUDY_ONCE_PARENT_COMMENT_MODIFICATION_BLOCKED(FORBIDDEN, "답변이 존재하는 질문은 수정 할 수 없습니다."),
STUDY_ONCE_PARENT_COMMENT_REMOVAL_BLOCKED(FORBIDDEN, "답변이 존재하는 질문은 삭제 할 수 없습니다."),
STUDY_ONCE_NAME_EMPTY_OR_WHITESPACE(BAD_REQUEST, "스터디 이름은 null, 빈 값, 혹은 공백만으로 이루어질 수 없습니다."),
MEMBER_NOT_FOUND(NOT_FOUND, "없는 회원입니다."),
REVIEW_NOT_FOUND(NOT_FOUND, "없는 리뷰입니다."),
REVIEW_OVER_CONTENT_SIZE(BAD_REQUEST, "리뷰 글자수가 최대 글자수 이하여야 합니다."),
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/example/demo/service/study/StudyOnceService.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.example.demo.dto.study.StudyOnceCreateRequest;
import com.example.demo.dto.study.StudyOnceSearchRequest;
import com.example.demo.dto.study.StudyOnceSearchResponse;
import com.example.demo.dto.study.StudyOnceUpdateRequest;
import com.example.demo.dto.study.UpdateAttendanceRequest;
import com.example.demo.dto.study.UpdateAttendanceResponse;

Expand All @@ -31,5 +32,14 @@ UpdateAttendanceResponse updateAttendances(long leaderId, long studyOnceId,

StudyMembersResponse findStudyMembersById(Long studyOnceId);

StudyOnceSearchResponse findStudyOnce(Long studyOnceId, LocalDateTime now);

boolean doesOnlyStudyLeaderExist(Long studyOnceId);

boolean isStudyOnceLeader(Long memberId, Long studyOnceId);

void updateStudyOnce(long requestedMemberId, long studyOnceId, StudyOnceUpdateRequest request, LocalDateTime now);

void updateStudyOncePartially(long requestedMemberId, long studyOnceId, StudyOnceUpdateRequest request,
LocalDateTime now);
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.example.demo.dto.study.StudyOnceCreateRequest;
import com.example.demo.dto.study.StudyOnceSearchRequest;
import com.example.demo.dto.study.StudyOnceSearchResponse;
import com.example.demo.dto.study.StudyOnceUpdateRequest;
import com.example.demo.dto.study.UpdateAttendanceRequest;
import com.example.demo.dto.study.UpdateAttendanceResponse;
import com.example.demo.exception.CafegoryException;
Expand Down Expand Up @@ -188,6 +189,39 @@ public StudyOnceSearchResponse createStudy(long leaderId, StudyOnceCreateRequest
return studyOnceMapper.toStudyOnceSearchResponse(saved, canJoin);
}

@Override
public void updateStudyOnce(long requestedMemberId, long studyOnceId, StudyOnceUpdateRequest request,
LocalDateTime now) {
StudyOnce studyOnce = findStudyOnceById(studyOnceId);
if (!isStudyOnceLeader(requestedMemberId, studyOnceId)) {
throw new CafegoryException(STUDY_ONCE_LEADER_PERMISSION_DENIED);
}
if (request.getCafeId() != null) {
studyOnce.changeCafe(findCafeById(request.getCafeId()));
}
if (request.getName() != null) {
studyOnce.changeName(request.getName());
}
if (request.getStartDateTime() != null && request.getEndDateTime() != null) {
studyOnce.changeStudyOnceTime(request.getStartDateTime(), request.getEndDateTime());
}
studyOnce.changeMaxMemberCount(request.getMaxMemberCount());
studyOnce.changeCanTalk(request.isCanTalk());
}

@Override
public void updateStudyOncePartially(long requestedMemberId, long studyOnceId, StudyOnceUpdateRequest request,
LocalDateTime now) {
StudyOnce studyOnce = findStudyOnceById(studyOnceId);
if (!isStudyOnceLeader(requestedMemberId, studyOnceId)) {
throw new CafegoryException(STUDY_ONCE_LEADER_PERMISSION_DENIED);
}
if (request.getCafeId() != null) {
studyOnce.changeCafe(findCafeById(request.getCafeId()));
}
studyOnce.changeMaxMemberCount(request.getMaxMemberCount());
}

@Override
public Long changeCafe(Long requestMemberId, Long studyOnceId, final Long changingCafeId) {
final StudyOnce studyOnce = findStudyOnceById(studyOnceId);
Expand All @@ -205,6 +239,18 @@ public StudyMembersResponse findStudyMembersById(Long studyOnceId) {
return new StudyMembersResponse(memberResponses);
}

@Override
public StudyOnceSearchResponse findStudyOnce(Long studyOnceId, LocalDateTime now) {
StudyOnce studyOnce = findStudyOnceById(studyOnceId);
return studyOnceMapper.toStudyOnceSearchResponse(studyOnce, studyOnce.canJoin(now));
}

@Override
public boolean doesOnlyStudyLeaderExist(Long studyOnceId) {
StudyOnce studyOnce = findStudyOnceById(studyOnceId);
return studyOnce.doesOnlyLeaderExist();
}

@Override
public boolean isStudyOnceLeader(Long memberId, Long studyOnceId) {
StudyOnce studyOnce = findStudyOnceById(studyOnceId);
Expand Down
82 changes: 82 additions & 0 deletions src/test/java/com/example/demo/domain/study/StudyOnceTest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.example.demo.domain.study;

import static com.example.demo.exception.ExceptionType.*;
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;

import java.time.LocalDateTime;
Expand All @@ -15,6 +16,7 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;

import com.example.demo.domain.member.Member;
import com.example.demo.exception.CafegoryException;
Expand Down Expand Up @@ -222,4 +224,84 @@ void tryQuitFailByNotJoin() {
.isInstanceOf(CafegoryException.class)
.hasMessage(STUDY_ONCE_TRY_QUIT_NOT_JOIN.getErrorMessage());
}

@Test
@DisplayName("스터디 이름 변경, null 검증")
void validate_null_by_changeName() {
Member leader = Member.builder().id(LEADER_ID).build();
StudyOnce studyOnce = makeStudy(leader, NOW.plusHours(4), NOW.plusHours(8));
assertThatThrownBy(() -> studyOnce.changeName(null))
.isInstanceOf(CafegoryException.class)
.hasMessage(STUDY_ONCE_NAME_EMPTY_OR_WHITESPACE.getErrorMessage());
}

@ParameterizedTest
@ValueSource(strings = {"", " "})
@DisplayName("스터디 이름 변경, 빈값, 공백문자 검증")
void validate_empty_or_whitespace_by_changeName(String value) {
Member leader = Member.builder().id(LEADER_ID).build();
StudyOnce studyOnce = makeStudy(leader, NOW.plusHours(4), NOW.plusHours(8));
assertThatThrownBy(() -> studyOnce.changeName(value))
.isInstanceOf(CafegoryException.class)
.hasMessage(STUDY_ONCE_NAME_EMPTY_OR_WHITESPACE.getErrorMessage());
}

@Test
@DisplayName("최대 참여인원 5명이다. 정상동작")
void changeMaxMemberCount() {
Member leader = Member.builder().id(LEADER_ID).build();
StudyOnce studyOnce = makeStudy(leader, NOW.plusHours(4), NOW.plusHours(8));
assertDoesNotThrow(() -> studyOnce.changeMaxMemberCount(5));
}

@Test
@DisplayName("최대 참여인원은 5명이다. 6명이면 예외가 터진다.")
void validate_maxMemberCount_by_changeMaxMemberCount() {
Member leader = Member.builder().id(LEADER_ID).build();
StudyOnce studyOnce = makeStudy(leader, NOW.plusHours(4), NOW.plusHours(8));
assertThatThrownBy(() -> studyOnce.changeMaxMemberCount(6))
.isInstanceOf(CafegoryException.class)
.hasMessage(STUDY_ONCE_LIMIT_MEMBER_CAPACITY.getErrorMessage());
}

@Test
@DisplayName("최대 참여 인원보다 현재 참석예정인 인원이 크다면 예외가 터진다.")
void validate_maxOrNowMemberCount_by_changeMaxMemberCount() {
Member leader = Member.builder().id(LEADER_ID).build();
StudyOnce studyOnce = StudyOnce.builder()
.startDateTime(NOW.plusHours(4))
.endDateTime(NOW.plusHours(8))
.maxMemberCount(4)
.nowMemberCount(1)
.leader(leader)
.build();

assertThatThrownBy(() -> studyOnce.changeMaxMemberCount(0))
.isInstanceOf(CafegoryException.class)
.hasMessage(STUDY_ONCE_CANNOT_REDUCE_BELOW_CURRENT.getErrorMessage());
}

@Test
@DisplayName("카공에 참여인원이 카공장만 있으면 true 반환")
void doesOnlyLeaderExist_then_true() {
Member leader = Member.builder().id(LEADER_ID).build();
StudyOnce studyOnce = makeStudy(leader, NOW.plusHours(4), NOW.plusHours(8));

boolean doesOnlyLeaderExist = studyOnce.doesOnlyLeaderExist();
assertThat(doesOnlyLeaderExist).isTrue();
}

@Test
@DisplayName("카공에 참여인원이 여러명이라면 false 반환")
void doesOnlyLeaderExist_then_false() {
Member leader = Member.builder().id(LEADER_ID).build();
Member member = makeMemberWithStudyOnce(NOW.plusHours(9), NOW.plusHours(13));
StudyOnce studyOnce = makeStudy(leader, NOW.plusHours(4), NOW.plusHours(8));

studyOnce.tryJoin(member, NOW.plusHours(3).minusSeconds(1));

boolean doesOnlyLeaderExist = studyOnce.doesOnlyLeaderExist();
assertThat(doesOnlyLeaderExist).isFalse();
}

}
Loading

0 comments on commit 28f96f8

Please sign in to comment.