diff --git a/src/main/java/com/telepigeon/server/annotation/UserId.java b/src/main/java/com/telepigeon/server/annotation/UserId.java new file mode 100644 index 0000000..036a239 --- /dev/null +++ b/src/main/java/com/telepigeon/server/annotation/UserId.java @@ -0,0 +1,11 @@ +package com.telepigeon.server.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface UserId { +} diff --git a/src/main/java/com/telepigeon/server/controller/RoomController.java b/src/main/java/com/telepigeon/server/controller/RoomController.java new file mode 100644 index 0000000..42a8128 --- /dev/null +++ b/src/main/java/com/telepigeon/server/controller/RoomController.java @@ -0,0 +1,32 @@ +package com.telepigeon.server.controller; + +import com.telepigeon.server.annotation.UserId; +import com.telepigeon.server.domain.Room; +import com.telepigeon.server.dto.room.request.RoomCreateDto; +import com.telepigeon.server.service.room.RoomService; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.net.URI; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1") +public class RoomController { + private final RoomService roomService; + + @PostMapping("/rooms") + public ResponseEntity createRoom( + @UserId Long userId, + @Valid @RequestBody RoomCreateDto roomCreateDto + ) { + Room createdRoom = roomService.createRoom(roomCreateDto); + URI location = URI.create("/rooms/" + createdRoom.getId()); + return ResponseEntity.created(location).build(); + } +} diff --git a/src/main/java/com/telepigeon/server/domain/Profile.java b/src/main/java/com/telepigeon/server/domain/Profile.java index ea2e8f3..80efef6 100644 --- a/src/main/java/com/telepigeon/server/domain/Profile.java +++ b/src/main/java/com/telepigeon/server/domain/Profile.java @@ -5,6 +5,7 @@ import com.telepigeon.server.dto.type.Gender; import jakarta.persistence.*; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -46,4 +47,19 @@ public class Profile { @JoinColumn(name="room_id") private Room room; + @Builder + private Profile(Users user, Room room) { + this.user = user; + this.room = room; + } + + public static Profile create( + Users user, + Room room + ) { + return Profile.builder() + .user(user) + .room(room) + .build(); + } } diff --git a/src/main/java/com/telepigeon/server/domain/Room.java b/src/main/java/com/telepigeon/server/domain/Room.java index ad1e936..1cdb05c 100644 --- a/src/main/java/com/telepigeon/server/domain/Room.java +++ b/src/main/java/com/telepigeon/server/domain/Room.java @@ -1,11 +1,14 @@ package com.telepigeon.server.domain; +import com.telepigeon.server.dto.room.request.RoomCreateDto; import jakarta.persistence.*; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import java.time.LocalDateTime; +import java.util.List; @Entity @Getter @@ -21,4 +24,25 @@ public class Room { private String code; private LocalDateTime createdAt; + + @OneToMany(mappedBy="room", cascade=CascadeType.ALL, fetch=FetchType.LAZY) + private List profiles; + + @Builder + private Room(String name, String code, LocalDateTime createdAt) { + this.name = name; + this.code = code; + this.createdAt = createdAt; + } + + public static Room create( + RoomCreateDto createDto, + String code + ) { + return Room.builder() + .name(createDto.name()) + .code(code) + .createdAt(LocalDateTime.now()) + .build(); + } } diff --git a/src/main/java/com/telepigeon/server/dto/room/request/RoomCreateDto.java b/src/main/java/com/telepigeon/server/dto/room/request/RoomCreateDto.java new file mode 100644 index 0000000..a0d4fcd --- /dev/null +++ b/src/main/java/com/telepigeon/server/dto/room/request/RoomCreateDto.java @@ -0,0 +1,16 @@ +package com.telepigeon.server.dto.room.request; + +import com.telepigeon.server.domain.Room; +import jakarta.annotation.Nullable; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +public record RoomCreateDto( + @NotNull @Size(max=8, min=2) + String name +) { + public static RoomCreateDto of(Room room) { + return new RoomCreateDto(String.valueOf(room.getName())); + } +} diff --git a/src/main/java/com/telepigeon/server/exception/NotFoundException.java b/src/main/java/com/telepigeon/server/exception/NotFoundException.java new file mode 100644 index 0000000..c310acf --- /dev/null +++ b/src/main/java/com/telepigeon/server/exception/NotFoundException.java @@ -0,0 +1,11 @@ +package com.telepigeon.server.exception; + +import com.telepigeon.server.exception.code.NotFoundErrorCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class NotFoundException extends RuntimeException{ + private final NotFoundErrorCode errorCode; +} diff --git a/src/main/java/com/telepigeon/server/exception/code/NotFoundErrorCode.java b/src/main/java/com/telepigeon/server/exception/code/NotFoundErrorCode.java index f264c83..ab0cd1d 100644 --- a/src/main/java/com/telepigeon/server/exception/code/NotFoundErrorCode.java +++ b/src/main/java/com/telepigeon/server/exception/code/NotFoundErrorCode.java @@ -9,10 +9,12 @@ @AllArgsConstructor public enum NotFoundErrorCode implements DefaultErrorCode{ NOT_FOUND_END_POINT(HttpStatus.NOT_FOUND, "error", "존재하지 않는 API입니다."), + ROOM_NOT_FOUND(HttpStatus.NOT_FOUND, "error", "존재하지 않는 방입니다."), + USER_NOT_FOUND(HttpStatus.NOT_FOUND, "error", "존재하지 않는 사용자입니다."), ; @JsonIgnore private final HttpStatus httpStatus; private final String code; private final String message; -} +} \ No newline at end of file diff --git a/src/main/java/com/telepigeon/server/repository/ProfileRepository.java b/src/main/java/com/telepigeon/server/repository/ProfileRepository.java new file mode 100644 index 0000000..c9a41c8 --- /dev/null +++ b/src/main/java/com/telepigeon/server/repository/ProfileRepository.java @@ -0,0 +1,7 @@ +package com.telepigeon.server.repository; + +import com.telepigeon.server.domain.Profile; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ProfileRepository extends JpaRepository { +} diff --git a/src/main/java/com/telepigeon/server/repository/RoomRepository.java b/src/main/java/com/telepigeon/server/repository/RoomRepository.java new file mode 100644 index 0000000..ded9af6 --- /dev/null +++ b/src/main/java/com/telepigeon/server/repository/RoomRepository.java @@ -0,0 +1,9 @@ +package com.telepigeon.server.repository; + +import com.telepigeon.server.domain.Room; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface RoomRepository extends JpaRepository { + boolean existsByName(String name); + boolean existsByCode(String code); +} diff --git a/src/main/java/com/telepigeon/server/repository/UserRepository.java b/src/main/java/com/telepigeon/server/repository/UserRepository.java new file mode 100644 index 0000000..3c7fb42 --- /dev/null +++ b/src/main/java/com/telepigeon/server/repository/UserRepository.java @@ -0,0 +1,12 @@ +package com.telepigeon.server.repository; + +import com.telepigeon.server.domain.Users; +import com.telepigeon.server.exception.NotFoundException; +import com.telepigeon.server.exception.code.NotFoundErrorCode; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserRepository extends JpaRepository { + default Users findByIdOrThrow(Long id) { + return findById(id).orElseThrow(() -> new NotFoundException(NotFoundErrorCode.USER_NOT_FOUND)); + } +} diff --git a/src/main/java/com/telepigeon/server/service/profile/ProfileSaver.java b/src/main/java/com/telepigeon/server/service/profile/ProfileSaver.java new file mode 100644 index 0000000..9e1b826 --- /dev/null +++ b/src/main/java/com/telepigeon/server/service/profile/ProfileSaver.java @@ -0,0 +1,17 @@ +package com.telepigeon.server.service.profile; + +import com.telepigeon.server.domain.Profile; +import com.telepigeon.server.repository.ProfileRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class ProfileSaver { + + private final ProfileRepository profileRepository; + + public Profile save(Profile profile) { + return profileRepository.save(profile); + } +} diff --git a/src/main/java/com/telepigeon/server/service/room/RoomRemover.java b/src/main/java/com/telepigeon/server/service/room/RoomRemover.java new file mode 100644 index 0000000..9732b5d --- /dev/null +++ b/src/main/java/com/telepigeon/server/service/room/RoomRemover.java @@ -0,0 +1,17 @@ +package com.telepigeon.server.service.room; + +import com.telepigeon.server.domain.Room; +import com.telepigeon.server.repository.RoomRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class RoomRemover { + + private final RoomRepository roomRepository; + + public void remove(final Room room) { + roomRepository.delete(room); + } +} diff --git a/src/main/java/com/telepigeon/server/service/room/RoomRetriever.java b/src/main/java/com/telepigeon/server/service/room/RoomRetriever.java new file mode 100644 index 0000000..56b1a84 --- /dev/null +++ b/src/main/java/com/telepigeon/server/service/room/RoomRetriever.java @@ -0,0 +1,24 @@ +package com.telepigeon.server.service.room; + +import com.telepigeon.server.domain.Room; +import com.telepigeon.server.exception.NotFoundException; +import com.telepigeon.server.exception.code.NotFoundErrorCode; +import com.telepigeon.server.repository.RoomRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class RoomRetriever { + + private final RoomRepository roomRepository; + + public boolean existsByName(String name) { + return roomRepository.existsByName(name); + } + + public Room findById(final long id) { + return roomRepository.findById(id) + .orElseThrow(() -> new NotFoundException(NotFoundErrorCode.ROOM_NOT_FOUND)); + } +} diff --git a/src/main/java/com/telepigeon/server/service/room/RoomSaver.java b/src/main/java/com/telepigeon/server/service/room/RoomSaver.java new file mode 100644 index 0000000..7492c3b --- /dev/null +++ b/src/main/java/com/telepigeon/server/service/room/RoomSaver.java @@ -0,0 +1,17 @@ +package com.telepigeon.server.service.room; + +import com.telepigeon.server.domain.Room; +import com.telepigeon.server.repository.RoomRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class RoomSaver { + + private final RoomRepository roomRepository; + + public Room save(final Room room) { + return roomRepository.save(room); + } +} diff --git a/src/main/java/com/telepigeon/server/service/room/RoomService.java b/src/main/java/com/telepigeon/server/service/room/RoomService.java new file mode 100644 index 0000000..cca6fec --- /dev/null +++ b/src/main/java/com/telepigeon/server/service/room/RoomService.java @@ -0,0 +1,59 @@ +package com.telepigeon.server.service.room; + +import com.telepigeon.server.domain.Profile; +import com.telepigeon.server.domain.Room; +import com.telepigeon.server.domain.Users; +import com.telepigeon.server.dto.room.request.RoomCreateDto; +import com.telepigeon.server.repository.RoomRepository; +import com.telepigeon.server.repository.UserRepository; +import com.telepigeon.server.service.profile.ProfileSaver; +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.Random; + +@Service +@RequiredArgsConstructor +public class RoomService { + + private final RoomRepository roomRepository; + private final RoomSaver roomSaver; + private final RoomRetriever roomRetriever; + private final RoomRemover roomRemover; + private final UserRepository userRepository; + private final ProfileSaver profileSaver; + + @Transactional + public Room createRoom(final RoomCreateDto roomCreateDto){ +// Users user = userRepository.findByIdOrThrow(userId); + + String code = createCode(); + + Room room = Room.create(roomCreateDto, code); + Room savedRoom = roomSaver.save(room); + +// Profile profile = Profile.create(user, savedRoom); +// Profile savedProfile = profileSaver.save(profile); + + return savedRoom; + } + + private String createCode() { + String characters = "abcdefghijklmnopqrstuvwxyz0123456789"; + Random random = new Random(); + String code; + + do { + StringBuilder codeBuilder = new StringBuilder(); + for (int i = 0; i < 10; i++) { + int index = random.nextInt(characters.length()); + codeBuilder.append(characters.charAt(index)); + } + code = codeBuilder.toString(); + } while (roomRepository.existsByCode(code)); + + + return code; + } +} diff --git a/src/test/java/com/telepigeon/server/roomTest/RoomDomainTest.java b/src/test/java/com/telepigeon/server/roomTest/RoomDomainTest.java new file mode 100644 index 0000000..18d6338 --- /dev/null +++ b/src/test/java/com/telepigeon/server/roomTest/RoomDomainTest.java @@ -0,0 +1,24 @@ +package com.telepigeon.server.roomTest; + +import com.telepigeon.server.domain.Room; +import com.telepigeon.server.dto.room.request.RoomCreateDto; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class RoomDomainTest { + + @Test + @DisplayName("Room 생성") + public void createRoomTest() { + Room room = Room.create(new RoomCreateDto("name"), "code"); + Assertions.assertNotNull(room); + } + + @Test + @DisplayName("Room 생성 확인") + public void checkCreateRoomTest() { + Room room = Room.create(new RoomCreateDto("name"), "code"); + Assertions.assertEquals(room.getName(), "name"); + } +} diff --git a/src/test/java/com/telepigeon/server/roomTest/RoomServiceTest.java b/src/test/java/com/telepigeon/server/roomTest/RoomServiceTest.java new file mode 100644 index 0000000..6d69e31 --- /dev/null +++ b/src/test/java/com/telepigeon/server/roomTest/RoomServiceTest.java @@ -0,0 +1,40 @@ +package com.telepigeon.server.roomTest; + +import com.telepigeon.server.domain.Room; +import com.telepigeon.server.dto.room.request.RoomCreateDto; +import com.telepigeon.server.repository.RoomRepository; +import com.telepigeon.server.service.room.RoomRetriever; +import com.telepigeon.server.service.room.RoomSaver; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; + +public class RoomServiceTest { + + @Mock + private RoomRepository roomRepository = Mockito.mock(RoomRepository.class); + + @Test + @DisplayName("Room DB에 저장 확인") + public void checkRoomInDB() { + RoomSaver roomSaver = new RoomSaver(roomRepository); + Room room = Room.create(new RoomCreateDto("name"), "code"); + Mockito.doAnswer(invocation -> room).when(roomRepository).save(room); + Room room1 = roomSaver.save(room); + Assertions.assertEquals(room.getName(), room1.getName()); + } + + @Test + @DisplayName("Room DB에서 꺼내오기 확인") + public void checkRoomToDB() { + RoomRetriever roomRetriever = new RoomRetriever(roomRepository); + Room room = Room.create(new RoomCreateDto("name"), "code"); + Mockito.doAnswer(invocation -> true).when(roomRepository).existsByName(room.getName()); + RoomCreateDto roomCreateDto = RoomCreateDto.of(room); + boolean isCheck = roomRetriever.existsByName(roomCreateDto.name()); + Assertions.assertTrue(isCheck); + } + +}