From 563ce2ff509cc26c0f04a680fe913ac6134e7c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=92=E1=85=A2=E1=84=83?= =?UTF-8?q?=E1=85=A9=E1=86=BC?= Date: Wed, 11 Oct 2023 12:51:47 +0900 Subject: [PATCH] =?UTF-8?q?[feature/room-with-user]=20room=20api=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/koliving/api/KolivingApplication.java | 17 +++++------ .../com/koliving/api/auth/AuthFacade.java | 3 +- .../auth/application/dto/TokenRequest.java | 2 +- .../auth/application/dto/TokenResponse.java | 11 ++++--- .../api/auth/login/LoginSuccessHandler.java | 3 ++ .../api/room/application/RoomService.java | 13 ++++++--- .../com/koliving/api/room/domain/Room.java | 28 +++++++++++++----- .../api/room/infra/RoomRepository.java | 10 +++++++ .../koliving/api/room/ui/RoomController.java | 29 +++++++++++++++++-- src/main/java/com/koliving/api/user/User.java | 4 ++- .../koliving/api/fixtures/UserFixture.java | 14 +++++++++ .../api/room/infra/RoomRepositoryTest.java | 20 +++++++++++-- 12 files changed, 122 insertions(+), 32 deletions(-) create mode 100644 src/test/java/com/koliving/api/fixtures/UserFixture.java diff --git a/src/main/java/com/koliving/api/KolivingApplication.java b/src/main/java/com/koliving/api/KolivingApplication.java index 767032c2..0ccc33cf 100644 --- a/src/main/java/com/koliving/api/KolivingApplication.java +++ b/src/main/java/com/koliving/api/KolivingApplication.java @@ -81,18 +81,17 @@ CommandLineRunner commandLineRunner( initFurnishings(furnishingRepository); initLocations(locationRepository); initLanguages(languageRepository); - //FIXME 테스트 후 제거 예정 - initRooms(roomRepository, locationRepository, furnishingRepository, imageFileRepository); - initUser(userRepository, encoder); + User user = initUser(userRepository, encoder); + initRooms(roomRepository, locationRepository, furnishingRepository, imageFileRepository, user); }; } - private void initUser(UserRepository userRepository, PasswordEncoder encoder) { + private User initUser(UserRepository userRepository, PasswordEncoder encoder) { final User user = User.builder() .email("koliving@koliving.com") .build(); user.setPassword(encoder.encode("test1234!@")); - userRepository.save(user); + return userRepository.save(user); } private void initImageFiles(ImageFileRepository imageFileRepository) { @@ -105,7 +104,7 @@ private void initImageFiles(ImageFileRepository imageFileRepository) { } private void initRooms(RoomRepository roomRepository, LocationRepository locationRepository, - FurnishingRepository furnishingRepository, ImageFileRepository imageFileRepository) { + FurnishingRepository furnishingRepository, ImageFileRepository imageFileRepository, User user) { Location location = locationRepository.findByName("Songjeong").get(); Location location2 = locationRepository.findByName("Huam").get(); Location location3 = locationRepository.findByName("Amsaje 1").get(); @@ -137,7 +136,7 @@ private void initRooms(RoomRepository roomRepository, LocationRepository locatio imageFile, imageFile2 ) - ), + ).by(user), Room.valueOf( location2, RoomInfo.valueOf(RoomType.ONE_BED_FLATS, 1, 2, 2), @@ -148,7 +147,7 @@ private void initRooms(RoomRepository roomRepository, LocationRepository locatio LocalDate.of(2023, 8, 30), "용산구 후암동) ONE_BED_FLATS, 방1, 욕실2, 룸메2 보증금 5_000_000 월세X 관리비X 가구X 2023.08.30 입주", Sets.newHashSet() - ), + ).by(user), Room.valueOf( location3, RoomInfo.valueOf(RoomType.ONE_BED_FLATS, 1, 2, 2), @@ -159,7 +158,7 @@ private void initRooms(RoomRepository roomRepository, LocationRepository locatio LocalDate.now(), "강동구 암사제1동) ONE_BED_FLATS, 방1, 욕실2, 룸메2 보증금 5_000_000 월세300_000 관리비X 가구X 2023.08.30 입주", Sets.newHashSet() - ) + ).by(user) ) ); diff --git a/src/main/java/com/koliving/api/auth/AuthFacade.java b/src/main/java/com/koliving/api/auth/AuthFacade.java index 49016f56..a4019f32 100644 --- a/src/main/java/com/koliving/api/auth/AuthFacade.java +++ b/src/main/java/com/koliving/api/auth/AuthFacade.java @@ -123,9 +123,8 @@ public TokenResponse createToken(TokenRequest request) { .orElseThrow(() -> new KolivingServiceException(UNAUTHORIZED)); user.checkPassword(passwordEncoder.encode(request.password())); - //TODO refresh 토큰 존재 확인 final JwtTokenDto jwtTokenDto = issueAuthTokens(user); - return TokenResponse.valueOf(jwtTokenDto.getAccessToken()); + return TokenResponse.valueOf(jwtTokenDto.getAccessToken(), jwtTokenDto.getRefreshToken()); } } diff --git a/src/main/java/com/koliving/api/auth/application/dto/TokenRequest.java b/src/main/java/com/koliving/api/auth/application/dto/TokenRequest.java index 8fd03e29..b7b0d542 100644 --- a/src/main/java/com/koliving/api/auth/application/dto/TokenRequest.java +++ b/src/main/java/com/koliving/api/auth/application/dto/TokenRequest.java @@ -7,7 +7,7 @@ public record TokenRequest( @Schema(description = "계정", example = "koliving@koliving.com") @NotEmpty String email, - @Schema(description = "비밀번호", example = "password1234") + @Schema(description = "비밀번호", example = "test1234!@") @NotEmpty String password ) { diff --git a/src/main/java/com/koliving/api/auth/application/dto/TokenResponse.java b/src/main/java/com/koliving/api/auth/application/dto/TokenResponse.java index 06fcefe2..7cb095d0 100644 --- a/src/main/java/com/koliving/api/auth/application/dto/TokenResponse.java +++ b/src/main/java/com/koliving/api/auth/application/dto/TokenResponse.java @@ -7,12 +7,15 @@ */ @Schema(description = "토큰 발급 정보") public record TokenResponse( - @Schema(description = "액세스 토큰", example = "eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiI0IiwiYXV0aG9yaXRpZXMiOlt7ImF1dGhvcml0eSI6Im1lbWJlcjp3cml0ZSJ9LHsiYXV0aG9yaXR5IjoibWVtYmVyOnJlYWQifSx7ImF1dGhvcml0eSI6InJvb206cmVhZCJ9LHsiYXV0aG9yaXR5Ijoicm9vbTp3cml0ZSJ9LHsiYXV0aG9yaXR5IjoiUk9MRV9BRE1JTiJ9XSwiaWF0IjoxNjU4MjE4MTI2LCJleHAiOjE2ODk2OTI0MDB9.9S32yrsYqbnLQZGP4kPnoK3K5M1o2RmTzF-rbj8ABqFCk95tB-8irPCArdptIUp_") - String accessToken + @Schema(description = "Access 토큰", example = "eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiI0IiwiYXV0aG9yaXRpZXMiOlt7ImF1dGhvcml0eSI6Im1lbWJlcjp3cml0ZSJ9LHsiYXV0aG9yaXR5IjoibWVtYmVyOnJlYWQifSx7ImF1dGhvcml0eSI6InJvb206cmVhZCJ9LHsiYXV0aG9yaXR5Ijoicm9vbTp3cml0ZSJ9LHsiYXV0aG9yaXR5IjoiUk9MRV9BRE1JTiJ9XSwiaWF0IjoxNjU4MjE4MTI2LCJleHAiOjE2ODk2OTI0MDB9.9S32yrsYqbnLQZGP4kPnoK3K5M1o2RmTzF-rbj8ABqFCk95tB-8irPCArdptIUp_") + String accessToken, + + @Schema(description = "Refresh 토큰", example = "eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiI0IiwiYXV0aG9yaXRpZXMiOlt7ImF1dGhvcml0eSI6Im1lbWJlcjp3cml0ZSJ9LHsiYXV0aG9yaXR5IjoibWVtYmVyOnJlYWQifSx7ImF1dGhvcml0eSI6InJvb206cmVhZCJ9LHsiYXV0aG9yaXR5Ijoicm9vbTp3cml0ZSJ9LHsiYXV0aG9yaXR5IjoiUk9MRV9BRE1JTiJ9XSwiaWF0IjoxNjU4MjE4MTI2LCJleHAiOjE2ODk2OTI0MDB9.9S32yrsYqbnLQZGP4kPnoK3K5M1o2RmTzF-rbj8ABqFCk95tB-8irPCArdptIUp_") + String refreshToken ) { - public static TokenResponse valueOf(String accessToken) { - return new TokenResponse(accessToken); + public static TokenResponse valueOf(String accessToken, String refreshToken) { + return new TokenResponse(accessToken, refreshToken); } } diff --git a/src/main/java/com/koliving/api/auth/login/LoginSuccessHandler.java b/src/main/java/com/koliving/api/auth/login/LoginSuccessHandler.java index 8bdf0ea1..d8c06e9e 100644 --- a/src/main/java/com/koliving/api/auth/login/LoginSuccessHandler.java +++ b/src/main/java/com/koliving/api/auth/login/LoginSuccessHandler.java @@ -6,6 +6,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; +import org.apache.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.UserDetails; @@ -33,5 +34,7 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo httpUtils.getCookieOfAccessToken(accessToken), httpUtils.getCookieOfRefreshToken(refreshToken) ); + + response.addHeader(HttpHeaders.AUTHORIZATION, "bearer " + accessToken); } } diff --git a/src/main/java/com/koliving/api/room/application/RoomService.java b/src/main/java/com/koliving/api/room/application/RoomService.java index 97739c9a..53f1bc89 100644 --- a/src/main/java/com/koliving/api/room/application/RoomService.java +++ b/src/main/java/com/koliving/api/room/application/RoomService.java @@ -13,7 +13,6 @@ import com.koliving.api.room.application.dto.RoomSaveRequest; import com.koliving.api.room.application.dto.RoomSearchCondition; import com.koliving.api.room.domain.Furnishing; -import com.koliving.api.room.domain.QRoom; import com.koliving.api.room.domain.Room; import com.koliving.api.room.infra.FurnishingRepository; import com.koliving.api.room.infra.RoomRepository; @@ -21,9 +20,10 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; + +import com.koliving.api.user.User; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -50,12 +50,12 @@ public List list() { } @Transactional - public Long save(RoomSaveRequest request) { + public Long save(RoomSaveRequest request, User user) { Room room = request.toEntity( getLocationById(request.locationId()), getFurnishingsByIds(request.furnishingIds()), getImageFiles(request.imageIds()) - ); + ).by(user); final Room savedRoom = roomRepository.save(room); return savedRoom.getId(); @@ -98,4 +98,9 @@ private Location getLocationById(Long locationId) { public Page search(Pageable pageable, RoomSearchCondition condition) { return roomRepository.search(pageable, condition); } + + public Room findOne(Long id) { + return roomRepository.findByIdWithUser(id) + .orElseThrow(() -> new KolivingServiceException(RECORD_NOT_EXIST)); + } } diff --git a/src/main/java/com/koliving/api/room/domain/Room.java b/src/main/java/com/koliving/api/room/domain/Room.java index e0e36401..0ae9c032 100644 --- a/src/main/java/com/koliving/api/room/domain/Room.java +++ b/src/main/java/com/koliving/api/room/domain/Room.java @@ -1,9 +1,11 @@ package com.koliving.api.room.domain; import com.koliving.api.base.domain.BaseEntity; +import com.koliving.api.base.exception.KolivingServiceException; import com.koliving.api.file.domain.ImageFile; import com.koliving.api.location.domain.Location; import com.koliving.api.room.domain.info.RoomInfo; +import com.koliving.api.user.User; import jakarta.persistence.AttributeOverride; import jakarta.persistence.Column; import jakarta.persistence.Embedded; @@ -25,8 +27,10 @@ import java.time.LocalDate; import java.util.HashSet; +import java.util.Objects; import java.util.Set; +import static com.koliving.api.base.ServiceError.ILLEGAL_ROOM_INFO; import static jakarta.persistence.GenerationType.IDENTITY; import static lombok.AccessLevel.PROTECTED; @@ -50,7 +54,11 @@ public class Room extends BaseEntity { private Long id; @OneToOne - @JoinColumn(name = "location_id") + @JoinColumn(name = "user_id", nullable = false) + private User user; + + @OneToOne + @JoinColumn(name = "location_id", nullable = false) private Location location; @Embedded @@ -81,7 +89,7 @@ public class Room extends BaseEntity { @Lob private String description; - @OneToMany + @ManyToMany @JoinTable( name = "TB_ROOM_IMAGES", joinColumns = @JoinColumn(name = "room_id"), @@ -102,10 +110,6 @@ private Room(Location location, RoomInfo roomInfo, Money deposit, Money monthlyR this.imageFiles = imageFiles; } - private void validate(Money deposit, Money monthlyRent, Set imageFiles) { - //TODO 최대값 validation 추가 - } - public static Room valueOf( Location location, RoomInfo info, @@ -120,6 +124,16 @@ public static Room valueOf( return new Room(location, info, deposit, monthlyRent, maintenance, furnishings, availableDate, description, imageFiles); } - private Long userId; + private void validate(Money deposit, Money monthlyRent, Set imageFiles) { + //TODO 최대값 validation 추가 + } + + public Room by(User user) { + if (Objects.isNull(user)) { + throw new KolivingServiceException(ILLEGAL_ROOM_INFO); + } + this.user = user; + return this; + } } diff --git a/src/main/java/com/koliving/api/room/infra/RoomRepository.java b/src/main/java/com/koliving/api/room/infra/RoomRepository.java index f7af1ad2..23173000 100644 --- a/src/main/java/com/koliving/api/room/infra/RoomRepository.java +++ b/src/main/java/com/koliving/api/room/infra/RoomRepository.java @@ -2,7 +2,17 @@ import com.koliving.api.room.domain.Room; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; +import java.util.Optional; public interface RoomRepository extends JpaRepository, RoomRepositoryQueryDsl { + @Query("select r from TB_ROOM r join fetch r.user u") + List findAllWithUser(); + + @Query("select r from TB_ROOM r join fetch r.user u where r.id=:id") + Optional findByIdWithUser(Long id); + } diff --git a/src/main/java/com/koliving/api/room/ui/RoomController.java b/src/main/java/com/koliving/api/room/ui/RoomController.java index a1150fae..d97cfe97 100644 --- a/src/main/java/com/koliving/api/room/ui/RoomController.java +++ b/src/main/java/com/koliving/api/room/ui/RoomController.java @@ -6,6 +6,7 @@ import com.koliving.api.room.application.dto.RoomSaveRequest; import com.koliving.api.room.application.dto.RoomSearchCondition; import com.koliving.api.room.domain.Room; +import com.koliving.api.user.User; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; @@ -15,12 +16,16 @@ import java.util.List; import lombok.RequiredArgsConstructor; +import org.apache.tomcat.util.net.openssl.ciphers.Authentication; import org.springdoc.core.annotations.ParameterObject; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; 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.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -49,8 +54,8 @@ public class RoomController { ), }) @PostMapping - public ResponseEntity save(@RequestBody RoomSaveRequest request) { - final Long id = roomService.save(request); + public ResponseEntity save(@RequestBody RoomSaveRequest request, @AuthenticationPrincipal User user) { + final Long id = roomService.save(request, user); return ResponseEntity.created(URI.create("api/v1/rooms/" + id)) .build(); @@ -75,4 +80,24 @@ public ResponseEntity> search(@ParameterObject @PageableDefault Pagea return ResponseEntity.ok() .body(roomService.search(pageable, condition)); } + + @Operation( + summary = "방 조회", + description = "방을 조회합니다.", + responses = { + @ApiResponse( + responseCode = "200", + description = "방 조회 성공" + ), + @ApiResponse( + responseCode = "400", + description = "방 조회 실패", + content = @Content(schema = @Schema(implementation = ErrorResponse.class)) + ), + }) + @GetMapping("/{id}") + public ResponseEntity findById(@PathVariable Long id) { + return ResponseEntity.ok() + .body(roomService.findOne(id)); + } } diff --git a/src/main/java/com/koliving/api/user/User.java b/src/main/java/com/koliving/api/user/User.java index dea061fa..94eaf7c9 100644 --- a/src/main/java/com/koliving/api/user/User.java +++ b/src/main/java/com/koliving/api/user/User.java @@ -58,6 +58,9 @@ public class User implements UserDetails { private String description; + @Column + private String imageUrl; + @Enumerated(EnumType.STRING) @Column(name = "USER_ROLE") private UserRole userRole; @@ -139,5 +142,4 @@ public boolean isCredentialsNonExpired() { public boolean checkPassword(String password) { return this.password.equals(password); } - } diff --git a/src/test/java/com/koliving/api/fixtures/UserFixture.java b/src/test/java/com/koliving/api/fixtures/UserFixture.java new file mode 100644 index 00000000..8070eecd --- /dev/null +++ b/src/test/java/com/koliving/api/fixtures/UserFixture.java @@ -0,0 +1,14 @@ +package com.koliving.api.fixtures; + +import com.koliving.api.user.User; + +import java.util.UUID; + +public class UserFixture { + public static User createUser() { + User user = User.builder() + .email(String.format("%s@koliving.com", UUID.randomUUID())).build(); + + return user; + } +} diff --git a/src/test/java/com/koliving/api/room/infra/RoomRepositoryTest.java b/src/test/java/com/koliving/api/room/infra/RoomRepositoryTest.java index eb1f723d..138006e0 100644 --- a/src/test/java/com/koliving/api/room/infra/RoomRepositoryTest.java +++ b/src/test/java/com/koliving/api/room/infra/RoomRepositoryTest.java @@ -4,8 +4,10 @@ import static com.koliving.api.fixtures.MaintenanceFixture.관리비_없음; import static com.koliving.api.fixtures.RoomInfoFixture.스튜디오_방0_욕실1_룸메1; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.useRepresentation; import com.koliving.api.BaseDataJpaTest; +import com.koliving.api.fixtures.UserFixture; import com.koliving.api.location.domain.Location; import com.koliving.api.location.domain.LocationType; import com.koliving.api.location.infra.LocationRepository; @@ -19,6 +21,9 @@ import java.util.List; import java.util.NoSuchElementException; import java.util.stream.Collectors; + +import com.koliving.api.user.User; +import com.koliving.api.user.UserRepository; import org.assertj.core.util.Sets; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -38,6 +43,9 @@ class RoomRepositoryTest extends BaseDataJpaTest { @Autowired private RoomRepository roomRepository; + @Autowired + private UserRepository userRepository; + @BeforeEach void setUp() { final List furnishings = Arrays.stream(FurnishingType.values()) @@ -59,6 +67,11 @@ void create() { ) ); + User user = UserFixture.createUser(); + + userRepository.save(user); + + // when Room savedRoom = roomRepository.save( Room.valueOf( @@ -71,7 +84,7 @@ void create() { LocalDate.of(2023, 8, 29), "설명이에요", Collections.emptySet() - ) + ).by(user) ); Room actual = roomRepository.findById(savedRoom.getId()) @@ -101,6 +114,9 @@ void createWithFurnishings() { ) ); + User user = UserFixture.createUser(); + userRepository.save(user); + final Furnishing tv = furnishingRepository.findByType(FurnishingType.TV) .orElseThrow(NoSuchElementException::new); @@ -119,7 +135,7 @@ void createWithFurnishings() { LocalDate.of(2023, 8, 29), "설명이에요", Collections.emptySet() - ) + ).by(user) ); roomRepository.flush();