diff --git a/.github/workflows/ci-test.yml b/.github/workflows/ci-test.yml index d06da19..44f1691 100644 --- a/.github/workflows/ci-test.yml +++ b/.github/workflows/ci-test.yml @@ -74,10 +74,18 @@ jobs: echo "${{ secrets.APPLICATION_TEST_YML }}" > ./src/test/resources/application-test.yml - # 1-4. gradle 환경 설치 - - name: Setup Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 - + # 1-4. 성능 향상을 위한 Gradle 캐싱 + - name: Gradle Caching + uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + gradle-${{ runner.os }} + + # 1-5. 테스트 컨테이너들 작동여부 체크 - name: MySQL 체크 run: | until nc -z localhost 3306; do @@ -100,11 +108,11 @@ jobs: sleep 3 done - # 1-5. 빌드 + # 2. 빌드 - name: Build with Gradle Wrapper run: ./gradlew clean build - ## 테스트 커버리지 + # 3. 테스트 커버리지 - name: Test Coverage Report id: jacoco uses: madrapps/jacoco-report@v1.2 @@ -116,7 +124,7 @@ jobs: min-coverage-overall: 0 debug-mode: true - # 7. JUnit 테스트 결과 게시 + # 4. JUnit 테스트 결과 게시 - name: Test 결과 출력 uses: EnricoMi/publish-unit-test-result-action@v2 if: always() diff --git a/src/main/java/com/zipte/platform/server/adapter/in/web/DashBoardApi.java b/src/main/java/com/zipte/platform/server/adapter/in/web/DashBoardApi.java new file mode 100644 index 0000000..cdae0b8 --- /dev/null +++ b/src/main/java/com/zipte/platform/server/adapter/in/web/DashBoardApi.java @@ -0,0 +1,98 @@ +package com.zipte.platform.server.adapter.in.web; + +import com.zipte.platform.core.response.ApiResponse; +import com.zipte.platform.security.oauth2.domain.PrincipalDetails; +import com.zipte.platform.server.adapter.in.web.dto.response.*; +import com.zipte.platform.server.adapter.in.web.swagger.DashBoardApiSpec; +import com.zipte.platform.server.application.in.dashboard.DashBoardUseCase; +import com.zipte.platform.server.domain.estate.Estate; +import com.zipte.platform.server.domain.estate.EstatePrice; +import com.zipte.platform.server.domain.region.Region; +import com.zipte.platform.server.domain.region.RegionPrice; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/api/v1/dashboard") +@RequiredArgsConstructor +public class DashBoardApi implements DashBoardApiSpec { + + private final DashBoardUseCase dashboardService; + + @GetMapping("/region") + public ApiResponse> getMyRegion(@AuthenticationPrincipal PrincipalDetails principalDetails) { + + /// 유저 아이디 추출하기 -> AOP로 한번에 처리하기? + Long userId = principalDetails.getId(); + + List region = dashboardService.getFavoriteRegion(userId); + + List responses = RegionResponse.from(region); + return ApiResponse.ok(responses); + } + + + @GetMapping("/region/price") + public ApiResponse> getMyRegionPrice(@AuthenticationPrincipal PrincipalDetails principalDetails) { + + /// 유저 아이디 추출하기 + Long userId = principalDetails.getId(); + + List regionPrices = dashboardService.getFavoriteRegionPrices(userId); + + List responses = RegionPriceResponse.from(regionPrices); + + return ApiResponse.ok(responses); + } + + + @GetMapping("/estate") + public ApiResponse> getMyEstate(@AuthenticationPrincipal PrincipalDetails principalDetails) { + + /// 유저 아이디 추출하기 + Long userId = principalDetails.getId(); + + List estates = dashboardService.getFavoriteEstates(userId); + + List responses = EstateDetailResponse.from(estates); + + return ApiResponse.ok(responses); + } + + @GetMapping("/estate/price") + public ApiResponse> getMyEstatePrice(@AuthenticationPrincipal PrincipalDetails principalDetails) { + + /// 유저 아이디 추출하기 + Long userId = principalDetails.getId(); + + /// 서비스 계층 추출 + List estatePrices = dashboardService.getFavoriteEstatePrice(userId); + + List responses = EstatePriceListResponse.from(estatePrices); + + return ApiResponse.ok(responses); + } + + @GetMapping("/estate/myhome") + public ApiResponse> getMyHomeEstate(@AuthenticationPrincipal PrincipalDetails principalDetails) { + + /// 유저 아이디 추출하기 + Long userId = principalDetails.getId(); + + /// 서비스 계층 추출 + List myEstatePrices = dashboardService.getMyEstatePrices(userId); + + /// + List responses = EstatePriceListResponse.from(myEstatePrices); + + return ApiResponse.ok(responses); + } + + + +} diff --git a/src/main/java/com/zipte/platform/server/adapter/in/web/dto/response/EstateDetailResponse.java b/src/main/java/com/zipte/platform/server/adapter/in/web/dto/response/EstateDetailResponse.java index 81ec7b5..f3babf6 100644 --- a/src/main/java/com/zipte/platform/server/adapter/in/web/dto/response/EstateDetailResponse.java +++ b/src/main/java/com/zipte/platform/server/adapter/in/web/dto/response/EstateDetailResponse.java @@ -42,5 +42,4 @@ public static List from(List estates) { .map(EstateDetailResponse::from) .toList(); } - } diff --git a/src/main/java/com/zipte/platform/server/adapter/in/web/dto/response/RegionPriceResponse.java b/src/main/java/com/zipte/platform/server/adapter/in/web/dto/response/RegionPriceResponse.java index 7ad1960..72e89f0 100644 --- a/src/main/java/com/zipte/platform/server/adapter/in/web/dto/response/RegionPriceResponse.java +++ b/src/main/java/com/zipte/platform/server/adapter/in/web/dto/response/RegionPriceResponse.java @@ -3,6 +3,8 @@ import com.zipte.platform.server.domain.region.RegionPrice; import lombok.Builder; +import java.util.*; + @Builder public record RegionPriceResponse( @JsonProperty("지역코드") @@ -20,7 +22,6 @@ public record RegionPriceResponse( @JsonProperty("25~30평") String between25and30, - @JsonProperty("30평 이상") String upper30 ) { @@ -35,8 +36,28 @@ public static RegionPriceResponse from(RegionPrice regionPrice) { .build(); } + + public static List from(List regionPrices) { + return regionPrices.stream() + .map(RegionPriceResponse::from) + .toList(); + } + + /// 내부 함수 private static String format(Double price) { if (price == null) return "-"; - return String.format("%.0f만원", price); + + long value = price.longValue(); // 만원 단위 정수화 + long billion = value / 10000; // 억 단위 + long rest = value % 10000; // 천만원 이하 + + if (billion > 0 && rest > 0) { + return String.format("%d억 %,d만원", billion, rest); + } else if (billion > 0) { + return String.format("%d억", billion); + } else { + return String.format("%,d만원", rest); + } } + } diff --git a/src/main/java/com/zipte/platform/server/adapter/in/web/swagger/DashBoardApiSpec.java b/src/main/java/com/zipte/platform/server/adapter/in/web/swagger/DashBoardApiSpec.java new file mode 100644 index 0000000..212088a --- /dev/null +++ b/src/main/java/com/zipte/platform/server/adapter/in/web/swagger/DashBoardApiSpec.java @@ -0,0 +1,59 @@ +package com.zipte.platform.server.adapter.in.web.swagger; + +import com.zipte.platform.core.response.ApiResponse; +import com.zipte.platform.security.oauth2.domain.PrincipalDetails; +import com.zipte.platform.server.adapter.in.web.dto.response.EstateDetailResponse; +import com.zipte.platform.server.adapter.in.web.dto.response.EstatePriceListResponse; +import com.zipte.platform.server.adapter.in.web.dto.response.RegionPriceResponse; +import com.zipte.platform.server.adapter.in.web.dto.response.RegionResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.core.annotation.AuthenticationPrincipal; + +import java.util.List; + +@Tag(name = "대시보드 API", description = "대시보드 관련 API") +public interface DashBoardApiSpec { + + @Operation( + summary = "나의 관심 지역 목록 조회", + description = "JWT 를 통해서 나의 관심 지역 목록을 조회합니다." + ) + ApiResponse> getMyRegion( + @AuthenticationPrincipal PrincipalDetails principalDetails); + + + @Operation( + summary = "나의 관심 지역 가격 조회", + description = "JWT 를 통해서 나의 관심 지역 가격을 조회합니다. (그래프 생성 데이터 전달)" + ) + ApiResponse> getMyRegionPrice( + @AuthenticationPrincipal PrincipalDetails principalDetails); + + + @Operation( + summary = "나의 관심 아파트 목록 조회", + description = "JWT 를 통해서 나의 관심 아파트 목록을 조회합니다." + ) + ApiResponse> getMyEstate( + @AuthenticationPrincipal PrincipalDetails principalDetails); + + + @Operation( + summary = "나의 관심 아파트 가격 조회", + description = "JWT 를 통해서 나의 관심 아파트 가격을 조회합니다. (그래프 생성 데이터 전달)" + ) + ApiResponse> getMyEstatePrice( + @AuthenticationPrincipal PrincipalDetails principalDetails); + + + + @Operation( + summary = "나의 아파트 조회", + description = "JWT 를 통해서 나의 거주 인증 아파트를 조회합니다. (그래프 생성 데이터 전달)" + ) + ApiResponse> getMyHomeEstate( + @AuthenticationPrincipal PrincipalDetails principalDetails); + + +} diff --git a/src/main/java/com/zipte/platform/server/adapter/out/EstateOwnerShipAdapter.java b/src/main/java/com/zipte/platform/server/adapter/out/EstateOwnerShipAdapter.java index 465454b..0ee01b7 100644 --- a/src/main/java/com/zipte/platform/server/adapter/out/EstateOwnerShipAdapter.java +++ b/src/main/java/com/zipte/platform/server/adapter/out/EstateOwnerShipAdapter.java @@ -7,6 +7,8 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; +import java.util.List; + @Component @RequiredArgsConstructor public class EstateOwnerShipAdapter implements EstateOwnerShipPort { @@ -33,4 +35,12 @@ public void deleteOwnership(Long userId, String kaptCode) { repository.deleteByKaptCodeAndUserId(kaptCode, userId); } + @Override + public List loadMyOwnerships(Long userId) { + + return repository.findByUserId(userId).stream() + .map(EstateOwnershipJpaEntity::toDomain) + .toList(); + } + } diff --git a/src/main/java/com/zipte/platform/server/adapter/out/EstatePersistenceAdapter.java b/src/main/java/com/zipte/platform/server/adapter/out/EstatePersistenceAdapter.java index a0f05b6..c48a963 100644 --- a/src/main/java/com/zipte/platform/server/adapter/out/EstatePersistenceAdapter.java +++ b/src/main/java/com/zipte/platform/server/adapter/out/EstatePersistenceAdapter.java @@ -53,4 +53,11 @@ public List loadEstatesByRegion(String region) { .map(EstateDocument::toDomain) .toList(); } + + @Override + public List loadEstatesByCodes(List kaptCodes) { + return repository.findByKaptCodeIn(kaptCodes).stream() + .map(EstateDocument::toDomain) + .toList(); + } } diff --git a/src/main/java/com/zipte/platform/server/adapter/out/EstatePricePersistenceAdapter.java b/src/main/java/com/zipte/platform/server/adapter/out/EstatePricePersistenceAdapter.java index 01803cd..fa4738c 100644 --- a/src/main/java/com/zipte/platform/server/adapter/out/EstatePricePersistenceAdapter.java +++ b/src/main/java/com/zipte/platform/server/adapter/out/EstatePricePersistenceAdapter.java @@ -40,4 +40,11 @@ public List loadEstatePriceByCodeAndArea(String kaptCode, double ex public List loadAllEstatePricesBetween(String kaptCode, String from, String to) { return List.of(); } + + @Override + public List loadEstatePricesByCodes(List kaptCodes) { + return repository.findByKaptCodeIn(kaptCodes).stream() + .map(EstatePriceDocument::toDomain) + .toList(); + } } diff --git a/src/main/java/com/zipte/platform/server/adapter/out/FavoritePersistenceAdapter.java b/src/main/java/com/zipte/platform/server/adapter/out/FavoritePersistenceAdapter.java index a4bb554..0de9f3e 100644 --- a/src/main/java/com/zipte/platform/server/adapter/out/FavoritePersistenceAdapter.java +++ b/src/main/java/com/zipte/platform/server/adapter/out/FavoritePersistenceAdapter.java @@ -55,6 +55,13 @@ public Page loadUserFavoriteByType(Long userId, FavoriteType type, Pag .map(FavoriteJpaEntity::toDomain); } + @Override + public List loadUserFavoriteByType(Long userId, FavoriteType type) { + return repository.findByUserIdAndType(userId, type).stream() + .map(FavoriteJpaEntity::toDomain) + .toList(); + } + @Override public boolean checkFavoriteByUserIdAndTypeAndCode(Long userId, FavoriteType type, String code) { if (type.equals(FavoriteType.REGION)) { diff --git a/src/main/java/com/zipte/platform/server/adapter/out/RegionPersistenceAdapter.java b/src/main/java/com/zipte/platform/server/adapter/out/RegionPersistenceAdapter.java index a9ac634..6f3f66f 100644 --- a/src/main/java/com/zipte/platform/server/adapter/out/RegionPersistenceAdapter.java +++ b/src/main/java/com/zipte/platform/server/adapter/out/RegionPersistenceAdapter.java @@ -41,6 +41,14 @@ public List loadChildRegionsByPrefix(String prefix, String suffix) { .toList(); } + + @Override + public List loadRegionsByCodes(List codes) { + return repository.findByCodeIn(codes).stream() + .map(RegionJpaEntity::toDomain) + .toList(); + } + @Override public boolean checkExistCode(String regionCode) { return repository.existsByCode(regionCode); diff --git a/src/main/java/com/zipte/platform/server/adapter/out/RegionPricePersistenceAdapter.java b/src/main/java/com/zipte/platform/server/adapter/out/RegionPricePersistenceAdapter.java index d54f100..5d8b03e 100644 --- a/src/main/java/com/zipte/platform/server/adapter/out/RegionPricePersistenceAdapter.java +++ b/src/main/java/com/zipte/platform/server/adapter/out/RegionPricePersistenceAdapter.java @@ -7,6 +7,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; +import java.util.List; import java.util.NoSuchElementException; import java.util.Optional; @@ -50,6 +51,13 @@ public boolean checkRegionPriceExist(String regionCode) { return repository.existsByRegionCode(regionCode); } + @Override + public List loadRegionPriceByCodes(List regionCodes) { + return repository.findByRegionCodeIn(regionCodes).stream() + .map(RegionPriceJpaEntity::toDomain) + .toList(); + } + @Override public void deleteRegionPriceByCode(String regionCode) { diff --git a/src/main/java/com/zipte/platform/server/adapter/out/jpa/estateOwnership/EstateOwnershipJpaRepository.java b/src/main/java/com/zipte/platform/server/adapter/out/jpa/estateOwnership/EstateOwnershipJpaRepository.java index 9306221..dc5d059 100644 --- a/src/main/java/com/zipte/platform/server/adapter/out/jpa/estateOwnership/EstateOwnershipJpaRepository.java +++ b/src/main/java/com/zipte/platform/server/adapter/out/jpa/estateOwnership/EstateOwnershipJpaRepository.java @@ -2,9 +2,15 @@ import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + public interface EstateOwnershipJpaRepository extends JpaRepository { boolean existsByKaptCodeAndUserId(String kaptCode, Long userId); void deleteByKaptCodeAndUserId(String kaptCode, Long userId); + + List findByUserId(Long userId); + + } diff --git a/src/main/java/com/zipte/platform/server/adapter/out/jpa/favorite/FavoriteJpaRepository.java b/src/main/java/com/zipte/platform/server/adapter/out/jpa/favorite/FavoriteJpaRepository.java index 66e26b0..8bd562f 100644 --- a/src/main/java/com/zipte/platform/server/adapter/out/jpa/favorite/FavoriteJpaRepository.java +++ b/src/main/java/com/zipte/platform/server/adapter/out/jpa/favorite/FavoriteJpaRepository.java @@ -15,6 +15,8 @@ public interface FavoriteJpaRepository extends JpaRepository findByUserIdAndType(Long userId, FavoriteType type, Pageable pageable); + List findByUserIdAndType(Long userId, FavoriteType type); + Optional findByIdAndUserId(Long id, Long userId); boolean existsByUserIdAndKaptCode(Long userId, String kaptCode); diff --git a/src/main/java/com/zipte/platform/server/adapter/out/jpa/region/RegionJpaRepository.java b/src/main/java/com/zipte/platform/server/adapter/out/jpa/region/RegionJpaRepository.java index 1be1356..38d762d 100644 --- a/src/main/java/com/zipte/platform/server/adapter/out/jpa/region/RegionJpaRepository.java +++ b/src/main/java/com/zipte/platform/server/adapter/out/jpa/region/RegionJpaRepository.java @@ -16,4 +16,6 @@ public interface RegionJpaRepository extends JpaRepository findByCodeIn(List codes); + } diff --git a/src/main/java/com/zipte/platform/server/adapter/out/jpa/region/RegionPriceJpaRepository.java b/src/main/java/com/zipte/platform/server/adapter/out/jpa/region/RegionPriceJpaRepository.java index babbd31..2303377 100644 --- a/src/main/java/com/zipte/platform/server/adapter/out/jpa/region/RegionPriceJpaRepository.java +++ b/src/main/java/com/zipte/platform/server/adapter/out/jpa/region/RegionPriceJpaRepository.java @@ -2,11 +2,13 @@ import org.springframework.data.jpa.repository.JpaRepository; -import java.util.Optional; +import java.util.*; public interface RegionPriceJpaRepository extends JpaRepository { Optional findByRegionCode(String regionCode); + List findByRegionCodeIn(List regionCodes); + boolean existsByRegionCode(String regionCode); } diff --git a/src/main/java/com/zipte/platform/server/adapter/out/mongo/estate/EstateMongoRepository.java b/src/main/java/com/zipte/platform/server/adapter/out/mongo/estate/EstateMongoRepository.java index 9bc97a7..5767362 100644 --- a/src/main/java/com/zipte/platform/server/adapter/out/mongo/estate/EstateMongoRepository.java +++ b/src/main/java/com/zipte/platform/server/adapter/out/mongo/estate/EstateMongoRepository.java @@ -29,6 +29,11 @@ public interface EstateMongoRepository extends MongoRepository findByKaptCodeIn(List kaptCodes); + /// 테스트용 void deleteByKaptCode(String kaptCode); + } diff --git a/src/main/java/com/zipte/platform/server/adapter/out/mongo/estate/EstatePriceMongoRepository.java b/src/main/java/com/zipte/platform/server/adapter/out/mongo/estate/EstatePriceMongoRepository.java index b8d9f44..64fbf27 100644 --- a/src/main/java/com/zipte/platform/server/adapter/out/mongo/estate/EstatePriceMongoRepository.java +++ b/src/main/java/com/zipte/platform/server/adapter/out/mongo/estate/EstatePriceMongoRepository.java @@ -13,4 +13,5 @@ public interface EstatePriceMongoRepository extends MongoRepository findFirstByKaptCodeOrderByTransactionDateDesc(String kaptCode); + List findByKaptCodeIn(List kaptCodes); } diff --git a/src/main/java/com/zipte/platform/server/application/in/dashboard/DashBoardUseCase.java b/src/main/java/com/zipte/platform/server/application/in/dashboard/DashBoardUseCase.java index 515123f..01cc731 100644 --- a/src/main/java/com/zipte/platform/server/application/in/dashboard/DashBoardUseCase.java +++ b/src/main/java/com/zipte/platform/server/application/in/dashboard/DashBoardUseCase.java @@ -1,4 +1,43 @@ package com.zipte.platform.server.application.in.dashboard; +import com.zipte.platform.server.domain.estate.Estate; +import com.zipte.platform.server.domain.estate.EstatePrice; +import com.zipte.platform.server.domain.region.Region; +import com.zipte.platform.server.domain.region.RegionPrice; + +import java.util.List; + public interface DashBoardUseCase { + + /* + 매매 / 관심 지역 관심 지역 - 실거래가 동향 관심 지역으로 설정한 지역의 가격 변화에 대한 알림을 받는다. + 매매 / 관심 아파트 관심 아파트 - 시세 변화 관심 아파트의 가격 변화에 대한 알림을 받는다. + 전체 / 맞춤형 아파트 제공 맞춤형 추천 매물 현황 추천시스템을 통해 관심 아파트로 설정한 유사 아파트를 제공한다. + 나의 아파트 가격 변화 / 나의 아파트 가격 변화 조회 / 개인정보에 등록한 나의 아파트의 가격 변화를 그래프로 확인한다. + */ + + + /// 관심 지역 조회하기 + List getFavoriteRegion(Long userId); + + /// 관심 지역 가격 조회하기 + List getFavoriteRegionPrices(Long userId); + + + /// 관심 아파트 목록 조회하기 + List getFavoriteEstates(Long userId); + + + /// 관심 아파트 가격 목록 조회하기 + List getFavoriteEstatePrice(Long userId); + + + /// AI 추천 목록 조회하기 + + + /// 주거지 인증을 한 나의 아파트 조회하기 + List getMyEstatePrices(Long userId); + + + } diff --git a/src/main/java/com/zipte/platform/server/application/out/estate/EstatePricePort.java b/src/main/java/com/zipte/platform/server/application/out/estate/EstatePricePort.java index c6cd300..7d29533 100644 --- a/src/main/java/com/zipte/platform/server/application/out/estate/EstatePricePort.java +++ b/src/main/java/com/zipte/platform/server/application/out/estate/EstatePricePort.java @@ -19,5 +19,6 @@ public interface EstatePricePort { /// 기간별 조회하기 List loadAllEstatePricesBetween(String kaptCode, String from, String to); - + /// 외부 의존성 + List loadEstatePricesByCodes(List kaptCodes); } diff --git a/src/main/java/com/zipte/platform/server/application/out/estate/LoadEstatePort.java b/src/main/java/com/zipte/platform/server/application/out/estate/LoadEstatePort.java index 484b979..c55063a 100644 --- a/src/main/java/com/zipte/platform/server/application/out/estate/LoadEstatePort.java +++ b/src/main/java/com/zipte/platform/server/application/out/estate/LoadEstatePort.java @@ -23,4 +23,5 @@ public interface LoadEstatePort { /// 외부에서 활용하는 포트 List loadEstatesByRegion(String region); + List loadEstatesByCodes(List kaptCodes); } diff --git a/src/main/java/com/zipte/platform/server/application/out/estateOwnership/EstateOwnerShipPort.java b/src/main/java/com/zipte/platform/server/application/out/estateOwnership/EstateOwnerShipPort.java index 0c4e71d..233e3d8 100644 --- a/src/main/java/com/zipte/platform/server/application/out/estateOwnership/EstateOwnerShipPort.java +++ b/src/main/java/com/zipte/platform/server/application/out/estateOwnership/EstateOwnerShipPort.java @@ -2,6 +2,8 @@ import com.zipte.platform.server.domain.estateOwnership.EstateOwnership; +import java.util.*; + public interface EstateOwnerShipPort { /// 저장하기 @@ -15,4 +17,7 @@ public interface EstateOwnerShipPort { void deleteOwnership(Long userId, String kaptCode); + /// 대시보드용 + List loadMyOwnerships(Long userId); + } diff --git a/src/main/java/com/zipte/platform/server/application/out/favorite/FavoritePort.java b/src/main/java/com/zipte/platform/server/application/out/favorite/FavoritePort.java index 9f239cf..68aba88 100644 --- a/src/main/java/com/zipte/platform/server/application/out/favorite/FavoritePort.java +++ b/src/main/java/com/zipte/platform/server/application/out/favorite/FavoritePort.java @@ -27,6 +27,8 @@ public interface FavoritePort { // 관심 목록 조회하기 Page loadUserFavoriteByType(Long userId, FavoriteType type, Pageable pageable); + List loadUserFavoriteByType(Long userId, FavoriteType type); + // 중복 여부 판단 조회 boolean checkFavoriteByUserIdAndTypeAndCode(Long userId, FavoriteType type, String code); diff --git a/src/main/java/com/zipte/platform/server/application/out/region/RegionPort.java b/src/main/java/com/zipte/platform/server/application/out/region/RegionPort.java index 2173ae2..a415461 100644 --- a/src/main/java/com/zipte/platform/server/application/out/region/RegionPort.java +++ b/src/main/java/com/zipte/platform/server/application/out/region/RegionPort.java @@ -16,6 +16,9 @@ public interface RegionPort { /// 존재 여부를 체크하는 boolean boolean checkExistCode(String regionCode); + /// 코드를 바탕으로 한번에 조회하기 + List loadRegionsByCodes(List codes); + /// 배치 처리 List loadAllRegions(); diff --git a/src/main/java/com/zipte/platform/server/application/out/region/RegionPricePort.java b/src/main/java/com/zipte/platform/server/application/out/region/RegionPricePort.java index 3d98f55..db76474 100644 --- a/src/main/java/com/zipte/platform/server/application/out/region/RegionPricePort.java +++ b/src/main/java/com/zipte/platform/server/application/out/region/RegionPricePort.java @@ -2,7 +2,7 @@ import com.zipte.platform.server.domain.region.RegionPrice; -import java.util.Optional; +import java.util.*; public interface RegionPricePort { @@ -17,6 +17,8 @@ public interface RegionPricePort { boolean checkRegionPriceExist(String regionCode); + List loadRegionPriceByCodes(List regionCodes); + /// 삭제 void deleteRegionPriceByCode(String regionCode); diff --git a/src/main/java/com/zipte/platform/server/application/service/DashBoardService.java b/src/main/java/com/zipte/platform/server/application/service/DashBoardService.java new file mode 100644 index 0000000..d3198e8 --- /dev/null +++ b/src/main/java/com/zipte/platform/server/application/service/DashBoardService.java @@ -0,0 +1,149 @@ +package com.zipte.platform.server.application.service; + +import com.zipte.platform.core.response.ErrorCode; +import com.zipte.platform.server.application.in.dashboard.DashBoardUseCase; +import com.zipte.platform.server.application.out.estate.EstatePricePort; +import com.zipte.platform.server.application.out.estate.LoadEstatePort; +import com.zipte.platform.server.application.out.estateOwnership.EstateOwnerShipPort; +import com.zipte.platform.server.application.out.favorite.FavoritePort; +import com.zipte.platform.server.application.out.region.RegionPort; +import com.zipte.platform.server.application.out.region.RegionPricePort; +import com.zipte.platform.server.application.out.user.UserPort; +import com.zipte.platform.server.domain.estate.Estate; +import com.zipte.platform.server.domain.estate.EstatePrice; +import com.zipte.platform.server.domain.estateOwnership.EstateOwnership; +import com.zipte.platform.server.domain.favorite.Favorite; +import com.zipte.platform.server.domain.favorite.FavoriteType; +import com.zipte.platform.server.domain.region.Region; +import com.zipte.platform.server.domain.region.RegionPrice; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.NoSuchElementException; + +@Slf4j +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class DashBoardService implements DashBoardUseCase { + + + private final UserPort userPort; + + private final FavoritePort favoritePort; + + private final RegionPort regionPort; + + private final RegionPricePort regionPricePort; + + private final LoadEstatePort estatePort; + + private final EstatePricePort estatePricePort; + + private final EstateOwnerShipPort estateOwnerShipPort; + + /// 나의 관심 지역 조회 + @Override + public List getFavoriteRegion(Long userId) { + + /// 유저 예외 처리 + checkUser(userId); + + /// 유저의 관심 지역 목록 조회하기 + List regionList = favoritePort.loadUserFavoriteByType(userId, FavoriteType.REGION); + + /// 관심 지역 목록을 바탕으로 조회하기 + List regionCodes = regionList.stream() + .map(Favorite::getRegionCode) + .toList(); + + return regionPort.loadRegionsByCodes(regionCodes); + } + + @Override + public List getFavoriteRegionPrices(Long userId) { + + /// 유저 예외 처리 + checkUser(userId); + + /// 유저의 관심 지역 목록 조회하기 + List regionList = favoritePort.loadUserFavoriteByType(userId, FavoriteType.REGION); + + /// 관심 지역 목록을 바탕으로 가격 조회하기 + List regionCodes = regionList.stream() + .map(Favorite::getRegionCode) + .toList(); + + /// 코드들을 바탕으로 조회하기 + return regionPricePort.loadRegionPriceByCodes(regionCodes); + } + + + /// 나의 관심 아파트 조회 + @Override + public List getFavoriteEstates(Long userId) { + + /// 유저 예외 처리 + checkUser(userId); + + /// 유저의 관심 목록 조회하기 + List estateList = favoritePort.loadUserFavoriteByType(userId, FavoriteType.APARTMENT); + + /// 유저 관련 아파트 조회 + List codes = estateList.stream() + .map(Favorite::getKaptCode) + .toList(); + + /// 코드들을 바탕으로 조회하기 + return estatePort.loadEstatesByCodes(codes); + } + + + @Override + public List getFavoriteEstatePrice(Long userId) { + + /// 유저 예외 처리 + checkUser(userId); + + /// 유저의 관심 목록 조회하기 + List regionList = favoritePort.loadUserFavoriteByType(userId, FavoriteType.APARTMENT); + + /// 관심 아파트 목록을 바탕으로 가격 조회하기 + List codes = regionList.stream() + .map(Favorite::getKaptCode) + .toList(); + + return estatePricePort.loadEstatePricesByCodes(codes); + } + + + /// 나의 소유 아파트 조회 + @Override + public List getMyEstatePrices(Long userId) { + + /// 유저 예외 처리 + checkUser(userId); + + /// 나의 주거지 인증 아파트 목록 조회 + List ownerships = estateOwnerShipPort.loadMyOwnerships(userId); + + List codes = ownerships.stream() + .map(EstateOwnership::getKaptCode) + .toList(); + + return estatePricePort.loadEstatePricesByCodes(codes); + } + + + /// 내부 함수 + private void checkUser(Long userId) { + if (!userPort.checkExistingById(userId)) { + throw new NoSuchElementException(ErrorCode.NOT_USER.getMessage()); + } + } + + +} diff --git a/src/test/java/com/zipte/platform/server/adapter/in/web/DashBoardApiTest.java b/src/test/java/com/zipte/platform/server/adapter/in/web/DashBoardApiTest.java new file mode 100644 index 0000000..e77828a --- /dev/null +++ b/src/test/java/com/zipte/platform/server/adapter/in/web/DashBoardApiTest.java @@ -0,0 +1,225 @@ +package com.zipte.platform.server.adapter.in.web; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.zipte.platform.core.response.ApiResponse; +import com.zipte.platform.security.annotation.WithMockCustomUser; +import com.zipte.platform.server.adapter.in.web.dto.response.EstateDetailResponse; +import com.zipte.platform.server.adapter.in.web.dto.response.EstatePriceListResponse; +import com.zipte.platform.server.adapter.in.web.dto.response.RegionPriceResponse; +import com.zipte.platform.server.adapter.in.web.dto.response.RegionResponse; +import com.zipte.platform.server.application.service.DashBoardService; +import com.zipte.platform.server.domain.estate.Estate; +import com.zipte.platform.server.domain.estate.EstateFixtures; +import com.zipte.platform.server.domain.estate.EstatePrice; +import com.zipte.platform.server.domain.estate.EstatePriceFixtures; +import com.zipte.platform.server.domain.region.Region; +import com.zipte.platform.server.domain.region.RegionFixtures; +import com.zipte.platform.server.domain.region.RegionPrice; +import com.zipte.platform.server.domain.region.RegionPriceFixtures; +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.BDDMockito.given; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.*; + +@SpringBootTest +@ActiveProfiles("test") +@AutoConfigureMockMvc(addFilters = false) +class DashBoardApiTest { + + @Autowired + private MockMvc mockMvc; + + @MockitoBean + private DashBoardService dashBoardService; + + private ObjectMapper objectMapper; + + @BeforeEach + void setUp() { + this.objectMapper = new ObjectMapper(); + objectMapper.registerModule(new JavaTimeModule()); + } + + @Nested + @DisplayName("지역 관련") + class RegionLoad { + + + @Test + @WithMockCustomUser + @DisplayName("[happy] 지역관련") + void happy_region() throws Exception { + + // Given + Long userId = 1L; + + Region region1 = RegionFixtures.분당구(); + Region region2 = RegionFixtures.종로구(); + List responses = List.of(region1, region2); + given(dashBoardService.getFavoriteRegion(userId)) + .willReturn(responses); + List responseList = RegionResponse.from(responses); + + ApiResponse> apiResponse = ApiResponse.ok(responseList); + + // When + ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders + .get("/api/v1/dashboard/region") + .contentType(MediaType.APPLICATION_JSON)); + + // Then + MvcResult mvcResult = resultActions + .andExpect(status().isOk()) + .andDo(print()) + .andReturn(); + + String responseBody = mvcResult.getResponse().getContentAsString(); + + assertEquals(responseBody, objectMapper.writeValueAsString(apiResponse)); + } + } + + + @Nested + @DisplayName("지역 가격 관련") + class RegionPriceLoad { + + @Test + @WithMockCustomUser + @DisplayName("[happy] 관심 지역 가격 조회") + void getFavoriteRegionPrices_success() throws Exception { + // Given + Long userId = 1L; + + String regionCode1 = "region1"; + String regionCode2 = "region2"; + RegionPrice stub1 = RegionPriceFixtures.stub(regionCode1); + RegionPrice stub2 = RegionPriceFixtures.stub(regionCode2); + var prices = List.of(stub1, stub2); + + given(dashBoardService.getFavoriteRegionPrices(userId)) + .willReturn(prices); + ApiResponse> expectedResponse = ApiResponse.ok(RegionPriceResponse.from(prices)); + + // When + ResultActions result = mockMvc.perform(MockMvcRequestBuilders + .get("/api/v1/dashboard/region/price") + .contentType(MediaType.APPLICATION_JSON)); + + // Then + String responseBody = result.andExpect(status().isOk()) + .andDo(print()) + .andReturn().getResponse().getContentAsString(); + + assertEquals(objectMapper.writeValueAsString(expectedResponse), responseBody); + } + } + + @Nested + @DisplayName("관심 아파트 관련") + class EstateLoad { + + @Test + @WithMockCustomUser + @DisplayName("[happy] 관심 아파트 조회") + void getFavoriteEstates_success() throws Exception { + // Given + Long userId = 1L; + Estate 테스트1 = EstateFixtures.stub("테스트1"); + Estate 테스트2 = EstateFixtures.stub("테스트2"); + + + List estates = List.of(테스트1, 테스트2); + given(dashBoardService.getFavoriteEstates(userId)) + .willReturn(estates); + + ApiResponse> expectedResponse = ApiResponse.ok(EstateDetailResponse.from(estates)); + + // When + ResultActions result = mockMvc.perform(MockMvcRequestBuilders + .get("/api/v1/dashboard/estate") + .contentType(MediaType.APPLICATION_JSON)); + + // Then + String responseBody = result.andExpect(status().isOk()) + .andDo(print()) + .andReturn().getResponse().getContentAsString(); + + assertEquals(objectMapper.writeValueAsString(expectedResponse), responseBody); + } + + @Test + @WithMockCustomUser + @DisplayName("[happy] 관심 아파트 가격 조회") + void getFavoriteEstatePrices_success() throws Exception { + // Given + Long userId = 1L; + EstatePrice stub1 = EstatePriceFixtures.stub("테스트1"); + EstatePrice stub2 = EstatePriceFixtures.stub("테스트2"); + List prices = List.of(stub1, stub2); + + given(dashBoardService.getFavoriteEstatePrice(userId)) + .willReturn(prices); + + ApiResponse> expectedResponse = ApiResponse.ok(EstatePriceListResponse.from(prices)); + + // When + ResultActions result = mockMvc.perform(MockMvcRequestBuilders + .get("/api/v1/dashboard/estate/price") + .contentType(MediaType.APPLICATION_JSON)); + + // Then + String responseBody = result.andExpect(status().isOk()) + .andDo(print()) + .andReturn().getResponse().getContentAsString(); + + assertEquals(objectMapper.writeValueAsString(expectedResponse), responseBody); + } + } + + @Nested + @DisplayName("내 아파트 관련") + class MyHome { + + @Test + @WithMockCustomUser + @DisplayName("[happy] 내 보유 아파트 가격 조회") + void getMyEstatePrices_success() throws Exception { + // Given + Long userId = 1L; + EstatePrice stub1 = EstatePriceFixtures.stub("테스트1"); + + List prices = List.of(stub1); + given(dashBoardService.getMyEstatePrices(userId)) + .willReturn(prices); + ApiResponse> expectedResponse = ApiResponse.ok(EstatePriceListResponse.from(prices)); + + // When + ResultActions result = mockMvc.perform(MockMvcRequestBuilders + .get("/api/v1/dashboard/estate/myhome") + .contentType(MediaType.APPLICATION_JSON)); + + // Then + String responseBody = result.andExpect(status().isOk()) + .andDo(print()) + .andReturn().getResponse().getContentAsString(); + + assertEquals(objectMapper.writeValueAsString(expectedResponse), responseBody); + } + } +} diff --git a/src/test/java/com/zipte/platform/server/application/service/DashBoardServiceTest.java b/src/test/java/com/zipte/platform/server/application/service/DashBoardServiceTest.java new file mode 100644 index 0000000..4a45ae0 --- /dev/null +++ b/src/test/java/com/zipte/platform/server/application/service/DashBoardServiceTest.java @@ -0,0 +1,264 @@ +package com.zipte.platform.server.application.service; + +import com.zipte.platform.server.application.out.estate.EstatePricePort; +import com.zipte.platform.server.application.out.estate.LoadEstatePort; +import com.zipte.platform.server.application.out.estateOwnership.EstateOwnerShipPort; +import com.zipte.platform.server.application.out.favorite.FavoritePort; +import com.zipte.platform.server.application.out.region.RegionPort; +import com.zipte.platform.server.application.out.region.RegionPricePort; +import com.zipte.platform.server.application.out.user.UserPort; +import com.zipte.platform.server.domain.FavoriteFixtures; +import com.zipte.platform.server.domain.estate.Estate; +import com.zipte.platform.server.domain.estate.EstatePrice; +import com.zipte.platform.server.domain.estateOwnership.EstateOwnership; +import com.zipte.platform.server.domain.favorite.Favorite; +import com.zipte.platform.server.domain.favorite.FavoriteType; +import com.zipte.platform.server.domain.region.Region; +import com.zipte.platform.server.domain.region.RegionFixtures; +import com.zipte.platform.server.domain.estate.EstateFixtures; +import com.zipte.platform.server.domain.review.EstateOwnerShipFixtures; +import com.zipte.platform.server.domain.estate.EstatePriceFixtures; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.NoSuchElementException; + +import static org.assertj.core.api.BDDAssertions.then; +import static org.mockito.BDDMockito.given; + +@ExtendWith(MockitoExtension.class) +class DashBoardServiceTest { + + @Mock + private UserPort userPort; + + @Mock + private FavoritePort favoritePort; + + @Mock + private RegionPort regionPort; + + @Mock + private RegionPricePort regionPricePort; + + @Mock + private LoadEstatePort loadEstatePort; + + @Mock + private EstatePricePort estatePricePort; + + @Mock + private EstateOwnerShipPort estateOwnerShipPort; + + @InjectMocks + private DashBoardService sut; + + + @Nested + @DisplayName("지역 관련 조회") + class GetRegion { + + @Test + @DisplayName("[happy] 나의 관심 지역 목록을 조회합니다.") + void getRegionList_happy() { + + // Given + Long userId = 1L; + + given(userPort.checkExistingById(userId)) + .willReturn(true); + + Favorite 테스트_지역1 = FavoriteFixtures.regionFavorite(userId, "분당구"); + Favorite 테스트_지역2 = FavoriteFixtures.regionFavorite(userId, "종로구"); + List favoriteList = List.of(테스트_지역1, 테스트_지역2); + + given(favoritePort.loadUserFavoriteByType(userId, FavoriteType.REGION)) + .willReturn(favoriteList); + + List codes = List.of(테스트_지역1.getRegionCode(), 테스트_지역2.getRegionCode()); + + Region 분당구 = RegionFixtures.분당구(); + Region 종로구 = RegionFixtures.종로구(); + List regionList = List.of(분당구, 종로구); + + given(regionPort.loadRegionsByCodes(codes)) + .willReturn(regionList); + + // When + List response = sut.getFavoriteRegion(userId); + + // Then + then(response) + .isNotNull() + .hasSize(2) + .containsExactlyInAnyOrder(분당구, 종로구); + } + + + @Test + @DisplayName("[happy] 나의 관심 지역 가격을 조회합니다.") + void getRegionPrice_happy() { + + // Given + Long userId = 1L; + + given(userPort.checkExistingById(userId)) + .willReturn(true); + + Favorite 테스트_지역1 = FavoriteFixtures.regionFavorite(userId, "분당구"); + Favorite 테스트_지역2 = FavoriteFixtures.regionFavorite(userId, "종로구"); + List favoriteList = List.of(테스트_지역1, 테스트_지역2); + + given(favoritePort.loadUserFavoriteByType(userId, FavoriteType.REGION)) + .willReturn(favoriteList); + + List codes = List.of(테스트_지역1.getRegionCode(), 테스트_지역2.getRegionCode()); + + Region 분당구 = RegionFixtures.분당구(); + Region 종로구 = RegionFixtures.종로구(); + List regionList = List.of(분당구, 종로구); + + given(regionPort.loadRegionsByCodes(codes)) + .willReturn(regionList); + + // When + List response = sut.getFavoriteRegion(userId); + + // Then + then(response) + .isNotNull() + .hasSize(2) + .containsExactlyInAnyOrder(분당구, 종로구); + } + + } + + @Nested + @DisplayName("아파트 관련 조회") + class EstateTest { + + @Test + @DisplayName("[happy] 나의 관심 아파트 목록을 조회합니다.") + void getFavoriteEstates_success() { + // Given + Long userId = 1L; + String kaptCode1 = "KaptCode1"; + String kaptCode2 = "KaptCode2"; + given(userPort.checkExistingById(userId)) + .willReturn(true); + + Favorite apt1 = FavoriteFixtures.apartmentFavorite(userId, kaptCode1); + Favorite apt2 = FavoriteFixtures.apartmentFavorite(userId, kaptCode2); + + given(favoritePort.loadUserFavoriteByType(userId, FavoriteType.APARTMENT)) + .willReturn(List.of(apt1, apt2)); + + Estate stub1 = EstateFixtures.stub(kaptCode1); + Estate stub2 = EstateFixtures.stub(kaptCode2); + + given(loadEstatePort.loadEstatesByCodes(List.of(kaptCode1, kaptCode2))) + .willReturn(List.of(stub1, stub2)); + + // When + var result = sut.getFavoriteEstates(userId); + + // Then + then(result) + .hasSize(2) + .containsExactlyInAnyOrder(stub1, stub2); + } + + @Test + @DisplayName("[happy] 나의 관심 아파트 가격을 조회합니다.") + void getFavoriteEstatePrices_success() { + // Given + Long userId = 1L; + given(userPort.checkExistingById(userId)) + .willReturn(true); + + String kaptCode1 = "KaptCode1"; + String kaptCode2 = "KaptCode2"; + Favorite apt1 = FavoriteFixtures.apartmentFavorite(userId, kaptCode1); + Favorite apt2 = FavoriteFixtures.apartmentFavorite(userId, kaptCode2); + + given(favoritePort.loadUserFavoriteByType(userId, FavoriteType.APARTMENT)) + .willReturn(List.of(apt1, apt2)); + + EstatePrice stub = EstatePriceFixtures.stub(kaptCode1); + EstatePrice stub2 = EstatePriceFixtures.stub(kaptCode2); + + given(estatePricePort.loadEstatePricesByCodes(List.of(kaptCode1, kaptCode2))) + .willReturn(List.of(stub, stub2)); + + // When + List result = sut.getFavoriteEstatePrice(userId); + + // Then + then(result).hasSize(2) + .containsExactlyInAnyOrder(stub, stub2); + } + } + + @Nested + @DisplayName("소유 아파트 조회") + class OwnershipTest { + + @Test + @DisplayName("[happy] 나의 소유 아파트 가격을 조회합니다.") + void getMyEstatePrices_success() { + // Given + Long userId = 1L; + String kaptCode1 = "KaptCode1"; + String kaptCode2 = "KaptCode2"; + + given(userPort.checkExistingById(userId)).willReturn(true); + + EstateOwnership 소유1 = EstateOwnerShipFixtures.stub(userId, kaptCode1, LocalDateTime.now()); + EstateOwnership 소유2 = EstateOwnerShipFixtures.stub(userId, kaptCode2, LocalDateTime.now()); + + given(estateOwnerShipPort.loadMyOwnerships(userId)) + .willReturn(List.of(소유1, 소유2)); + + EstatePrice stub = EstatePriceFixtures.stub(kaptCode1); + EstatePrice stub2 = EstatePriceFixtures.stub(kaptCode2); + + given(estatePricePort.loadEstatePricesByCodes(List.of(kaptCode1, kaptCode2))) + .willReturn(List.of(stub, stub2)); + + // When + List result = sut.getMyEstatePrices(userId); + + // Then + then(result).hasSize(2) + .containsExactlyInAnyOrder(stub, stub2); + } + } + + @Nested + @DisplayName("예외 처리") + class ExceptionTest { + + @Test + @DisplayName("[error] 존재하지 않는 유저일 경우 예외를 던집니다.") + void throwException_whenUserNotFound() { + // Given + Long userId = 1L; + given(userPort.checkExistingById(userId)) + .willReturn(false); + + // When / Then + org.junit.jupiter.api.Assertions.assertThrows( + NoSuchElementException.class, + () -> sut.getFavoriteRegion(userId) + ); + } + } + +} diff --git a/src/test/java/com/zipte/platform/server/application/service/EstateOwnershipServiceTest.java b/src/test/java/com/zipte/platform/server/application/service/EstateOwnershipServiceTest.java index 08d3a43..b762c39 100644 --- a/src/test/java/com/zipte/platform/server/application/service/EstateOwnershipServiceTest.java +++ b/src/test/java/com/zipte/platform/server/application/service/EstateOwnershipServiceTest.java @@ -8,7 +8,7 @@ import com.zipte.platform.server.application.service.exception.NotExistingEstateInYourAreaException; import com.zipte.platform.server.domain.estate.Estate; import com.zipte.platform.server.domain.estateOwnership.EstateOwnership; -import com.zipte.platform.server.domain.review.EstateFixtures; +import com.zipte.platform.server.domain.estate.EstateFixtures; import com.zipte.platform.server.domain.review.EstateOwnerShipFixtures; import org.junit.Assert; import org.junit.jupiter.api.DisplayName; @@ -85,13 +85,19 @@ public void badRequest_code() { public void createOwnerShipBy1KM() { // Given - var request = createRequest(00.00, 00.00); + var request = createRequest(37.12345, 127.123456); - given(loadUserPort.checkExistingById(anyLong())).willReturn(true); + given(loadUserPort.checkExistingById(anyLong())) + .willReturn(true); - given(loadEstatePort.checkExistingByCode(any())).willReturn(true); + given(loadEstatePort.checkExistingByCode(any())) + .willReturn(true); + + given(port.loadOwnershipByUser(request.getUserId(), request.getKaptCode())) + .willReturn(false); + + Estate stub = EstateFixtures.stub(request.getKaptCode()); - Estate stub = EstateFixtures.stub(); given(loadEstatePort.loadEstatesNearBy(request.getLongitude(), request.getLatitude(), ONE_KM_IN_RADIANS)) .willReturn(List.of(stub)); diff --git a/src/test/java/com/zipte/platform/server/domain/estate/EstateFixtures.java b/src/test/java/com/zipte/platform/server/domain/estate/EstateFixtures.java index 4d28e80..c2ba7f9 100644 --- a/src/test/java/com/zipte/platform/server/domain/estate/EstateFixtures.java +++ b/src/test/java/com/zipte/platform/server/domain/estate/EstateFixtures.java @@ -4,7 +4,8 @@ public class EstateFixtures { - public static Estate stub(){ + + public static Estate stub() { Location location = Location.builder() .coordinates(Arrays.asList( @@ -33,8 +34,41 @@ public static Estate stub(){ .subwayStation("test-강남역") .welfareFacility("test-주민센터, test-복지관") .build(); + } + public static Estate stub(String kaptCode) { + Location location = Location.builder() + .type("Point") + .coordinates(Arrays.asList( + 127.1280, // 경도 + 37.4116) // 위도 + ) + .build(); + + return Estate.builder() + .id("test-estate-id") + .kaptCode(kaptCode) + .pricePerSquareMeter("test-price-1500000") + .kaptAddr("test-경기도 성남시 분당구 테스트로 123") + .kaptMparea_135("test-area-135") + .kaptMparea_136("test-area-136") + .kaptMparea_60("test-area-60") + .kaptMparea_85("test-area-85") + .kaptName("테스트힐스테이트아파트") + .location(location) // test 위도/경도 + .convenientFacility("test-편의시설1, test-편의시설2") + .educationFacility("test-초등학교, test-중학교") + .kaptdPcnt("test-85.0") + .kaptdPcntu("test-82.3") + .kaptdWtimebus("test-버스 15분") + .kaptdWtimesub("test-지하철 10분") + .subwayLine("test-2호선") + .subwayStation("test-강남역") + .welfareFacility("test-주민센터, test-복지관") + .build(); } + + } diff --git a/src/test/java/com/zipte/platform/server/domain/estate/EstatePriceFixtures.java b/src/test/java/com/zipte/platform/server/domain/estate/EstatePriceFixtures.java new file mode 100644 index 0000000..68ca46e --- /dev/null +++ b/src/test/java/com/zipte/platform/server/domain/estate/EstatePriceFixtures.java @@ -0,0 +1,18 @@ +package com.zipte.platform.server.domain.estate; + +public class EstatePriceFixtures { + + + public static EstatePrice stub(String kaptCode) { + + return EstatePrice.builder() + .id("id") + .kaptCode(kaptCode) + .kaptName("kaptName") + .exclusiveArea(0.0) + .floor(1) + .price("1000000") + .build(); + } + +} diff --git a/src/test/java/com/zipte/platform/server/domain/region/RegionPriceFixtures.java b/src/test/java/com/zipte/platform/server/domain/region/RegionPriceFixtures.java new file mode 100644 index 0000000..10fc588 --- /dev/null +++ b/src/test/java/com/zipte/platform/server/domain/region/RegionPriceFixtures.java @@ -0,0 +1,20 @@ +package com.zipte.platform.server.domain.region; + +import java.util.HashMap; +import java.util.Map; + +public class RegionPriceFixtures { + + public static RegionPrice stub(String region) { + Map averagePrices = new HashMap<>(); + averagePrices.put("15평 이하", 80000.0); + averagePrices.put("15~20평", 100000.0); + averagePrices.put("20~25평", 120000.0); + averagePrices.put("25~30평", 140000.0); + averagePrices.put("30평 이상", 160000.0); + + return RegionPrice.of(region, averagePrices); // 예: 분당구 코드 + } + +} + diff --git a/src/test/java/com/zipte/platform/server/domain/review/EstateFixtures.java b/src/test/java/com/zipte/platform/server/domain/review/EstateFixtures.java deleted file mode 100644 index f96afad..0000000 --- a/src/test/java/com/zipte/platform/server/domain/review/EstateFixtures.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.zipte.platform.server.domain.review; - -import com.zipte.platform.server.domain.estate.Estate; -import com.zipte.platform.server.domain.estate.Location; - -import java.util.Arrays; - -public class EstateFixtures { - - public static Estate stub() { - - Location location = Location.builder() - .type("Point") - .coordinates(Arrays.asList( - 127.1280, // 경도 - 37.4116) // 위도 - ) - .build(); - - return Estate.builder() - .kaptCode("kaptCode") - .location(location) - .build(); - } - -}