diff --git a/src/main/java/ei/algobaroapi/AlgobaroApiApplication.java b/src/main/java/ei/algobaroapi/AlgobaroApiApplication.java index 39a656f..cc01e56 100644 --- a/src/main/java/ei/algobaroapi/AlgobaroApiApplication.java +++ b/src/main/java/ei/algobaroapi/AlgobaroApiApplication.java @@ -2,7 +2,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; +@EnableScheduling @SpringBootApplication public class AlgobaroApiApplication { diff --git a/src/main/java/ei/algobaroapi/domain/room/controller/RoomControllerDocImpl.java b/src/main/java/ei/algobaroapi/domain/room/controller/RoomControllerDocImpl.java index 7b90f65..4603b4c 100644 --- a/src/main/java/ei/algobaroapi/domain/room/controller/RoomControllerDocImpl.java +++ b/src/main/java/ei/algobaroapi/domain/room/controller/RoomControllerDocImpl.java @@ -48,9 +48,13 @@ public RoomDetailResponseDto createRoom( @Override @PatchMapping("/rooms/{roomShortUuid}") - public RoomResponseDto updateRoomById(@PathVariable(name = "roomShortUuid") String roomShortUuid, - @RequestBody @Valid RoomUpdateRequestDto roomUpdateRequestDto) { - return roomService.updateRoomByShortUuid(roomShortUuid, roomUpdateRequestDto); + @PreAuthorize("hasRole('USER')") + public RoomResponseDto updateRoomById( + @PathVariable(name = "roomShortUuid") String roomShortUuid, + @RequestBody @Valid RoomUpdateRequestDto roomUpdateRequestDto, + @AuthenticationPrincipal Member member + ) { + return roomService.updateRoomByShortUuid(roomShortUuid, roomUpdateRequestDto, member); } @Override diff --git a/src/main/java/ei/algobaroapi/domain/room/domain/Room.java b/src/main/java/ei/algobaroapi/domain/room/domain/Room.java index f789296..0a9a124 100644 --- a/src/main/java/ei/algobaroapi/domain/room/domain/Room.java +++ b/src/main/java/ei/algobaroapi/domain/room/domain/Room.java @@ -76,9 +76,10 @@ public class Room extends BaseEntity { private String roomUuid; @OneToMany(mappedBy = "room", fetch = FetchType.LAZY) - private List roomMembers = new ArrayList<>(); + private List roomMembers; @Builder + @SuppressWarnings("java:S107") public Room(RoomStatus roomStatus, String title, List languages, LocalDateTime startAt, RoomAccessType roomAccessType, String problemLink, String problemPlatform, String password, Integer roomLimit, List tags, @@ -95,6 +96,8 @@ public Room(RoomStatus roomStatus, String title, List languages, LocalDa this.tags = tags; this.timeLimit = timeLimit; this.roomUuid = UUID.randomUUID().toString(); + + this.roomMembers = new ArrayList<>(); } public void update(RoomUpdateRequestDto roomUpdateRequestDto) { @@ -102,11 +105,6 @@ public void update(RoomUpdateRequestDto roomUpdateRequestDto) { this.title = roomUpdateRequestDto.getTitle(); } - if (roomUpdateRequestDto.getStartAt() != null && roomUpdateRequestDto.getStartAt() - .isAfter(LocalDateTime.now())) { - this.startAt = roomUpdateRequestDto.getStartAt(); - } - if (roomUpdateRequestDto.getLanguages() != null && !roomUpdateRequestDto.getLanguages() .isEmpty()) { this.languages = roomUpdateRequestDto.getLanguages(); @@ -176,5 +174,6 @@ public boolean isHeadCountFull(int roomSize) { public void updateRoomStatusRunning() { this.roomStatus = RoomStatus.RUNNING; + this.startAt = LocalDateTime.now(); } } diff --git a/src/main/java/ei/algobaroapi/domain/room/domain/RoomRepository.java b/src/main/java/ei/algobaroapi/domain/room/domain/RoomRepository.java index 0848a8d..5de75b8 100644 --- a/src/main/java/ei/algobaroapi/domain/room/domain/RoomRepository.java +++ b/src/main/java/ei/algobaroapi/domain/room/domain/RoomRepository.java @@ -1,5 +1,7 @@ package ei.algobaroapi.domain.room.domain; +import java.time.LocalDateTime; +import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; @@ -10,4 +12,10 @@ public interface RoomRepository extends JpaRepository, RoomRepositor @Query("SELECT r FROM Room r WHERE r.roomUuid LIKE CONCAT(:roomShortUuid, '%')") Optional findByRoomUuidStartingWith(String roomShortUuid); + + @Query(value = + "SELECT * FROM room WHERE DATE_ADD(start_at, INTERVAL time_limit + :timeOffsetMinutes MINUTE) < :now", + nativeQuery = true + ) + List findListEndAtBefore(LocalDateTime now, int timeOffsetMinutes); } diff --git a/src/main/java/ei/algobaroapi/domain/room/dto/request/RoomUpdateRequestDto.java b/src/main/java/ei/algobaroapi/domain/room/dto/request/RoomUpdateRequestDto.java index 4ced870..bac5286 100644 --- a/src/main/java/ei/algobaroapi/domain/room/dto/request/RoomUpdateRequestDto.java +++ b/src/main/java/ei/algobaroapi/domain/room/dto/request/RoomUpdateRequestDto.java @@ -2,7 +2,6 @@ import ei.algobaroapi.domain.room.domain.RoomAccessType; import io.swagger.v3.oas.annotations.media.Schema; -import java.time.LocalDateTime; import java.util.List; import lombok.Builder; import lombok.Getter; @@ -15,9 +14,6 @@ public class RoomUpdateRequestDto { @Schema(description = "방 제목", example = "같이 푸실분~") private String title; - @Schema(description = "방 시작 시간", example = "2024-2-18T17:30:00") - private LocalDateTime startAt; - @Schema(description = "사용 가능 언어", example = "[\"JAVA\", \"C++\"]") private List languages; diff --git a/src/main/java/ei/algobaroapi/domain/room/dto/response/RoomDetailResponseDto.java b/src/main/java/ei/algobaroapi/domain/room/dto/response/RoomDetailResponseDto.java index f057f1e..30854bf 100644 --- a/src/main/java/ei/algobaroapi/domain/room/dto/response/RoomDetailResponseDto.java +++ b/src/main/java/ei/algobaroapi/domain/room/dto/response/RoomDetailResponseDto.java @@ -47,6 +47,9 @@ public class RoomDetailResponseDto { @Schema(description = "타이머(Minute)", example = "20") private Integer timeLimit; + @Schema(description = "방 종료 예정 시간", example = "2024-03-04T00:45:18") + private String endTime; + @Schema(description = "방 short UUID", example = "2ad2e9db") private String roomShortUuid; @@ -88,6 +91,9 @@ public static RoomDetailResponseDto of(Room room, List ro room.getTags(), room.getTimeLimit(), room.getRoomShortUuid(), + room.getStartAt() == null + ? null + : room.getStartAt().plusMinutes(room.getTimeLimit()).toString(), roomMembers ); } diff --git a/src/main/java/ei/algobaroapi/domain/room/service/RoomSchedulerService.java b/src/main/java/ei/algobaroapi/domain/room/service/RoomSchedulerService.java new file mode 100644 index 0000000..d0f9f94 --- /dev/null +++ b/src/main/java/ei/algobaroapi/domain/room/service/RoomSchedulerService.java @@ -0,0 +1,6 @@ +package ei.algobaroapi.domain.room.service; + +public interface RoomSchedulerService { + + void deleteRoomScheduler(); +} diff --git a/src/main/java/ei/algobaroapi/domain/room/service/RoomSchedulerServiceImpl.java b/src/main/java/ei/algobaroapi/domain/room/service/RoomSchedulerServiceImpl.java new file mode 100644 index 0000000..3f08409 --- /dev/null +++ b/src/main/java/ei/algobaroapi/domain/room/service/RoomSchedulerServiceImpl.java @@ -0,0 +1,42 @@ +package ei.algobaroapi.domain.room.service; + +import ei.algobaroapi.domain.room.domain.Room; +import ei.algobaroapi.domain.room.domain.RoomRepository; +import ei.algobaroapi.domain.room_member.domain.RoomMemberRepository; +import java.time.LocalDateTime; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Component +@RequiredArgsConstructor +public class RoomSchedulerServiceImpl implements RoomSchedulerService { + + private static final int DELETE_ROOM_TIME_OFFSET_MINUTES = 30; + private final RoomRepository roomRepository; + private final RoomMemberRepository roomMemberRepository; + + @Override + @Scheduled(cron = "0/10 * * * * *") + @Transactional + public void deleteRoomScheduler() { + List toDeleteRooms = findExpiredRooms(); + deleteRoomsAndMembers(toDeleteRooms); + } + + private List findExpiredRooms() { + return roomRepository.findListEndAtBefore( + LocalDateTime.now(), + DELETE_ROOM_TIME_OFFSET_MINUTES + ); + } + + private void deleteRoomsAndMembers(List toDeleteRooms) { + for (Room toDeleteRoom : toDeleteRooms) { + roomMemberRepository.deleteAll(toDeleteRoom.getRoomMembers()); + } + roomRepository.deleteAll(toDeleteRooms); + } +} diff --git a/src/main/java/ei/algobaroapi/domain/room/service/RoomService.java b/src/main/java/ei/algobaroapi/domain/room/service/RoomService.java index ebe6cdb..9b387d8 100644 --- a/src/main/java/ei/algobaroapi/domain/room/service/RoomService.java +++ b/src/main/java/ei/algobaroapi/domain/room/service/RoomService.java @@ -15,8 +15,11 @@ public interface RoomService { RoomDetailResponseDto createRoom(RoomCreateRequestDto roomCreateRequestDto, Member member); - RoomResponseDto updateRoomByShortUuid(String roomShortUuid, - RoomUpdateRequestDto roomUpdateRequestDto); + RoomResponseDto updateRoomByShortUuid( + String roomShortUuid, + RoomUpdateRequestDto roomUpdateRequestDto, + Member member + ); RoomDetailResponseDto getRoomDetailShortUuid(String roomShortUuid); diff --git a/src/main/java/ei/algobaroapi/domain/room/service/RoomServiceImpl.java b/src/main/java/ei/algobaroapi/domain/room/service/RoomServiceImpl.java index 729aa09..f32f6da 100644 --- a/src/main/java/ei/algobaroapi/domain/room/service/RoomServiceImpl.java +++ b/src/main/java/ei/algobaroapi/domain/room/service/RoomServiceImpl.java @@ -59,8 +59,12 @@ public RoomDetailResponseDto createRoom(RoomCreateRequestDto roomCreateRequestDt @Override @Transactional - public RoomResponseDto updateRoomByShortUuid(String roomShortUuid, - RoomUpdateRequestDto roomUpdateRequestDto) { + public RoomResponseDto updateRoomByShortUuid( + String roomShortUuid, + RoomUpdateRequestDto roomUpdateRequestDto, + Member member + ) { + roomMemberService.validateHost(roomShortUuid, member.getId()); Room room = getRoomByShortUuid(roomShortUuid); room.update(roomUpdateRequestDto); diff --git a/src/main/java/ei/algobaroapi/domain/solve/domain/SolveHistory.java b/src/main/java/ei/algobaroapi/domain/solve/domain/SolveHistory.java index 8a0714e..f859a0b 100644 --- a/src/main/java/ei/algobaroapi/domain/solve/domain/SolveHistory.java +++ b/src/main/java/ei/algobaroapi/domain/solve/domain/SolveHistory.java @@ -52,6 +52,9 @@ public class SolveHistory extends BaseEntity { @Column(name = "solve_status", nullable = false) private SolveStatus solveStatus; + @Column(name = "failure_reason") + private String failureReason; + @Column(name = "problem_link", nullable = false) private String problemLink; @@ -68,14 +71,16 @@ public SolveHistory(Member member, String roomUuid, String problemLink) { this.startAt = LocalDateTime.now(); this.endAt = null; this.solveStatus = SolveStatus.FAIL; + this.failureReason = "UNSUBMITTED"; this.problemLink = problemLink; this.problemPlatform = ProblemPlatform.BOJ; } - public void updateCodeAndLanguageAndSolveStatus(String code, String language, String solveStatus) { + public void updateCodeAndLanguageAndSolveStatus(String code, String language, SolveStatus solveStatus, String failureReason) { this.inputCode = code; this.codeLanguage = language; - this.solveStatus = SolveStatus.valueOf(solveStatus); + this.solveStatus = solveStatus; + this.failureReason = failureReason; this.endAt = LocalDateTime.now(); } } diff --git a/src/main/java/ei/algobaroapi/domain/solve/dto/request/BojCodeSubmissionRequest.java b/src/main/java/ei/algobaroapi/domain/solve/dto/request/BojCodeSubmissionRequest.java index 010570c..8b95a56 100644 --- a/src/main/java/ei/algobaroapi/domain/solve/dto/request/BojCodeSubmissionRequest.java +++ b/src/main/java/ei/algobaroapi/domain/solve/dto/request/BojCodeSubmissionRequest.java @@ -1,5 +1,6 @@ package ei.algobaroapi.domain.solve.dto.request; +import ei.algobaroapi.domain.solve.domain.SolveStatus; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; import lombok.Getter; @@ -19,7 +20,10 @@ public class BojCodeSubmissionRequest { private String code; @Schema(description = "코드 실행 결과", example = "SUCCESS / FAIL") - private String solveStatus; + private SolveStatus solveStatus; + + @Schema(description = "실패 이유", example = "MEMORY_LIMIT, TIME_LIMIT, ETC") + private String failureReason; @Schema(description = "문제 링크", example = "https://www.acmicpc.net/problem/1000") private String problemLink; diff --git a/src/main/java/ei/algobaroapi/domain/solve/dto/request/CodeSubmissionRequest.java b/src/main/java/ei/algobaroapi/domain/solve/dto/request/CodeSubmissionRequest.java index c7ca6e8..b40b0c4 100644 --- a/src/main/java/ei/algobaroapi/domain/solve/dto/request/CodeSubmissionRequest.java +++ b/src/main/java/ei/algobaroapi/domain/solve/dto/request/CodeSubmissionRequest.java @@ -1,5 +1,6 @@ package ei.algobaroapi.domain.solve.dto.request; +import ei.algobaroapi.domain.solve.domain.SolveStatus; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; import lombok.Getter; @@ -19,5 +20,8 @@ public class CodeSubmissionRequest { private String code; @Schema(description = "코드 실행 결과", example = "SUCCESS / FAIL") - private String solveStatus; + private SolveStatus solveStatus; + + @Schema(description = "실패 이유", example = "MEMORY_LIMIT, TIME_LIMIT, ETC") + private String failureReason; } diff --git a/src/main/java/ei/algobaroapi/domain/solve/dto/response/SolveHistoryDetailResponse.java b/src/main/java/ei/algobaroapi/domain/solve/dto/response/SolveHistoryDetailResponse.java index 6cd7b5b..74b821e 100644 --- a/src/main/java/ei/algobaroapi/domain/solve/dto/response/SolveHistoryDetailResponse.java +++ b/src/main/java/ei/algobaroapi/domain/solve/dto/response/SolveHistoryDetailResponse.java @@ -26,6 +26,9 @@ public class SolveHistoryDetailResponse { @Schema(description = "풀이 결과", example = "SUCCESS") private final String solveStatus; + @Schema(description = "실패 이유", example = "MEMORY_LIMIT / TIME_LIMIT / ETC / NULL") + private final String failureReason; + @Schema(description = "풀이 시간", example = "2024-01-01T00:00:00") private final String solvedAt; @@ -42,6 +45,7 @@ public static SolveHistoryDetailResponse of(SolveHistory solveHistory) { solveHistory.getCodeLanguage(), solveHistory.getInputCode(), solveHistory.getSolveStatus().name(), + solveHistory.getFailureReason(), solveHistory.getStartAt().toString(), solveHistory.getProblemPlatform().name(), solveHistory.getProblemLink() diff --git a/src/main/java/ei/algobaroapi/domain/solve/dto/response/SolveHistoryResponse.java b/src/main/java/ei/algobaroapi/domain/solve/dto/response/SolveHistoryResponse.java index 420685b..d283815 100644 --- a/src/main/java/ei/algobaroapi/domain/solve/dto/response/SolveHistoryResponse.java +++ b/src/main/java/ei/algobaroapi/domain/solve/dto/response/SolveHistoryResponse.java @@ -23,6 +23,9 @@ public class SolveHistoryResponse { @Schema(description = "풀이 결과", example = "SUCCESS") private final String solveStatus; + @Schema(description = "실패 이유", example = "MEMORY_LIMIT / TIME_LIMIT / ETC / NULL") + private final String failureReason; + @Schema(description = "풀이 시간", example = "2024-01-01T00:00:00") private final String solvedAt; @@ -35,6 +38,7 @@ public static SolveHistoryResponse of(SolveHistory solveHistory) { solveHistory.getRoomUuid(), solveHistory.getCodeLanguage(), solveHistory.getSolveStatus().name(), + solveHistory.getFailureReason(), solveHistory.getStartAt().toString(), solveHistory.getProblemLink() ); diff --git a/src/main/java/ei/algobaroapi/domain/solve/dto/response/SolveResult.java b/src/main/java/ei/algobaroapi/domain/solve/dto/response/SolveResult.java index 5a5ce71..4c7f070 100644 --- a/src/main/java/ei/algobaroapi/domain/solve/dto/response/SolveResult.java +++ b/src/main/java/ei/algobaroapi/domain/solve/dto/response/SolveResult.java @@ -24,4 +24,7 @@ public class SolveResult { @Schema(description = "풀이 결과", example = "SUCCESS") private final SolveStatus solveStatus; + + @Schema(description = "실패 이유", example = "MEMORY_LIMIT / TIME_LIMIT / ETC / NULL") + private final String failureReason; } diff --git a/src/main/java/ei/algobaroapi/domain/solve/service/BojSolveServiceImpl.java b/src/main/java/ei/algobaroapi/domain/solve/service/BojSolveServiceImpl.java index 3c96dbe..df03406 100644 --- a/src/main/java/ei/algobaroapi/domain/solve/service/BojSolveServiceImpl.java +++ b/src/main/java/ei/algobaroapi/domain/solve/service/BojSolveServiceImpl.java @@ -32,7 +32,8 @@ public CodeSubmissionResponse submitCode(Long memberId, CodeSubmissionRequest re request.getRoomShortUuid(), request.getLanguage(), request.getCode(), - request.getSolveStatus() + request.getSolveStatus(), + request.getFailureReason() ); return CodeSubmissionResponse.of(request.getCode()); @@ -58,7 +59,8 @@ public BojCodeSubmissionResponse submitCodeAndCompile(Long memberId, request.getRoomShortUuid(), request.getLanguage(), request.getCode(), - request.getSolveStatus() + request.getSolveStatus(), + request.getFailureReason() ); return BojCodeSubmissionResponse.of(testCaseResults); diff --git a/src/main/java/ei/algobaroapi/domain/solve/service/SolveHistoryService.java b/src/main/java/ei/algobaroapi/domain/solve/service/SolveHistoryService.java index 8e2d04d..1da56f1 100644 --- a/src/main/java/ei/algobaroapi/domain/solve/service/SolveHistoryService.java +++ b/src/main/java/ei/algobaroapi/domain/solve/service/SolveHistoryService.java @@ -1,6 +1,7 @@ package ei.algobaroapi.domain.solve.service; import ei.algobaroapi.domain.solve.domain.SolveHistory; +import ei.algobaroapi.domain.solve.domain.SolveStatus; import ei.algobaroapi.domain.solve.dto.request.SolveHistoryListFindRequest; import ei.algobaroapi.domain.solve.dto.response.SolveHistoryDetailResponse; import ei.algobaroapi.domain.solve.dto.response.SolveHistoryResponse; @@ -18,7 +19,7 @@ PageResponse getHistoryList( SolveHistoryDetailResponse getHistoryDetail(Long memberId, Long solveId); - void updateSolveHistoryCode(Long memberId, String roomShortUuid, String language, String code, String solveStatus); + void updateSolveHistoryCode(Long memberId, String roomShortUuid, String language, String code, SolveStatus solveStatus, String failureReason); SolveResultResponse getSolveResultInRoom(String roomShortUuid); diff --git a/src/main/java/ei/algobaroapi/domain/solve/service/SolveHistoryServiceImpl.java b/src/main/java/ei/algobaroapi/domain/solve/service/SolveHistoryServiceImpl.java index 17062f6..6a01018 100644 --- a/src/main/java/ei/algobaroapi/domain/solve/service/SolveHistoryServiceImpl.java +++ b/src/main/java/ei/algobaroapi/domain/solve/service/SolveHistoryServiceImpl.java @@ -4,6 +4,7 @@ import ei.algobaroapi.domain.member.service.MemberService; import ei.algobaroapi.domain.solve.domain.SolveHistory; import ei.algobaroapi.domain.solve.domain.SolveHistoryRepository; +import ei.algobaroapi.domain.solve.domain.SolveStatus; import ei.algobaroapi.domain.solve.dto.request.SolveHistoryListFindRequest; import ei.algobaroapi.domain.solve.dto.response.SolveHistoryDetailResponse; import ei.algobaroapi.domain.solve.dto.response.SolveHistoryResponse; @@ -59,7 +60,8 @@ public void updateSolveHistoryCode( String roomShortUuid, String language, String code, - String solveStatus + SolveStatus solveStatus, + String failureReason ) { Member findMember = memberService.getMemberById(memberId); SolveHistory findSolveHistory = getSolveHistoryByMemberAndRoomUuid( @@ -67,7 +69,7 @@ public void updateSolveHistoryCode( findMember ); - findSolveHistory.updateCodeAndLanguageAndSolveStatus(code, language, solveStatus); + findSolveHistory.updateCodeAndLanguageAndSolveStatus(code, language, solveStatus, failureReason); } private SolveHistory getSolveHistoryByMemberAndRoomUuid( diff --git a/src/main/java/ei/algobaroapi/global/config/swaggerdoc/RoomControllerDoc.java b/src/main/java/ei/algobaroapi/global/config/swaggerdoc/RoomControllerDoc.java index 4aaf083..cc0a569 100644 --- a/src/main/java/ei/algobaroapi/global/config/swaggerdoc/RoomControllerDoc.java +++ b/src/main/java/ei/algobaroapi/global/config/swaggerdoc/RoomControllerDoc.java @@ -27,7 +27,11 @@ public interface RoomControllerDoc { @Operation(summary = "방 수정", description = "방 정보를 수정합니다.") @ApiResponse(responseCode = "200", description = "방 수정 성공") @ApiResponse(responseCode = "E03301", description = "수정하려는 방 정보를 찾지 못했습니다.") - RoomResponseDto updateRoomById(String roomShortUuid, RoomUpdateRequestDto roomUpdateRequestDto); + RoomResponseDto updateRoomById( + String roomShortUuid, + RoomUpdateRequestDto roomUpdateRequestDto, + Member member + ); @Operation(summary = "개별 방 정보 조회", description = "short UUID를 통해 방을 조회합니다.") @ApiResponse(responseCode = "200", description = "방 정보 조회 성공")