diff --git a/backend/src/main/java/middle_point_search/backend/domains/place/controller/PlaceController.java b/backend/src/main/java/middle_point_search/backend/domains/place/controller/PlaceController.java index 6e44822a..8ac626f5 100644 --- a/backend/src/main/java/middle_point_search/backend/domains/place/controller/PlaceController.java +++ b/backend/src/main/java/middle_point_search/backend/domains/place/controller/PlaceController.java @@ -3,6 +3,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -20,8 +21,9 @@ import middle_point_search.backend.common.dto.ErrorResponse; import middle_point_search.backend.common.util.MemberLoader; import middle_point_search.backend.domains.member.domain.Member; -import middle_point_search.backend.domains.place.dto.PlaceDTO.PlaceSaveOrUpdateRequest; -import middle_point_search.backend.domains.place.dto.PlaceDTO.PlacesFindResponse; +import middle_point_search.backend.domains.place.dto.request.SavePlaceRequest; +import middle_point_search.backend.domains.place.dto.request.UpdatePlaceRequest; +import middle_point_search.backend.domains.place.dto.response.FindPlacesResponse; import middle_point_search.backend.domains.place.service.PlaceService; @Tag(name = "PLACE API", description = "회원 장소에 대한 API입니다.") @@ -69,7 +71,7 @@ public class PlaceController { ) public ResponseEntity> placeSave( @PathVariable("roomId") Long roomId, - @RequestBody @Valid PlaceSaveOrUpdateRequest request + @RequestBody @Valid SavePlaceRequest request ) { Member member = memberLoader.getMember(); @@ -78,12 +80,59 @@ public ResponseEntity> placeSave( return ResponseEntity.ok(DataResponse.ok()); } + @PatchMapping("/rooms/{roomId}") + @Operation( + summary = "장소 변경하기", + description = """ + placeId, 주소와 좌표를 사용하여 장소 변경 + + AccessToken 필요.""", + responses = { + @ApiResponse( + responseCode = "200", + description = "성공" + ), + @ApiResponse( + responseCode = "400", + description = "요청 파라미터가 잘못되었습니다.[C-202]", + content = @Content(schema = @Schema(implementation = ErrorResponse.class)) + ), + @ApiResponse( + responseCode = "401", + description = "인증에 실패하였습니다.[C-101]", + content = @Content(schema = @Schema(implementation = ErrorResponse.class)) + ), + @ApiResponse( + responseCode = "402", + description = "Access Token을 재발급해야합니다.[A-004]", + content = @Content(schema = @Schema(implementation = ErrorResponse.class)) + ), + @ApiResponse( + responseCode = "403", + description = "해당 방의 회원이 아닙니다.[MR-003]", + content = @Content(schema = @Schema(implementation = ErrorResponse.class)) + ) + } + ) + public ResponseEntity> updatePlace( + @PathVariable("roomId") Long roomId, + @RequestBody @Valid UpdatePlaceRequest request + ) { + Member member = memberLoader.getMember(); + + placeService.updatePlace(roomId, member, request); + + return ResponseEntity.ok(DataResponse.ok()); + } + @GetMapping("/rooms/{roomId}") @Operation( summary = "장소 조회하기", description = """ 저장한 장소들 조회하기. + 내가 저장한 장소들과 다른 회원이 저장한 장소들을 모두 조회한다. + AccessToken 필요.""", responses = { @ApiResponse( @@ -112,16 +161,17 @@ public ResponseEntity> placeSave( ), } ) - public ResponseEntity> placesFind( + public ResponseEntity> placesFind( @PathVariable("roomId") Long roomId ) { Long memberId = memberLoader.getMemberId(); - PlacesFindResponse response = placeService.findPlaces(memberId, roomId); + FindPlacesResponse response = placeService.findPlaces(memberId, roomId); return ResponseEntity.ok(DataResponse.from(response)); } + @DeleteMapping("/{placeId}") @Operation( summary = "장소 삭제하기", @@ -166,3 +216,4 @@ public ResponseEntity> placeDelete( return ResponseEntity.ok(DataResponse.ok()); } } + diff --git a/backend/src/main/java/middle_point_search/backend/domains/place/domain/Place.java b/backend/src/main/java/middle_point_search/backend/domains/place/domain/Place.java index f35cfe7b..670e15ed 100644 --- a/backend/src/main/java/middle_point_search/backend/domains/place/domain/Place.java +++ b/backend/src/main/java/middle_point_search/backend/domains/place/domain/Place.java @@ -12,8 +12,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import middle_point_search.backend.domains.member.domain.Member; -import middle_point_search.backend.domains.place.dto.PlaceDTO.PlaceSaveOrUpdateRequest; -import middle_point_search.backend.domains.place.dto.PlaceDTO.PlaceVO; +import middle_point_search.backend.domains.place.dto.request.SavePlaceRequest; import middle_point_search.backend.domains.room.domain.Room; @Entity @@ -64,26 +63,22 @@ private Place(String siDo, String siGunGu, String roadNameAddress, Double addres this.googlePlaceId = googlePlaceId; } - public static Place from(PlaceSaveOrUpdateRequest placeSaveOrUpdateRequest, Room room, Member member, - String googlePlaceId) { - String siDo = placeSaveOrUpdateRequest.getSiDo(); - String siGunGu = placeSaveOrUpdateRequest.getSiGunGu(); - String roadNameAddress = placeSaveOrUpdateRequest.getRoadNameAddress(); - Double addressLatitude = placeSaveOrUpdateRequest.getAddressLat(); - Double addressLongitude = placeSaveOrUpdateRequest.getAddressLong(); - - return new Place(siDo, siGunGu, roadNameAddress, addressLatitude, addressLongitude, room, member, - googlePlaceId); - } - - public PlaceVO toVO() { - return new PlaceVO( - this.id, - this.siDo, - this.siGunGu, - this.roadNameAddress, - this.addressLatitude, - this.addressLongitude + public static Place + from( + SavePlaceRequest request, + Room room, + Member member, + String googlePlaceId + ) { + return new Place( + request.getSiDo(), + request.getSiGunGu(), + request.getRoadNameAddress(), + request.getAddressLat(), + request.getAddressLong(), + room, + member, + googlePlaceId ); } @@ -91,12 +86,4 @@ private void addRoom(Room room) { this.room = room; room.getPlaces().add(this); } - - public void update(PlaceSaveOrUpdateRequest request) { - this.siDo = request.getSiDo(); - this.siGunGu = request.getSiGunGu(); - this.roadNameAddress = request.getRoadNameAddress(); - this.addressLatitude = request.getAddressLat(); - this.addressLongitude = request.getAddressLong(); - } } diff --git a/backend/src/main/java/middle_point_search/backend/domains/place/dto/PlaceDTO.java b/backend/src/main/java/middle_point_search/backend/domains/place/dto/PlaceDTO.java deleted file mode 100644 index 36e976ef..00000000 --- a/backend/src/main/java/middle_point_search/backend/domains/place/dto/PlaceDTO.java +++ /dev/null @@ -1,51 +0,0 @@ -package middle_point_search.backend.domains.place.dto; - -import java.util.List; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Positive; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -public class PlaceDTO { - - @Getter - @NoArgsConstructor(access = AccessLevel.PRIVATE) - public static class PlaceSaveOrUpdateRequest { - - @NotBlank(message = "siDo는 비어 있을 수 없습니다.") - private String siDo; - @NotBlank(message = "siGunGu는 비어 있을 수 없습니다.") - private String siGunGu; - @NotBlank(message = "roadNameAddress는 비어 있을 수 없습니다.") - private String roadNameAddress; - @NotNull - @Positive(message = "addreesLat은 양수이어야 합니다.") - private Double addressLat; - @NotNull - @Positive(message = "addreesLong은 양수이어야 합니다.") - private Double addressLong; - } - - @Getter - @AllArgsConstructor - public static class PlacesFindResponse { - - private final Boolean existence; - private final List places; - } - - @Getter - @AllArgsConstructor - public static class PlaceVO { - private final Long placeId; - private final String siDo; - private final String siGunGu; - private final String roadNameAddress; - private final Double addressLat; - private final Double addressLong; - } -} diff --git a/backend/src/main/java/middle_point_search/backend/domains/place/dto/request/SavePlaceRequest.java b/backend/src/main/java/middle_point_search/backend/domains/place/dto/request/SavePlaceRequest.java new file mode 100644 index 00000000..9f1655d4 --- /dev/null +++ b/backend/src/main/java/middle_point_search/backend/domains/place/dto/request/SavePlaceRequest.java @@ -0,0 +1,27 @@ +package middle_point_search.backend.domains.place.dto.request; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class SavePlaceRequest { + + @NotBlank(message = "siDo는 비어 있을 수 없습니다.") + private String siDo; + + @NotBlank(message = "siGunGu는 비어 있을 수 없습니다.") + private String siGunGu; + + @NotBlank(message = "roadNameAddress는 비어 있을 수 없습니다.") + private String roadNameAddress; + + @NotNull(message = "addressLat는 비어 있을 수 없습니다.") + private Double addressLat; + + @NotNull(message = "addressLong는 비어 있을 수 없습니다.") + private Double addressLong; +} \ No newline at end of file diff --git a/backend/src/main/java/middle_point_search/backend/domains/place/dto/request/UpdatePlaceRequest.java b/backend/src/main/java/middle_point_search/backend/domains/place/dto/request/UpdatePlaceRequest.java new file mode 100644 index 00000000..275225ff --- /dev/null +++ b/backend/src/main/java/middle_point_search/backend/domains/place/dto/request/UpdatePlaceRequest.java @@ -0,0 +1,30 @@ +package middle_point_search.backend.domains.place.dto.request; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class UpdatePlaceRequest { + + @NotNull(message = "placeId는 비어 있을 수 없습니다.") + private Long placeId; + + @NotBlank(message = "siDo는 비어 있을 수 없습니다.") + private String siDo; + + @NotBlank(message = "siGunGu는 비어 있을 수 없습니다.") + private String siGunGu; + + @NotBlank(message = "roadNameAddress는 비어 있을 수 없습니다.") + private String roadNameAddress; + + @NotNull(message = "addressLat는 비어 있을 수 없습니다.") + private Double addressLat; + + @NotNull(message = "addressLong는 비어 있을 수 없습니다.") + private Double addressLong; +} diff --git a/backend/src/main/java/middle_point_search/backend/domains/place/dto/response/FindPlacesResponse.java b/backend/src/main/java/middle_point_search/backend/domains/place/dto/response/FindPlacesResponse.java new file mode 100644 index 00000000..55e62549 --- /dev/null +++ b/backend/src/main/java/middle_point_search/backend/domains/place/dto/response/FindPlacesResponse.java @@ -0,0 +1,40 @@ +package middle_point_search.backend.domains.place.dto.response; + +import java.util.List; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import middle_point_search.backend.domains.place.domain.Place; + +@Getter +@AllArgsConstructor +public class FindPlacesResponse { + + private final Boolean myLocationExistence; + private final List myLocations; + private final Boolean friendLocationExistence; + private final List friendLocations; + + @Getter + @AllArgsConstructor + public static class PlaceVO { + private final Long placeId; + private final String siDo; + private final String siGunGu; + private final String roadNameAddress; + private final Double addressLat; + private final Double addressLong; + + public static PlaceVO from(Place place) { + return new PlaceVO( + place.getId(), + place.getSiDo(), + place.getSiGunGu(), + place.getRoadNameAddress(), + place.getAddressLatitude(), + place.getAddressLongitude() + ); + } + } +} + diff --git a/backend/src/main/java/middle_point_search/backend/domains/place/repository/PlaceRepository.java b/backend/src/main/java/middle_point_search/backend/domains/place/repository/PlaceRepository.java index 966c613c..ff7d5345 100644 --- a/backend/src/main/java/middle_point_search/backend/domains/place/repository/PlaceRepository.java +++ b/backend/src/main/java/middle_point_search/backend/domains/place/repository/PlaceRepository.java @@ -3,6 +3,9 @@ import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import middle_point_search.backend.domains.place.domain.Place; @@ -11,4 +14,24 @@ public interface PlaceRepository extends JpaRepository { List findAllByRoom_Id(Long roomId); void deleteByIdAndRoom_Id(Long placeId, Long roomId); + + @Modifying + @Query("UPDATE Place p " + + "SET p.member.id = :memberId, " + + " p.siDo = :siDo, " + + " p.siGunGu = :siGunGu, " + + " p.roadNameAddress = :roadNameAddress, " + + " p.addressLatitude = :addressLat, " + + " p.addressLongitude = :addressLong, " + + " p.googlePlaceId = :googlePlaceId " + + "WHERE p.id = :placeId") + void updatePlace( + @Param("memberId") Long memberId, + @Param("placeId") Long placeId, + @Param("googlePlaceId") String googlePlaceId, + @Param("siDo") String siDo, + @Param("siGunGu") String siGunGu, + @Param("roadNameAddress") String roadNameAddress, + @Param("addressLat") Double addressLat, + @Param("addressLong") Double addressLong); } diff --git a/backend/src/main/java/middle_point_search/backend/domains/place/service/PlaceService.java b/backend/src/main/java/middle_point_search/backend/domains/place/service/PlaceService.java index 8311e48b..04dfea85 100644 --- a/backend/src/main/java/middle_point_search/backend/domains/place/service/PlaceService.java +++ b/backend/src/main/java/middle_point_search/backend/domains/place/service/PlaceService.java @@ -3,7 +3,6 @@ import static middle_point_search.backend.common.exception.errorCode.UserErrorCode.*; import java.util.List; -import java.util.stream.Collectors; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -15,9 +14,9 @@ import middle_point_search.backend.domains.member.domain.Member; import middle_point_search.backend.domains.memberRoom.MemberRoomValidateService; import middle_point_search.backend.domains.place.domain.Place; -import middle_point_search.backend.domains.place.dto.PlaceDTO.PlaceSaveOrUpdateRequest; -import middle_point_search.backend.domains.place.dto.PlaceDTO.PlaceVO; -import middle_point_search.backend.domains.place.dto.PlaceDTO.PlacesFindResponse; +import middle_point_search.backend.domains.place.dto.request.SavePlaceRequest; +import middle_point_search.backend.domains.place.dto.request.UpdatePlaceRequest; +import middle_point_search.backend.domains.place.dto.response.FindPlacesResponse; import middle_point_search.backend.domains.place.repository.PlaceRepository; import middle_point_search.backend.domains.room.domain.Room; import middle_point_search.backend.domains.room.service.RoomService; @@ -33,9 +32,33 @@ public class PlaceService { private final MemberRoomValidateService memberRoomValidateService; private final GoogleService googleService; + // 장소 조회 + public FindPlacesResponse findPlaces(Long memberId, Long roomId) { + // 회원이 방에 속해있는지 확인 + memberRoomValidateService.validateAuthorizedMember(memberId, roomId); + + List places = placeRepository.findAllByRoom_Id(roomId); + + List myPlaces = places.stream() + .filter(place -> place.getMember().getId().equals(memberId)) + .map(FindPlacesResponse.PlaceVO::from) + .toList(); + + List friendPlaces = places.stream() + .filter(place -> !place.getMember().getId().equals(memberId)) + .map(FindPlacesResponse.PlaceVO::from) + .toList(); + + return new FindPlacesResponse( + !myPlaces.isEmpty(), + myPlaces, + !friendPlaces.isEmpty(), + friendPlaces); + } + //장소 저장 @Transactional(rollbackFor = {CustomException.class}) - public void savePlace(Long roomId, Member member, PlaceSaveOrUpdateRequest request) { + public void savePlace(Long roomId, Member member, SavePlaceRequest request) { // 회원이 방에 속해있는지 확인 memberRoomValidateService.validateAuthorizedMember(member.getId(), roomId); @@ -48,19 +71,24 @@ public void savePlace(Long roomId, Member member, PlaceSaveOrUpdateRequest reque placeRepository.save(Place.from(request, room, member, googlePlaceId)); } - // 장소 조회 - public PlacesFindResponse findPlaces(Long memberId, Long roomId) { + //장소 업데이트 + @Transactional(rollbackFor = {CustomException.class}) + public void updatePlace(Long roomId, Member member, UpdatePlaceRequest request) { // 회원이 방에 속해있는지 확인 - memberRoomValidateService.validateAuthorizedMember(memberId, roomId); - - List places = placeRepository.findAllByRoom_Id(roomId); + memberRoomValidateService.validateAuthorizedMember(member.getId(), roomId); - List placeVOs = places.stream() - .map(Place::toVO) - .collect(Collectors.toList()); + // 구글 placeId 조회 + String googlePlaceId = googleService.findGooglePlaceId(request.getAddressLat(), request.getAddressLong()); - boolean existence = !placeVOs.isEmpty(); - return new PlacesFindResponse(existence, placeVOs); + placeRepository.updatePlace( + member.getId(), + request.getPlaceId(), + googlePlaceId, + request.getSiDo(), + request.getSiGunGu(), + request.getRoadNameAddress(), + request.getAddressLat(), + request.getAddressLong()); } // 장소 삭제