diff --git a/build.gradle b/build.gradle index 3e0ad970..0db3be14 100644 --- a/build.gradle +++ b/build.gradle @@ -35,6 +35,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'io.jsonwebtoken:jjwt-api:0.11.5' implementation 'jakarta.servlet:jakarta.servlet-api:6.0.0' + implementation 'com.amazonaws:aws-java-sdk-core:1.12.698' + implementation 'com.amazonaws:aws-java-sdk-s3:1.12.698' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' diff --git a/src/main/java/com/wayble/server/common/entity/BaseEntity.java b/src/main/java/com/wayble/server/common/entity/BaseEntity.java index be3badb6..9044360d 100644 --- a/src/main/java/com/wayble/server/common/entity/BaseEntity.java +++ b/src/main/java/com/wayble/server/common/entity/BaseEntity.java @@ -23,4 +23,8 @@ public class BaseEntity { @Column(name = "deleted_at") private LocalDateTime deletedAt; + + public void restore() { + this.deletedAt = null; + } } diff --git a/src/main/java/com/wayble/server/explore/controller/WaybleZoneSearchController.java b/src/main/java/com/wayble/server/explore/controller/WaybleZoneSearchController.java index 00141839..0ac6d6c6 100644 --- a/src/main/java/com/wayble/server/explore/controller/WaybleZoneSearchController.java +++ b/src/main/java/com/wayble/server/explore/controller/WaybleZoneSearchController.java @@ -21,7 +21,7 @@ public class WaybleZoneSearchController { private final WaybleZoneSearchService waybleZoneSearchService; - @GetMapping("") + @GetMapping("/maps") public CommonResponse> findByCondition( @Valid @ModelAttribute WaybleZoneSearchConditionDto conditionDto, @RequestParam(name = "page", defaultValue = "0") int page, diff --git a/src/main/java/com/wayble/server/explore/dto/FacilityResponseDto.java b/src/main/java/com/wayble/server/explore/dto/FacilityResponseDto.java new file mode 100644 index 00000000..b80eb4b9 --- /dev/null +++ b/src/main/java/com/wayble/server/explore/dto/FacilityResponseDto.java @@ -0,0 +1,29 @@ +package com.wayble.server.explore.dto; + +import com.wayble.server.explore.entity.EsWaybleZoneFacility; +import lombok.Builder; + +@Builder +public record FacilityResponseDto( + Boolean hasSlope, + Boolean hasNoDoorStep, + Boolean hasElevator, + Boolean hasTableSeat, + Boolean hasDisabledToilet, + String floorInfo +) { + public static FacilityResponseDto from(EsWaybleZoneFacility facility) { + if (facility == null) { + return null; + } + + return FacilityResponseDto.builder() + .hasSlope(facility.isHasSlope()) + .hasNoDoorStep(facility.isHasNoDoorStep()) + .hasElevator(facility.isHasElevator()) + .hasTableSeat(facility.isHasTableSeat()) + .hasDisabledToilet(facility.isHasDisabledToilet()) + .floorInfo(facility.getFloorInfo()) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/wayble/server/explore/dto/recommend/WaybleZoneRecommendResponseDto.java b/src/main/java/com/wayble/server/explore/dto/recommend/WaybleZoneRecommendResponseDto.java index 051c0495..743519b1 100644 --- a/src/main/java/com/wayble/server/explore/dto/recommend/WaybleZoneRecommendResponseDto.java +++ b/src/main/java/com/wayble/server/explore/dto/recommend/WaybleZoneRecommendResponseDto.java @@ -1,5 +1,6 @@ package com.wayble.server.explore.dto.recommend; +import com.wayble.server.explore.dto.FacilityResponseDto; import com.wayble.server.explore.entity.WaybleZoneDocument; import com.wayble.server.wayblezone.entity.WaybleZoneType; import lombok.Builder; @@ -23,6 +24,8 @@ public record WaybleZoneRecommendResponseDto( Long reviewCount, + FacilityResponseDto facility, + Double distanceScore, Double similarityScore, @@ -42,6 +45,7 @@ public static WaybleZoneRecommendResponseDto from(WaybleZoneDocument waybleZoneD .reviewCount(waybleZoneDocument.getReviewCount()) .latitude(waybleZoneDocument.getAddress().getLocation().getLat()) .longitude(waybleZoneDocument.getAddress().getLocation().getLon()) + .facility(FacilityResponseDto.from(waybleZoneDocument.getFacility())) .distanceScore(0.0) .similarityScore(0.0) .recencyScore(0.0) diff --git a/src/main/java/com/wayble/server/explore/dto/search/WaybleZoneDocumentRegisterDto.java b/src/main/java/com/wayble/server/explore/dto/search/WaybleZoneDocumentRegisterDto.java index 67120fce..9017ca7f 100644 --- a/src/main/java/com/wayble/server/explore/dto/search/WaybleZoneDocumentRegisterDto.java +++ b/src/main/java/com/wayble/server/explore/dto/search/WaybleZoneDocumentRegisterDto.java @@ -1,6 +1,7 @@ package com.wayble.server.explore.dto.search; import com.wayble.server.common.entity.Address; +import com.wayble.server.wayblezone.entity.WaybleZoneFacility; import com.wayble.server.wayblezone.entity.WaybleZoneType; import lombok.Builder; @@ -11,6 +12,7 @@ public record WaybleZoneDocumentRegisterDto( WaybleZoneType waybleZoneType, String thumbnailImageUrl, Address address, + WaybleZoneFacility facility, Double averageRating, Long reviewCount ) { diff --git a/src/main/java/com/wayble/server/explore/dto/search/WaybleZoneSearchResponseDto.java b/src/main/java/com/wayble/server/explore/dto/search/WaybleZoneSearchResponseDto.java index aebf57ea..a6b10d48 100644 --- a/src/main/java/com/wayble/server/explore/dto/search/WaybleZoneSearchResponseDto.java +++ b/src/main/java/com/wayble/server/explore/dto/search/WaybleZoneSearchResponseDto.java @@ -1,5 +1,6 @@ package com.wayble.server.explore.dto.search; +import com.wayble.server.explore.dto.FacilityResponseDto; import com.wayble.server.explore.entity.WaybleZoneDocument; import com.wayble.server.wayblezone.entity.WaybleZoneType; import lombok.AccessLevel; @@ -24,7 +25,9 @@ public record WaybleZoneSearchResponseDto( Double averageRating, - Long reviewCount + Long reviewCount, + + FacilityResponseDto facility ) { public static WaybleZoneSearchResponseDto from(WaybleZoneDocument waybleZoneDocument, Double distance) { return WaybleZoneSearchResponseDto.builder() @@ -37,6 +40,7 @@ public static WaybleZoneSearchResponseDto from(WaybleZoneDocument waybleZoneDocu .distance(distance) .latitude(waybleZoneDocument.getAddress().getLocation().getLat()) .longitude(waybleZoneDocument.getAddress().getLocation().getLon()) + .facility(FacilityResponseDto.from(waybleZoneDocument.getFacility())) .build(); } } diff --git a/src/main/java/com/wayble/server/explore/entity/EsWaybleZoneFacility.java b/src/main/java/com/wayble/server/explore/entity/EsWaybleZoneFacility.java new file mode 100644 index 00000000..d1268c84 --- /dev/null +++ b/src/main/java/com/wayble/server/explore/entity/EsWaybleZoneFacility.java @@ -0,0 +1,34 @@ +package com.wayble.server.explore.entity; + +import com.wayble.server.wayblezone.entity.WaybleZoneFacility; +import lombok.*; + +@ToString +@Builder(access = AccessLevel.PRIVATE) +@Getter +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class EsWaybleZoneFacility { + + private boolean hasSlope; + private boolean hasNoDoorStep; + private boolean hasElevator; + private boolean hasTableSeat; + private boolean hasDisabledToilet; + private String floorInfo; + + public static EsWaybleZoneFacility from(WaybleZoneFacility facility) { + if (facility == null) { + return null; + } + + return EsWaybleZoneFacility.builder() + .hasSlope(facility.isHasSlope()) + .hasNoDoorStep(facility.isHasNoDoorStep()) + .hasElevator(facility.isHasElevator()) + .hasTableSeat(facility.isHasTableSeat()) + .hasDisabledToilet(facility.isHasDisabledToilet()) + .floorInfo(facility.getFloorInfo()) + .build(); + } +} diff --git a/src/main/java/com/wayble/server/explore/entity/WaybleZoneDocument.java b/src/main/java/com/wayble/server/explore/entity/WaybleZoneDocument.java index 09ad5e90..59fdbe1f 100644 --- a/src/main/java/com/wayble/server/explore/entity/WaybleZoneDocument.java +++ b/src/main/java/com/wayble/server/explore/entity/WaybleZoneDocument.java @@ -33,6 +33,9 @@ public class WaybleZoneDocument { @Field(type = FieldType.Object) private EsAddress address; + @Field(type = FieldType.Object) + private EsWaybleZoneFacility facility; + private double averageRating; private long reviewCount; @@ -42,8 +45,9 @@ public static WaybleZoneDocument fromEntity(WaybleZone waybleZone) { .zoneId(waybleZone.getId()) .zoneName(waybleZone.getZoneName()) .zoneType(waybleZone.getZoneType()) - .thumbnailImageUrl("thumbnail image url") // TODO: 이미지 경로 추가 + .thumbnailImageUrl(waybleZone.getMainImageUrl() != null ? waybleZone.getMainImageUrl() : null) // TODO: 이미지 경로 추가 .address(EsAddress.from(waybleZone.getAddress())) + .facility(EsWaybleZoneFacility.from(waybleZone.getFacility())) .averageRating(0.0) .reviewCount(0L) .build(); @@ -56,6 +60,7 @@ public static WaybleZoneDocument fromDto(WaybleZoneDocumentRegisterDto dto) { .zoneType(dto.waybleZoneType()) .thumbnailImageUrl(dto.thumbnailImageUrl()) .address(EsAddress.from(dto.address())) + .facility(EsWaybleZoneFacility.from(dto.facility())) .averageRating(dto.averageRating() != null ? dto.averageRating() : 0.0) .reviewCount(dto.reviewCount() != null ? dto.reviewCount() : 0L) .build(); diff --git a/src/main/java/com/wayble/server/explore/repository/recommend/WaybleZoneQueryRecommendRepository.java b/src/main/java/com/wayble/server/explore/repository/recommend/WaybleZoneQueryRecommendRepository.java index 8a2d65eb..0bd4f435 100644 --- a/src/main/java/com/wayble/server/explore/repository/recommend/WaybleZoneQueryRecommendRepository.java +++ b/src/main/java/com/wayble/server/explore/repository/recommend/WaybleZoneQueryRecommendRepository.java @@ -139,6 +139,7 @@ public List searchPersonalWaybleZones(User user, .longitude(zone.getAddress().getLocation().getLon()) .averageRating(zone.getAverageRating()) .reviewCount(zone.getReviewCount()) + .facility(com.wayble.server.explore.dto.FacilityResponseDto.from(zone.getFacility())) .distanceScore(distanceScore) .similarityScore(similarityScore) .recencyScore(recencyScore) diff --git a/src/main/java/com/wayble/server/explore/service/WaybleZoneRecommendService.java b/src/main/java/com/wayble/server/explore/service/WaybleZoneRecommendService.java index de826816..a9195d46 100644 --- a/src/main/java/com/wayble/server/explore/service/WaybleZoneRecommendService.java +++ b/src/main/java/com/wayble/server/explore/service/WaybleZoneRecommendService.java @@ -57,7 +57,7 @@ public List getWaybleZonePersonalRecommend(Long return recommendResponseDtoList; } - public Optional getTodayRecommendZone(Long userId) { + private Optional getTodayRecommendZone(Long userId) { LocalDate today = LocalDate.now(); Optional recommendLogDocument = recommendLogDocumentRepository.findByUserIdAndRecommendationDate(userId, today); @@ -72,7 +72,7 @@ public Optional getTodayRecommendZone(Long userI } } - public void saveRecommendLog(Long userId, Long zoneId) { + private void saveRecommendLog(Long userId, Long zoneId) { String logId = UUID.randomUUID().toString(); LocalDate dateNow = LocalDate.now(); @@ -88,7 +88,7 @@ public void saveRecommendLog(Long userId, Long zoneId) { recommendLogDocumentRepository.save(recommendLogDocument); } - public void updateRecommendLog(Long userId, Long zoneId) { + private void updateRecommendLog(Long userId, Long zoneId) { RecommendLogDocument recommendLogDocument = recommendLogDocumentRepository.findByUserIdAndZoneId(userId, zoneId) .orElseThrow(() -> new ApplicationException(RecommendErrorCase.RECOMMEND_LOG_NOT_EXIST)); diff --git a/src/main/java/com/wayble/server/user/repository/UserRepository.java b/src/main/java/com/wayble/server/user/repository/UserRepository.java index b375ea87..01289862 100644 --- a/src/main/java/com/wayble/server/user/repository/UserRepository.java +++ b/src/main/java/com/wayble/server/user/repository/UserRepository.java @@ -3,6 +3,8 @@ import com.wayble.server.user.entity.LoginType; import com.wayble.server.user.entity.User; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import java.util.Optional; diff --git a/src/main/java/com/wayble/server/wayblezone/controller/WaybleZoneController.java b/src/main/java/com/wayble/server/wayblezone/controller/WaybleZoneController.java index a64923a1..51063d3c 100644 --- a/src/main/java/com/wayble/server/wayblezone/controller/WaybleZoneController.java +++ b/src/main/java/com/wayble/server/wayblezone/controller/WaybleZoneController.java @@ -13,6 +13,7 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -57,7 +58,8 @@ public CommonResponse getWaybleZoneDetail( ) { // TODO: JWT에서 userId 추출해서 상세 조회 기록 남기기 - // waybleZoneVisitLogService.saveVisitLog(null, waybleZoneId); + Long userId = (Long) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + waybleZoneVisitLogService.saveVisitLog(userId, waybleZoneId); return CommonResponse.success(waybleZoneService.getWaybleZoneDetail(waybleZoneId)); } } \ No newline at end of file diff --git a/src/main/resources/elasticsearch/settings/wayble_zone_mappings.json b/src/main/resources/elasticsearch/settings/wayble_zone_mappings.json index cf56946e..48a09902 100644 --- a/src/main/resources/elasticsearch/settings/wayble_zone_mappings.json +++ b/src/main/resources/elasticsearch/settings/wayble_zone_mappings.json @@ -7,6 +7,29 @@ }, "address.location": { "type": "geo_point" + }, + "facility": { + "type": "object", + "properties": { + "hasSlope": { + "type": "boolean" + }, + "hasNoDoorStep": { + "type": "boolean" + }, + "hasElevator": { + "type": "boolean" + }, + "hasTableSeat": { + "type": "boolean" + }, + "hasDisabledToilet": { + "type": "boolean" + }, + "floorInfo": { + "type": "keyword" + } + } } } } \ No newline at end of file diff --git a/src/test/java/com/wayble/server/explore/WaybleZoneRecommendApiIntegrationTest.java b/src/test/java/com/wayble/server/explore/WaybleZoneRecommendApiIntegrationTest.java index 61b09b63..83fe14f5 100644 --- a/src/test/java/com/wayble/server/explore/WaybleZoneRecommendApiIntegrationTest.java +++ b/src/test/java/com/wayble/server/explore/WaybleZoneRecommendApiIntegrationTest.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.wayble.server.common.config.security.jwt.JwtTokenProvider; import com.wayble.server.common.entity.Address; import com.wayble.server.explore.dto.recommend.WaybleZoneRecommendResponseDto; import com.wayble.server.explore.dto.search.WaybleZoneDocumentRegisterDto; @@ -19,6 +20,7 @@ import com.wayble.server.user.entity.User; import com.wayble.server.user.entity.UserType; import com.wayble.server.user.repository.UserRepository; +import com.wayble.server.wayblezone.entity.WaybleZoneFacility; import com.wayble.server.wayblezone.entity.WaybleZoneType; import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; @@ -46,6 +48,9 @@ public class WaybleZoneRecommendApiIntegrationTest { @Autowired private MockMvc mockMvc; + @Autowired + private JwtTokenProvider jwtTokenProvider; + @Autowired private UserRepository userRepository; @@ -70,11 +75,13 @@ public class WaybleZoneRecommendApiIntegrationTest { private static final double RADIUS = 50.0; - private static final Long SAMPLES = 1000L; + private static final Long SAMPLES = 100L; private static final String baseUrl = "/api/v1/wayble-zones/recommend"; - private final Long userId = 1L; + private Long userId; + + private String token; List nameList = new ArrayList<>(Arrays.asList( "던킨도너츠", @@ -91,6 +98,15 @@ public class WaybleZoneRecommendApiIntegrationTest { @BeforeAll public void setup() { + User testUser = User.createUser( + "testUser", "testUsername", UUID.randomUUID() + "@email", "password", + generateRandomBirthDate(), Gender.MALE, LoginType.KAKAO, UserType.DISABLED + ); + + userRepository.save(testUser); + userId = testUser.getId(); + token = jwtTokenProvider.generateToken(userId, "ROLE_USER"); + for (int i = 1; i <= SAMPLES / 2; i++) { Long zoneId = (long) (Math.random() * SAMPLES) + 1; if(!recommendLogDocumentRepository.existsByUserIdAndZoneId(userId, zoneId)) { @@ -119,6 +135,8 @@ public void setup() { .longitude(points.get("longitude")) .build(); + WaybleZoneFacility facility = createRandomFacility(i); + WaybleZoneDocumentRegisterDto dto = WaybleZoneDocumentRegisterDto .builder() .zoneId((long) i) @@ -126,6 +144,7 @@ public void setup() { .address(address) .thumbnailImageUrl("thumbnail url" + i) .waybleZoneType(WaybleZoneType.values()[i % WaybleZoneType.values().length]) + .facility(facility) .averageRating(Math.random() * 5) .reviewCount((long)(Math.random() * 500)) .build(); @@ -135,12 +154,12 @@ public void setup() { User user = User.createUser( "user" + i, - "user" + i, - "user email" + i, - "user password" + i, + "username" + i, + UUID.randomUUID() + "@email", + "password" + i, generateRandomBirthDate(), Gender.values()[i % 2], - LoginType.BASIC, + LoginType.values()[i % LoginType.values().length], UserType.DISABLED ); userRepository.save(user); @@ -173,30 +192,24 @@ public void teardown() { @DisplayName("데이터 저장 테스트") public void checkDataExists() { List waybleZoneDocumentList = waybleZoneDocumentRepository.findAll(); - System.out.println("=== 웨이블존 목록 ==="); - assertThat(waybleZoneDocumentList.size()).isGreaterThan(0); + System.out.println("Total documents: " + waybleZoneDocumentList.size()); for (WaybleZoneDocument doc : waybleZoneDocumentList) { assertThat(doc.getZoneId()).isNotNull(); assertThat(doc.getZoneName()).isNotNull(); assertThat(doc.getAddress().getLocation()).isNotNull(); - System.out.println("존 정보: " + doc.toString()); - System.out.println("주소: " + doc.getAddress().toString()); } List waybleZoneVisitLogList = waybleZoneVisitLogDocumentRepository.findAll(); - System.out.println("=== 웨이블존 방문 목록 ==="); - assertThat(waybleZoneVisitLogList.size()).isGreaterThan(0); - for (WaybleZoneVisitLogDocument doc : waybleZoneVisitLogList) { - System.out.println("방문 정보" + doc.toString()); - } + System.out.println("visit log size: " + waybleZoneVisitLogList.size()); } @Test @DisplayName("추천 기록 저장 테스트") public void saveRecommendLogTest() throws Exception { MvcResult result = mockMvc.perform(get(baseUrl) + .header("Authorization", "Bearer " + token) .param("userId", String.valueOf(userId)) .param("latitude", String.valueOf(LATITUDE)) .param("longitude", String.valueOf(LONGITUDE)) @@ -224,19 +237,13 @@ public void saveRecommendLogTest() throws Exception { assertThat(recommendLogDocument.get().getUserId()).isEqualTo(userId); assertThat(recommendLogDocument.get().getZoneId()).isEqualTo(zoneId); assertThat(recommendLogDocument.get().getRecommendationDate()).isEqualTo(LocalDate.now()); - System.out.println("===recommend log==="); - System.out.println("id = " + recommendLogDocument.get().getId()); - System.out.println("userId = " +recommendLogDocument.get().getUserId()); - System.out.println("zoneId = " +recommendLogDocument.get().getZoneId()); - System.out.println("recommendationDate = " +recommendLogDocument.get().getRecommendationDate()); - System.out.println("recommendCount " +recommendLogDocument.get().getRecommendCount()); } @Test @DisplayName("추천 기능 테스트") public void recommendWaybleZone() throws Exception { - MvcResult result = mockMvc.perform(get(baseUrl) + .header("Authorization", "Bearer " + token) .param("userId", String.valueOf(userId)) .param("latitude", String.valueOf(LATITUDE)) .param("longitude", String.valueOf(LONGITUDE)) @@ -261,6 +268,13 @@ public void recommendWaybleZone() throws Exception { assertThat(dto.zoneType()).isNotNull(); assertThat(dto.latitude()).isNotNull(); assertThat(dto.longitude()).isNotNull(); + assertThat(dto.facility()).isNotNull(); + assertThat(dto.facility().hasSlope()).isNotNull(); + assertThat(dto.facility().hasNoDoorStep()).isNotNull(); + assertThat(dto.facility().hasElevator()).isNotNull(); + assertThat(dto.facility().hasTableSeat()).isNotNull(); + assertThat(dto.facility().hasDisabledToilet()).isNotNull(); + assertThat(dto.facility().floorInfo()).isNotNull(); System.out.println("zoneId = " + dto.zoneId()); System.out.println("zoneName = " + dto.zoneName()); @@ -275,16 +289,28 @@ public void recommendWaybleZone() throws Exception { System.out.println("similarityScore = " + dto.similarityScore()); System.out.println("recencyScore = " + dto.recencyScore()); System.out.println("totalScore = " + dto.totalScore()); + System.out.println("=== Facility Info ==="); + if (dto.facility() != null) { + System.out.println("hasSlope = " + dto.facility().hasSlope()); + System.out.println("hasNoDoorStep = " + dto.facility().hasNoDoorStep()); + System.out.println("hasElevator = " + dto.facility().hasElevator()); + System.out.println("hasTableSeat = " + dto.facility().hasTableSeat()); + System.out.println("hasDisabledToilet = " + dto.facility().hasDisabledToilet()); + System.out.println("floorInfo = " + dto.facility().floorInfo()); + } else { + System.out.println("facility info is null"); + } } @Test @DisplayName("추천 결과 상위 N개 값 테스트") public void recommendWaybleZoneTop20() throws Exception { MvcResult result = mockMvc.perform(get(baseUrl) + .header("Authorization", "Bearer " + token) .param("userId", String.valueOf(userId)) .param("latitude", String.valueOf(LATITUDE)) .param("longitude", String.valueOf(LONGITUDE)) - .param("count", String.valueOf(20)) + .param("size", String.valueOf(20)) .accept(MediaType.APPLICATION_JSON) ) .andExpect(status().is2xxSuccessful()) @@ -373,4 +399,19 @@ private LocalDate generateRandomBirthDate() { return start.plusDays(randomDays); } + + private WaybleZoneFacility createRandomFacility(int i) { + Random random = new Random(i); // 시드 고정으로 재현 가능한 랜덤 + + String[] floors = {"B1", "1층", "2층", "3층"}; + + return WaybleZoneFacility.builder() + .hasSlope(random.nextBoolean()) + .hasNoDoorStep(random.nextBoolean()) + .hasElevator(random.nextBoolean()) + .hasTableSeat(random.nextBoolean()) + .hasDisabledToilet(random.nextBoolean()) + .floorInfo(floors[random.nextInt(floors.length)]) + .build(); + } } diff --git a/src/test/java/com/wayble/server/explore/WaybleZoneSearchApiIntegrationTest.java b/src/test/java/com/wayble/server/explore/WaybleZoneSearchApiIntegrationTest.java index ad299b58..5b38ba6e 100644 --- a/src/test/java/com/wayble/server/explore/WaybleZoneSearchApiIntegrationTest.java +++ b/src/test/java/com/wayble/server/explore/WaybleZoneSearchApiIntegrationTest.java @@ -3,11 +3,18 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.wayble.server.common.config.security.jwt.JwtTokenProvider; import com.wayble.server.common.entity.Address; import com.wayble.server.explore.dto.search.WaybleZoneDocumentRegisterDto; import com.wayble.server.explore.dto.search.WaybleZoneSearchResponseDto; import com.wayble.server.explore.entity.WaybleZoneDocument; import com.wayble.server.explore.repository.WaybleZoneDocumentRepository; +import com.wayble.server.user.entity.Gender; +import com.wayble.server.user.entity.LoginType; +import com.wayble.server.user.entity.User; +import com.wayble.server.user.entity.UserType; +import com.wayble.server.user.repository.UserRepository; +import com.wayble.server.wayblezone.entity.WaybleZoneFacility; import com.wayble.server.wayblezone.entity.WaybleZoneType; import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; @@ -18,7 +25,10 @@ import org.springframework.test.web.servlet.MvcResult; import java.nio.charset.StandardCharsets; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; import java.util.*; +import java.util.concurrent.ThreadLocalRandom; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.offset; @@ -36,6 +46,12 @@ public class WaybleZoneSearchApiIntegrationTest { @Autowired private WaybleZoneDocumentRepository waybleZoneDocumentRepository; + @Autowired + private UserRepository userRepository; + + @Autowired + private JwtTokenProvider jwtTokenProvider; + @Autowired private ObjectMapper objectMapper; @@ -47,6 +63,10 @@ public class WaybleZoneSearchApiIntegrationTest { private static final String baseUrl = "/api/v1/wayble-zones/search"; + private Long userId; + + private String token; + List nameList = new ArrayList<>(Arrays.asList( "던킨도너츠", "베스킨라빈스", @@ -62,6 +82,15 @@ public class WaybleZoneSearchApiIntegrationTest { @BeforeAll public void setup() { + User testUser = User.createUser( + "testUser", "testUsername", UUID.randomUUID() + "@email", "password", + generateRandomBirthDate(), Gender.MALE, LoginType.KAKAO, UserType.DISABLED + ); + + userRepository.save(testUser); + userId = testUser.getId(); + token = jwtTokenProvider.generateToken(userId, "ROLE_USER"); + for (int i = 1; i <= 1000; i++) { Map points = makeRandomPoint(); Address address = Address.builder() @@ -74,12 +103,15 @@ public void setup() { .longitude(points.get("longitude")) .build(); + WaybleZoneFacility facility = createRandomFacility(i); + WaybleZoneDocumentRegisterDto dto = WaybleZoneDocumentRegisterDto .builder() .zoneId((long) i) .zoneName(nameList.get((int) (Math.random() * nameList.size()))) .address(address) .waybleZoneType(WaybleZoneType.values()[i % WaybleZoneType.values().length]) + .facility(facility) .averageRating(Math.random() * 5) .reviewCount((long)(Math.random() * 500)) .build(); @@ -92,28 +124,28 @@ public void setup() { @AfterAll public void teardown() { waybleZoneDocumentRepository.deleteAll(); + userRepository.deleteAll(); } @Test public void checkDataExists() { List all = waybleZoneDocumentRepository.findAll(); - System.out.println("=== 저장된 데이터 확인 ==="); - System.out.println("Total documents: " + all.size()); + assertThat(all.size()).isGreaterThan(0); + System.out.println("Total documents: " + all.size()); for (WaybleZoneDocument doc : all) { assertThat(doc.getZoneId()).isNotNull(); assertThat(doc.getZoneName()).isNotNull(); assertThat(doc.getAddress().getLocation()).isNotNull(); - System.out.println("존 정보: " + doc.toString()); - System.out.println("주소: " + doc.getAddress().toString()); } } @Test @DisplayName("좌표를 전달받아 반경 이내의 웨이블 존을 거리 순으로 조회") public void findWaybleZoneByDistanceAscending() throws Exception{ - MvcResult result = mockMvc.perform(get(baseUrl) + MvcResult result = mockMvc.perform(get(baseUrl + "/maps") + .header("Authorization", "Bearer " + token) .param("latitude", String.valueOf(LATITUDE)) .param("longitude", String.valueOf(LONGITUDE)) .param("radiusKm", String.valueOf(RADIUS)) @@ -150,10 +182,33 @@ public void findWaybleZoneByDistanceAscending() throws Exception{ dto.distance(), dtoList.get(i-1).distance()) .isGreaterThanOrEqualTo(dtoList.get(i - 1).distance()); } + + // facility 검증 추가 + assertThat(dto.facility()).isNotNull(); + assertThat(dto.facility().hasSlope()).isNotNull(); + assertThat(dto.facility().hasNoDoorStep()).isNotNull(); + assertThat(dto.facility().hasElevator()).isNotNull(); + assertThat(dto.facility().hasTableSeat()).isNotNull(); + assertThat(dto.facility().hasDisabledToilet()).isNotNull(); + assertThat(dto.facility().floorInfo()).isNotNull(); } - for (WaybleZoneSearchResponseDto dto : dtoList) { - System.out.println(dto.toString()); + + if (!dtoList.isEmpty()) { + WaybleZoneSearchResponseDto firstDto = dtoList.get(0); + System.out.println("=== Search Result - Facility Info ==="); + System.out.println("zoneId = " + firstDto.zoneId()); + System.out.println("zoneName = " + firstDto.zoneName()); + if (firstDto.facility() != null) { + System.out.println("hasSlope = " + firstDto.facility().hasSlope()); + System.out.println("hasNoDoorStep = " + firstDto.facility().hasNoDoorStep()); + System.out.println("hasElevator = " + firstDto.facility().hasElevator()); + System.out.println("hasTableSeat = " + firstDto.facility().hasTableSeat()); + System.out.println("hasDisabledToilet = " + firstDto.facility().hasDisabledToilet()); + System.out.println("floorInfo = " + firstDto.facility().floorInfo()); + } else { + System.out.println("facility info is null"); + } } } @@ -161,7 +216,8 @@ public void findWaybleZoneByDistanceAscending() throws Exception{ @DisplayName("특정 단어가 포함된 웨이블존을 거리 순으로 반환") public void findWaybleZoneByNameAscending() throws Exception{ final String word = nameList.get((int) (Math.random() * nameList.size())).substring(0, 2); - MvcResult result = mockMvc.perform(get(baseUrl) + MvcResult result = mockMvc.perform(get(baseUrl + "/maps") + .header("Authorization", "Bearer " + token) .param("latitude", String.valueOf(LATITUDE)) .param("longitude", String.valueOf(LONGITUDE)) .param("radiusKm", String.valueOf(RADIUS)) @@ -214,7 +270,8 @@ public void findWaybleZoneByNameAscending() throws Exception{ @DisplayName("특정 타입의 웨이블존을 거리 순으로 반환") public void findWaybleZoneByZoneTypeAscending() throws Exception{ final WaybleZoneType zoneType = WaybleZoneType.CAFE; - MvcResult result = mockMvc.perform(get(baseUrl) + MvcResult result = mockMvc.perform(get(baseUrl + "/maps") + .header("Authorization", "Bearer " + token) .param("latitude", String.valueOf(LATITUDE)) .param("longitude", String.valueOf(LONGITUDE)) .param("radiusKm", String.valueOf(RADIUS)) @@ -268,7 +325,8 @@ public void findWaybleZoneByZoneTypeAscending() throws Exception{ public void findWaybleZoneByNameAndZoneTypeAscending() throws Exception{ final String word = nameList.get((int) (Math.random() * nameList.size())).substring(0, 2); final WaybleZoneType zoneType = WaybleZoneType.CAFE; - MvcResult result = mockMvc.perform(get(baseUrl) + MvcResult result = mockMvc.perform(get(baseUrl + "/maps") + .header("Authorization", "Bearer " + token) .param("latitude", String.valueOf(LATITUDE)) .param("longitude", String.valueOf(LONGITUDE)) .param("radiusKm", String.valueOf(RADIUS)) @@ -348,5 +406,31 @@ private Map makeRandomPoint() { return Map.of("latitude", randomLat, "longitude", randomLng); } + + private LocalDate generateRandomBirthDate() { + LocalDate today = LocalDate.now(); + LocalDate start = today.minusYears(90); // 90세 + LocalDate end = today.minusYears(10); // 10세 + + long daysBetween = ChronoUnit.DAYS.between(start, end); + long randomDays = ThreadLocalRandom.current().nextLong(daysBetween + 1); + + return start.plusDays(randomDays); + } + + private WaybleZoneFacility createRandomFacility(int i) { + Random random = new Random(i); // 시드 고정으로 재현 가능한 랜덤 + + String[] floors = {"B1", "1층", "2층", "3층"}; + + return WaybleZoneFacility.builder() + .hasSlope(random.nextBoolean()) + .hasNoDoorStep(random.nextBoolean()) + .hasElevator(random.nextBoolean()) + .hasTableSeat(random.nextBoolean()) + .hasDisabledToilet(random.nextBoolean()) + .floorInfo(floors[random.nextInt(floors.length)]) + .build(); + } }