diff --git a/src/main/java/com/wayble/server/review/controller/ReviewController.java b/src/main/java/com/wayble/server/review/controller/ReviewController.java index ee0ae4ac..4c493c15 100644 --- a/src/main/java/com/wayble/server/review/controller/ReviewController.java +++ b/src/main/java/com/wayble/server/review/controller/ReviewController.java @@ -2,6 +2,7 @@ import com.wayble.server.common.response.CommonResponse; import com.wayble.server.review.dto.ReviewRegisterDto; +import com.wayble.server.review.dto.ReviewResponseDto; import com.wayble.server.review.service.ReviewService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; @@ -9,9 +10,13 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.validation.Valid; +import jakarta.validation.constraints.Pattern; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; + +import java.util.List; + @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/wayble-zones/{waybleZoneId}/reviews") @@ -38,4 +43,17 @@ public CommonResponse registerReview( reviewService.registerReview(waybleZoneId, dto, authorizationHeader); return CommonResponse.success("리뷰가 등록되었습니다."); } + + @GetMapping + @Operation(summary = "웨이블존 리뷰 목록 조회", description = "웨이블존에 대한 리뷰를 최신순 또는 평점순으로 조회합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "리뷰 목록 조회 성공"), + @ApiResponse(responseCode = "404", description = "해당 웨이블존이 존재하지 않음") + }) + public CommonResponse> getReviews( + @PathVariable Long waybleZoneId, + @RequestParam(defaultValue = "latest") String sort + ) { + return CommonResponse.success(reviewService.getReviews(waybleZoneId, sort)); + } } \ No newline at end of file diff --git a/src/main/java/com/wayble/server/review/dto/ReviewResponseDto.java b/src/main/java/com/wayble/server/review/dto/ReviewResponseDto.java index d99466e9..db03dd8e 100644 --- a/src/main/java/com/wayble/server/review/dto/ReviewResponseDto.java +++ b/src/main/java/com/wayble/server/review/dto/ReviewResponseDto.java @@ -1,4 +1,14 @@ package com.wayble.server.review.dto; -public record ReviewResponseDto() { -} +import java.time.LocalDate; +import java.util.List; + +public record ReviewResponseDto( + Long reviewId, + String userNickname, + double rating, + String content, + LocalDate visitDate, + int likes, + List images +) {} diff --git a/src/main/java/com/wayble/server/review/repository/ReviewRepository.java b/src/main/java/com/wayble/server/review/repository/ReviewRepository.java index bffe95a4..94d4e08a 100644 --- a/src/main/java/com/wayble/server/review/repository/ReviewRepository.java +++ b/src/main/java/com/wayble/server/review/repository/ReviewRepository.java @@ -3,5 +3,9 @@ import com.wayble.server.review.entity.Review; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + public interface ReviewRepository extends JpaRepository { + List findByWaybleZoneIdOrderByCreatedAtDesc(Long waybleZoneId); // 최신순 + List findByWaybleZoneIdOrderByRatingDesc(Long waybleZoneId); // 평점순 } diff --git a/src/main/java/com/wayble/server/review/service/ReviewService.java b/src/main/java/com/wayble/server/review/service/ReviewService.java index 060ed3bc..06e1f34d 100644 --- a/src/main/java/com/wayble/server/review/service/ReviewService.java +++ b/src/main/java/com/wayble/server/review/service/ReviewService.java @@ -2,6 +2,7 @@ import com.wayble.server.common.exception.ApplicationException; import com.wayble.server.review.dto.ReviewRegisterDto; +import com.wayble.server.review.dto.ReviewResponseDto; import com.wayble.server.review.entity.Review; import com.wayble.server.review.entity.ReviewImage; import com.wayble.server.review.repository.ReviewImageRepository; @@ -15,6 +16,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.util.List; +import java.util.stream.Collectors; + @Service @RequiredArgsConstructor public class ReviewService { @@ -43,4 +47,29 @@ public void registerReview(Long zoneId, ReviewRegisterDto dto, String token) { // visitDate 및 facilities 저장은 필요시 추가 구현 } + + public List getReviews(Long zoneId, String sort) { + WaybleZone zone = waybleZoneRepository.findById(zoneId) + .orElseThrow(() -> new ApplicationException(WaybleZoneErrorCase.WAYBLE_ZONE_NOT_FOUND)); + + List reviews = switch (sort) { + case "rating" -> reviewRepository.findByWaybleZoneIdOrderByRatingDesc(zoneId); + case "latest" -> reviewRepository.findByWaybleZoneIdOrderByCreatedAtDesc(zoneId); + default -> reviewRepository.findByWaybleZoneIdOrderByCreatedAtDesc(zoneId); + }; + + return reviews.stream() + .map(review -> new ReviewResponseDto( + review.getId(), + review.getUser().getNickname(), + review.getRating(), + review.getContent(), + review.getCreatedAt().toLocalDate(), + review.getLikeCount(), + review.getReviewImageList().stream() + .map(ReviewImage::getImageUrl) + .collect(Collectors.toList()) + )) + .collect(Collectors.toList()); + } } \ No newline at end of file diff --git a/src/main/java/com/wayble/server/user/entity/UserPlace.java b/src/main/java/com/wayble/server/user/entity/UserPlace.java new file mode 100644 index 00000000..d174c59d --- /dev/null +++ b/src/main/java/com/wayble/server/user/entity/UserPlace.java @@ -0,0 +1,29 @@ +package com.wayble.server.user.entity; + +import com.wayble.server.common.entity.BaseEntity; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + + +@Entity +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "user_place") // 유져가 저장한 장소 +public class UserPlace extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "title", nullable = false) + private String title; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id", nullable = false) + private User user; +} \ No newline at end of file diff --git a/src/main/java/com/wayble/server/user/entity/UserPlaceWaybleZoneMapping.java b/src/main/java/com/wayble/server/user/entity/UserPlaceWaybleZoneMapping.java new file mode 100644 index 00000000..b746bdd1 --- /dev/null +++ b/src/main/java/com/wayble/server/user/entity/UserPlaceWaybleZoneMapping.java @@ -0,0 +1,29 @@ +package com.wayble.server.user.entity; + +import com.wayble.server.wayblezone.entity.WaybleZone; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "user_place_wayble_zone_mapping") // 유저 장소와 웨이블존 다대다 매핑 +public class UserPlaceWaybleZoneMapping { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_place_id", nullable = false) + private UserPlace userPlace; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "wayble_zone_id", nullable = false) + private WaybleZone waybleZone; +} diff --git a/src/main/java/com/wayble/server/wayblezone/entity/WaybleZone.java b/src/main/java/com/wayble/server/wayblezone/entity/WaybleZone.java index 04478f1a..9f36d70a 100644 --- a/src/main/java/com/wayble/server/wayblezone/entity/WaybleZone.java +++ b/src/main/java/com/wayble/server/wayblezone/entity/WaybleZone.java @@ -3,6 +3,7 @@ import com.wayble.server.common.entity.Address; import com.wayble.server.common.entity.BaseEntity; import com.wayble.server.review.entity.Review; +import com.wayble.server.user.entity.UserPlaceWaybleZoneMapping; import jakarta.persistence.*; import lombok.*; import org.hibernate.annotations.SQLDelete; @@ -18,23 +19,29 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) @SQLDelete(sql = "UPDATE wayble_zone SET deleted_at = now() WHERE id = ?") @SQLRestriction("deleted_at IS NULL") -@Table(name = "wayble_zone") +@Table(name = "wayble_zone") // 웨이블 존 public class WaybleZone extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - private String zoneName; + private String zoneName; // 가게 이름 - private String contactNumber; + private String contactNumber; // 가게 전화 번호 @Enumerated(EnumType.STRING) @Column(nullable = false) - private WaybleZoneType zoneType; + private WaybleZoneType zoneType; // 가게 타입 (음식점,카페,편의점) @Embedded - private Address address; + private Address address; // 주소 + + @Column(nullable = false) + private double rating = 0.0; // 누적 평균 평점 + + @Column(nullable = false) + private int reviewCount = 0; // 리뷰 수 @OneToMany(mappedBy = "waybleZone", cascade = CascadeType.ALL, orphanRemoval = true) private List waybleZoneImageList = new ArrayList<>(); @@ -42,19 +49,12 @@ public class WaybleZone extends BaseEntity { @OneToMany(mappedBy = "waybleZone", cascade = CascadeType.ALL, orphanRemoval = true) private List reviewList = new ArrayList<>(); - /** - * TODO: 영업 시간 구현 필요 - */ - - /** - * TODO: 장애 시설 정보 구현 필요 - */ + @OneToMany(mappedBy = "waybleZone", cascade = CascadeType.ALL, orphanRemoval = true) + private List operatingHours = new ArrayList<>(); - /** - * TODO: Review 관련 엔티티 구현 필요 - */ + @OneToOne(mappedBy = "waybleZone", cascade = CascadeType.ALL, orphanRemoval = true) + private WaybleZoneFacility facility; - /** - * TODO: 내가 저장한 장소 관련 엔티티 구현 필요 - */ + @OneToMany(mappedBy = "waybleZone", cascade = CascadeType.ALL, orphanRemoval = true) + private List userPlaceMappings = new ArrayList<>(); } diff --git a/src/main/java/com/wayble/server/wayblezone/entity/WaybleZoneFacility.java b/src/main/java/com/wayble/server/wayblezone/entity/WaybleZoneFacility.java new file mode 100644 index 00000000..6c094364 --- /dev/null +++ b/src/main/java/com/wayble/server/wayblezone/entity/WaybleZoneFacility.java @@ -0,0 +1,43 @@ +package com.wayble.server.wayblezone.entity; + +import com.wayble.server.common.entity.BaseEntity; +import jakarta.persistence.*; +import lombok.*; + + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@Table(name = "wayble_zone_facility") // 웨이블존 편의 시설 정보 +public class WaybleZoneFacility extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + // 웨이블존 연관관계 (1:1로 가정 => 웨이블존당 시설 1세트) + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "wayble_zone_id", nullable = false) + private WaybleZone waybleZone; + + // 시설 정보 (있음/없음 여부) + @Column(nullable = false) + private boolean hasSlope; // 경사로 + + @Column(nullable = false) + private boolean hasNoDoorStep; // 문턱 + + @Column(nullable = false) + private boolean hasElevator; // 엘리베이터 + + @Column(nullable = false) + private boolean hasTableSeat; // 테이블석 + + @Column(nullable = false) + private boolean hasDisabledToilet; // 장애인 화장실 + + @Column(length = 20) + private String floorInfo; // 층수 정보 (ex: "1층", "B1", "2층") +} \ No newline at end of file diff --git a/src/main/java/com/wayble/server/wayblezone/entity/WaybleZoneOperatingHour.java b/src/main/java/com/wayble/server/wayblezone/entity/WaybleZoneOperatingHour.java new file mode 100644 index 00000000..ad0482f4 --- /dev/null +++ b/src/main/java/com/wayble/server/wayblezone/entity/WaybleZoneOperatingHour.java @@ -0,0 +1,64 @@ +package com.wayble.server.wayblezone.entity; + +import com.wayble.server.common.entity.BaseEntity; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.DayOfWeek; +import java.time.LocalTime; + + +@Entity +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "wayble_zone_operating_hours", + uniqueConstraints = @UniqueConstraint(columnNames = {"wayble_zone_id", "day_of_week"})) // 웨이블존 영업 정보 +public class WaybleZoneOperatingHour extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "wayble_zone_id", nullable = false) + private WaybleZone waybleZone; + + @Enumerated(EnumType.STRING) + @Column(name = "day_of_week", nullable = false) + private DayOfWeek dayOfWeek; // 요일 정보 + + @Column(name = "start_time", nullable = false) + private LocalTime startTime; // 영업 시작 시간 + + @Column(name = "close_time", nullable = false) + private LocalTime closeTime; // 영업 종료 시간 + + @Column(name = "is_closed", nullable = false) + private Boolean isClosed = false; // 영업 여부 + + /* 필요하게 되면 나중에 사용 + @PrePersist + @PreUpdate + private void validateTimes() { + if (isClosed) { + // 휴무일에는 시간 정보가 있어서는 안 됨 + if (startTime != null || closeTime != null) { + throw new IllegalStateException("휴무일에는 시간 정보가 있을 수 없습니다."); + } + } else { + // 영업일에는 시간 정보가 필수 + if (startTime == null || closeTime == null) { + throw new IllegalStateException("영업일에는 시작 시간과 종료 시간이 필요합니다."); + } + if (startTime.equals(closeTime)) { + throw new IllegalStateException("시작 시간과 종료 시간이 동일할 수 없습니다."); + } + } + } + */ +} \ No newline at end of file diff --git a/src/main/java/com/wayble/server/wayblezone/exception/WaybleZoneErrorCase.java b/src/main/java/com/wayble/server/wayblezone/exception/WaybleZoneErrorCase.java index df25ddf9..dd569ae0 100644 --- a/src/main/java/com/wayble/server/wayblezone/exception/WaybleZoneErrorCase.java +++ b/src/main/java/com/wayble/server/wayblezone/exception/WaybleZoneErrorCase.java @@ -8,7 +8,7 @@ @RequiredArgsConstructor public enum WaybleZoneErrorCase implements ErrorCase { - WAYBLE_ZONE_NOT_FOUND(400, 2001, "웨이블존을 찾을 수 없습니다."); + WAYBLE_ZONE_NOT_FOUND(404, 2001, "해당 웨이블존을 찾을 수 없습니다."); private final Integer httpStatusCode; private final Integer errorCode;