Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BE] 방 수정 기능 & 스케줄러 부분 분리 및 리팩토링(#605) #606

Merged
merged 7 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/src/main/java/corea/exception/ExceptionType.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public enum ExceptionType {
ALREADY_APPLY(HttpStatus.BAD_REQUEST, "해당 방에 이미 참여했습니다."),
NOT_ALREADY_APPLY(HttpStatus.BAD_REQUEST, "아직 참여하지 않은 방입니다."),
ROOM_STATUS_INVALID(HttpStatus.BAD_REQUEST, "방이 마감되었습니다."),
MEMBER_IS_NOT_MANAGER(HttpStatus.BAD_REQUEST, "매니저가 아닙니다."),
ROOM_PARTICIPANT_EXCEED(HttpStatus.BAD_REQUEST, "방 참여 인원 수가 최대입니다."),
PARTICIPANT_SIZE_LACK(HttpStatus.BAD_REQUEST, "참여 인원이 부족하여 매칭을 진행할 수 없습니다."),
PARTICIPANT_SIZE_LACK_DUE_TO_PULL_REQUEST(HttpStatus.BAD_REQUEST, "pull request 미제출로 인해 인원이 부족하여 매칭을 진행할 수 없습니다."),
Expand Down
61 changes: 8 additions & 53 deletions backend/src/main/java/corea/room/controller/RoomController.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,8 @@
import corea.auth.annotation.AccessedMember;
import corea.auth.annotation.LoginMember;
import corea.auth.domain.AuthInfo;
import corea.matchresult.dto.MatchResultResponses;
import corea.matchresult.service.MatchResultService;
import corea.room.domain.RoomStatus;
import corea.room.dto.RoomCreateRequest;
import corea.room.dto.RoomParticipantResponses;
import corea.room.dto.RoomResponse;
import corea.room.dto.RoomResponses;
import corea.room.dto.*;
import corea.room.service.RoomService;
import corea.scheduler.service.AutomaticMatchingService;
import corea.scheduler.service.AutomaticUpdateService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
Expand All @@ -25,16 +17,18 @@
public class RoomController implements RoomControllerSpecification {

private final RoomService roomService;
private final MatchResultService matchResultService;
private final AutomaticUpdateService automaticUpdateService;
private final AutomaticMatchingService automaticMatchingService;

@PostMapping
public ResponseEntity<RoomResponse> create(@LoginMember AuthInfo authInfo, @RequestBody RoomCreateRequest request) {
RoomResponse response = roomService.create(authInfo.getId(), request);

automaticMatchingService.matchOnRecruitmentDeadline(response);
automaticUpdateService.updateAtReviewDeadline(response);
return ResponseEntity.created(URI.create(String.format("/rooms/%d", response.id())))
.body(response);
}

@PutMapping
public ResponseEntity<RoomResponse> update(@LoginMember AuthInfo authInfo, @RequestBody RoomUpdateRequest request) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

명세서 있는 건가요?

RoomResponse response = roomService.update(authInfo.getId(), request);

return ResponseEntity.created(URI.create(String.format("/rooms/%d", response.id())))
.body(response);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update 응답 http status code가 201이네용??
깜빡한듯요~

Expand All @@ -52,55 +46,16 @@ public ResponseEntity<RoomParticipantResponses> participants(@PathVariable long
return ResponseEntity.ok(response);
}

@GetMapping("/{id}/reviewers")
public ResponseEntity<MatchResultResponses> reviewers(@PathVariable long id, @LoginMember AuthInfo authInfo) {
MatchResultResponses response = matchResultService.findReviewers(authInfo.getId(), id);
return ResponseEntity.ok(response);
}

@GetMapping("/{id}/reviewees")
public ResponseEntity<MatchResultResponses> reviewees(@PathVariable long id, @LoginMember AuthInfo authInfo) {
MatchResultResponses response = matchResultService.findReviewees(authInfo.getId(), id);
return ResponseEntity.ok(response);
}

@GetMapping("/participated")
public ResponseEntity<RoomResponses> participatedRooms(@LoginMember AuthInfo authInfo) {
RoomResponses response = roomService.findParticipatedRooms(authInfo.getId());
return ResponseEntity.ok(response);
}

@GetMapping("/opened")
public ResponseEntity<RoomResponses> openedRooms(@AccessedMember AuthInfo authInfo,
@RequestParam(defaultValue = "0") int page,
@RequestParam(value = "classification", defaultValue = "all") String expression) {
RoomResponses response = roomService.findRoomsWithRoomStatus(authInfo.getId(), page, expression, RoomStatus.OPEN);
return ResponseEntity.ok(response);
}

@GetMapping("/progress")
public ResponseEntity<RoomResponses> progressRooms(@AccessedMember AuthInfo authInfo,
@RequestParam(defaultValue = "0") int page,
@RequestParam(value = "classification", defaultValue = "all") String expression) {
RoomResponses response = roomService.findRoomsWithRoomStatus(authInfo.getId(), page, expression, RoomStatus.PROGRESS);
return ResponseEntity.ok(response);
}

@GetMapping("/closed")
public ResponseEntity<RoomResponses> closedRooms(@AccessedMember AuthInfo authInfo,
@RequestParam(defaultValue = "0") int page,
@RequestParam(value = "classification", defaultValue = "all") String expression) {
RoomResponses response = roomService.findRoomsWithRoomStatus(authInfo.getId(), page, expression, RoomStatus.CLOSE);
return ResponseEntity.ok(response);
}

@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable long id, @LoginMember AuthInfo authInfo) {
roomService.delete(id, authInfo.getId());

automaticMatchingService.cancel(id);
automaticUpdateService.cancel(id);

return ResponseEntity.noContent()
.build();
}
Expand Down
76 changes: 76 additions & 0 deletions backend/src/main/java/corea/room/dto/RoomUpdateRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package corea.room.dto;

import com.fasterxml.jackson.annotation.JsonFormat;
import corea.member.domain.Member;
import corea.room.domain.Room;
import corea.room.domain.RoomClassification;
import corea.room.domain.RoomStatus;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

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

@Schema(description = "방 수정 요청")
public record RoomUpdateRequest(
@Schema(description = "방 ID", example = "99")
@NotBlank
long roomId,

@Schema(description = "방 제목", example = "MVC를 아시나요?")
@NotBlank
String title,

@Schema(description = "방 내용", example = "MVC 패턴을 아시나요?")
String content,

@Schema(description = "repository 링크", example = "https://github.com/example/java-racingcar")
@NotBlank
String repositoryLink,

@Schema(description = "썸네일 링크", example = "https://gongu.copyright.or.kr/gongu/wrt/cmmn/wrtFileImageView.do?wrtSn=13301655&filePath=L2Rpc2sxL25ld2RhdGEvMjAyMS8yMS9DTFMxMDAwNC8xMzMwMTY1NV9XUlRfMjFfQ0xTMTAwMDRfMjAyMTEyMTNfMQ==&thumbAt=Y&thumbSe=b_tbumb&wrtTy=10004")
String thumbnailLink,

@Schema(description = "상호 리뷰 인원", example = "2")
@NotNull
int matchingSize,

@Schema(description = "중심으로 리뷰하면 좋은 키워드", example = "[\"TDD\", \"클린코드\"]")
List<String> keywords,

@Schema(description = "제한 참여 인원", example = "200")
@NotNull
int limitedParticipants,

@Schema(description = "모집 마감일", example = "2024-07-30 15:00")
@NotNull
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
LocalDateTime recruitmentDeadline,

@Schema(description = "리뷰 마감일", example = "2024-08-10 23:59")
@NotNull
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
LocalDateTime reviewDeadline,

@Schema(description = "방이 속하는 분야", example = "BE")
@NotNull
RoomClassification classification
) {

private static final int INITIAL_PARTICIPANTS_SIZE = 1;
private static final RoomStatus INITIAL_ROOM_STATUS = RoomStatus.OPEN;

public Room toEntity(Member manager) {
return new Room(
roomId,
title, content,
matchingSize, repositoryLink,
thumbnailLink, keywords,
INITIAL_PARTICIPANTS_SIZE, limitedParticipants,
manager, recruitmentDeadline,
reviewDeadline, classification,
INITIAL_ROOM_STATUS
);
}
}
17 changes: 17 additions & 0 deletions backend/src/main/java/corea/room/service/RoomService.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,23 @@ public RoomResponse create(long memberId, RoomCreateRequest request) {
return RoomResponse.of(room, participation.getMemberRole(), ParticipationStatus.MANAGER);
}

@Transactional
public RoomResponse update(long memberId, RoomUpdateRequest request) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P5

RoomUpdateResponse를 분리하는 건 어떠신가요?

RoomUpdateRequest가 있기도 하고,
update 메서드에서 MemberRoleParticipationStatus까지 반환해야 하는 이유가 없는 것 같아서요. 👀

방을 업데이트하는 로직인데 참여 여부를 확인하고 있는 것도 살짝 어색하게 느껴지고요.
(이미 방장임이 확인됨)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

방을 업데이트하는 로직인데 참여 여부를 확인하고 있는 것도 살짝 어색하게 느껴지고요.
(이미 방장임이 확인됨)

�우선, 저는 방장이 무조건 리뷰어로 참여만 하는거 자체를 경계하고 있습니다.
방장이 MANAGER 인건 명확하나, REVEIWER 인건 명확한지 궁금합니다.
( 지금 코드적 으로 명확히 분리가 안되서 더 그런거 같기도 해용 )

RoomUpdateResponse를 분리하는 건 어떠신 가요?

위의 관점도 그렇고, 명확하게 RoomResponse 도 프론트와 얘기해서 좀 더 분리가 잘 되는게 아니라면
당장은 불필요한 느낌입니당.

Room room = getRoom(request.roomId());
if (room.isNotMatchingManager(memberId)) {
throw new CoreaException(ExceptionType.MEMBER_IS_NOT_MANAGER);
}
Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new CoreaException(ExceptionType.MEMBER_NOT_FOUND));

Room updateRoom = roomRepository.save(request.toEntity(member));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P5

사소하긴 한데 updatedRoom은 어떠신가요?
updateRoom이라고 하니까 메서드명처럼 느껴지긴 합니다. 0ㅁ0

Participation participation = participationRepository.findByRoomIdAndMemberId(updateRoom.getId(), memberId)
.orElseThrow(() -> new CoreaException(ExceptionType.NOT_ALREADY_APPLY));

roomAutomaticService.updateTime(updateRoom);
return RoomResponse.of(updateRoom, participation.getMemberRole(), ParticipationStatus.MANAGER);
}

private void validateDeadLine(LocalDateTime recruitmentDeadline, LocalDateTime reviewDeadline) {
LocalDateTime currentDateTime = LocalDateTime.now();

Expand Down
16 changes: 16 additions & 0 deletions backend/src/test/java/corea/fixture/RoomFixture.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,22 @@ public static Room ROOM_DOMAIN(Long id, Member member) {
RoomStatus.OPEN
);
}
public static RoomUpdateRequest ROOM_UPDATE_REQUEST(long roomId){
return new RoomUpdateRequest(
roomId,
"Test Room",
"Test Content",
"https://github.com/youngsu5582/github-api-test",
"https://gongu.copyright.or.kr/gongu/wrt/cmmn/wrtFileImageView.do?wrtSn=13301655&filePath=L2Rpc2sxL25ld2RhdGEvMjAyMS8yMS9DTFMxMDAwNC8xMzMwMTY1NV9XUlRfMjFfQ0xTMTAwMDRfMjAyMTEyMTNfMQ==&thumbAt=Y&thumbSe=b_tbumb&wrtTy=10004",
2,
List.of("TDD, 클린코드, 자바"),
10,
LocalDateTime.now(),
LocalDateTime.now()
.plusDays(14),
RoomClassification.BACKEND
);
}

public static RoomCreateRequest ROOM_CREATE_REQUEST() {
return ROOM_CREATE_REQUEST(LocalDateTime.now()
Expand Down
61 changes: 45 additions & 16 deletions backend/src/test/java/corea/room/service/RoomServiceTest.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package corea.room.service;

import config.ServiceTest;
import corea.auth.domain.AuthInfo;
import corea.exception.CoreaException;
import corea.exception.ExceptionType;
import corea.fixture.MatchResultFixture;
Expand All @@ -17,7 +16,6 @@
import corea.participation.domain.ParticipationStatus;
import corea.participation.repository.ParticipationRepository;
import corea.room.domain.Room;
import corea.room.domain.RoomStatus;
import corea.room.dto.RoomCreateRequest;
import corea.room.dto.RoomParticipantResponses;
import corea.room.dto.RoomResponse;
Expand All @@ -28,9 +26,6 @@
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.EnumSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

Expand Down Expand Up @@ -74,13 +69,32 @@ void create() {
assertThat(roomRepository.findAll()).hasSize(1);
}

@Test
@DisplayName("방의 매니저가 아니면 수정 시, 예외를 발생합니다.")
void throw_exception_when_update_with_not_manager() {
Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON());
RoomResponse response = roomService.create(manager.getId(), RoomFixture.ROOM_CREATE_REQUEST());
assertThatThrownBy(() -> roomService.update(-1, RoomFixture.ROOM_UPDATE_REQUEST(response.id())))
.isInstanceOf(CoreaException.class);
}

@Test
@DisplayName("존재하지 않는 방이면, 예외를 발생합니다.")
void throw_exception_when_update_with_not_exist_room() {
Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON());
roomService.create(manager.getId(), RoomFixture.ROOM_CREATE_REQUEST());
assertThatThrownBy(() -> roomService.update(manager.getId(), RoomFixture.ROOM_UPDATE_REQUEST(-1)))
.isInstanceOf(CoreaException.class);
}

@Disabled
@Test
@DisplayName("방을 생성할 때 모집 마감 시간은 현재 시간보다 1시간 이후가 아니라면 예외가 발생한다.")
void invalidRecruitmentDeadline() {
Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON());

RoomCreateRequest request = RoomFixture.ROOM_CREATE_REQUEST_WITH_RECRUITMENT_DEADLINE(LocalDateTime.now().plusMinutes(59));
RoomCreateRequest request = RoomFixture.ROOM_CREATE_REQUEST_WITH_RECRUITMENT_DEADLINE(LocalDateTime.now()
.plusMinutes(59));

assertThatThrownBy(() -> roomService.create(manager.getId(), request))
.asInstanceOf(InstanceOfAssertFactories.type(CoreaException.class))
Expand All @@ -94,7 +108,9 @@ void invalidRecruitmentDeadline() {
void invalidReviewDeadline() {
Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON());

RoomCreateRequest request = RoomFixture.ROOM_CREATE_REQUEST(LocalDateTime.now().plusHours(2), LocalDateTime.now().plusDays(1));
RoomCreateRequest request = RoomFixture.ROOM_CREATE_REQUEST(LocalDateTime.now()
.plusHours(2), LocalDateTime.now()
.plusDays(1));

assertThatThrownBy(() -> roomService.create(manager.getId(), request))
.asInstanceOf(InstanceOfAssertFactories.type(CoreaException.class))
Expand Down Expand Up @@ -161,8 +177,10 @@ void findParticipatedRooms() {
Member pororo = memberRepository.save(MemberFixture.MEMBER_PORORO());
Member ash = memberRepository.save(MemberFixture.MEMBER_ASH());

Room pororoRoom = roomRepository.save(RoomFixture.ROOM_DOMAIN(pororo, LocalDateTime.now().plusDays(2)));
Room ashRoom = roomRepository.save(RoomFixture.ROOM_DOMAIN(ash, LocalDateTime.now().plusDays(3)));
Room pororoRoom = roomRepository.save(RoomFixture.ROOM_DOMAIN(pororo, LocalDateTime.now()
.plusDays(2)));
Room ashRoom = roomRepository.save(RoomFixture.ROOM_DOMAIN(ash, LocalDateTime.now()
.plusDays(3)));

Member joyson = memberRepository.save(MemberFixture.MEMBER_YOUNGSU());
Long joysonId = joyson.getId();
Expand All @@ -182,8 +200,10 @@ void findNonClosedParticipatedRooms() {
Member ash = memberRepository.save(MemberFixture.MEMBER_ASH());
Member movin = memberRepository.save(MemberFixture.MEMBER_MOVIN());

Room pororoRoom = roomRepository.save(RoomFixture.ROOM_DOMAIN(pororo, LocalDateTime.now().plusDays(2)));
Room ashRoom = roomRepository.save(RoomFixture.ROOM_DOMAIN(ash, LocalDateTime.now().plusDays(3)));
Room pororoRoom = roomRepository.save(RoomFixture.ROOM_DOMAIN(pororo, LocalDateTime.now()
.plusDays(2)));
Room ashRoom = roomRepository.save(RoomFixture.ROOM_DOMAIN(ash, LocalDateTime.now()
.plusDays(3)));
Room movinRoom = roomRepository.save(RoomFixture.ROOM_DOMAIN_WITH_CLOSED(movin));

Member joyson = memberRepository.save(MemberFixture.MEMBER_YOUNGSU());
Expand Down Expand Up @@ -259,7 +279,8 @@ void create_participationStatus_manager() {
assertAll(
() -> assertThat(response.manager()).isEqualTo(manager.getName()),
() -> assertThat(participation.isPresent()).isTrue(),
() -> assertThat(participation.get().getStatus()).isEqualTo(ParticipationStatus.MANAGER)
() -> assertThat(participation.get()
.getStatus()).isEqualTo(ParticipationStatus.MANAGER)
);
}

Expand Down Expand Up @@ -301,9 +322,13 @@ void findParticipants() {
List<Member> members = memberRepository.saveAll(MemberFixture.SEVEN_MEMBERS());

participationRepository.save(new Participation(room, manager));
participationRepository.saveAll(members.stream().map(member -> new Participation(room, member, MemberRole.BOTH, 2)).toList());
participationRepository.saveAll(members.stream()
.map(member -> new Participation(room, member, MemberRole.BOTH, 2))
.toList());

matchResultRepository.saveAll(members.stream().map(member -> MatchResultFixture.MATCH_RESULT_DOMAIN(room.getId(), manager, member)).toList());
matchResultRepository.saveAll(members.stream()
.map(member -> MatchResultFixture.MATCH_RESULT_DOMAIN(room.getId(), manager, member))
.toList());
matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN(room.getId(), members.get(0), manager));

RoomParticipantResponses participants = roomService.findParticipants(room.getId(), manager.getId());
Expand All @@ -326,9 +351,13 @@ void findParticipants_withNoPullRequestParticipants() {

List<Member> members = memberRepository.saveAll(MemberFixture.SEVEN_MEMBERS());

participationRepository.saveAll(members.stream().map(member -> new Participation(room, member, MemberRole.BOTH, 2)).toList());
participationRepository.saveAll(members.stream()
.map(member -> new Participation(room, member, MemberRole.BOTH, 2))
.toList());

matchResultRepository.saveAll(members.stream().map(member -> MatchResultFixture.MATCH_RESULT_DOMAIN(room.getId(), manager, member)).toList());
matchResultRepository.saveAll(members.stream()
.map(member -> MatchResultFixture.MATCH_RESULT_DOMAIN(room.getId(), manager, member))
.toList());
matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN(room.getId(), members.get(0), manager));


Expand Down
Loading