diff --git a/src/main/java/com/pinHouse/server/platform/adapter/in/web/NoticeInfraApi.java b/src/main/java/com/pinHouse/server/platform/adapter/in/web/NoticeInfraApi.java new file mode 100644 index 0000000..0b687b0 --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/adapter/in/web/NoticeInfraApi.java @@ -0,0 +1,40 @@ +package com.pinHouse.server.platform.adapter.in.web; + +import com.pinHouse.server.core.response.response.ApiResponse; +import com.pinHouse.server.platform.adapter.in.web.dto.response.InfraDTO; +import com.pinHouse.server.platform.adapter.in.web.swagger.NoticeInfraApiSpec; +import com.pinHouse.server.platform.application.in.NoticeInfraUseCase; +import com.pinHouse.server.platform.domain.notice.NoticeInfra; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v1/notices/infra") +@RequiredArgsConstructor +public class NoticeInfraApi implements NoticeInfraApiSpec { + + /// 서비스 의존성 + private final NoticeInfraUseCase service; + + /// 주변 인프라 조회 + @GetMapping("/{noticeId}") + public ApiResponse showNotice( + @PathVariable String noticeId) { + + /// 서비스 계층 + NoticeInfra noticeInfra = service.getNoticeInfraById(noticeId); + + /// DTO 수정 + InfraDTO.NoticeInfraResponse response = InfraDTO.NoticeInfraResponse.from(noticeInfra); + + /// 응답 + return ApiResponse.ok(response); + } + + /// 원하는 인프라를 바탕으로 많이 존재하는 지역을 설정 + + +} diff --git a/src/main/java/com/pinHouse/server/platform/adapter/in/web/dto/response/InfraDTO.java b/src/main/java/com/pinHouse/server/platform/adapter/in/web/dto/response/InfraDTO.java new file mode 100644 index 0000000..ecc1f22 --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/adapter/in/web/dto/response/InfraDTO.java @@ -0,0 +1,185 @@ +package com.pinHouse.server.platform.adapter.in.web.dto.response; + +import com.pinHouse.server.platform.domain.facility.*; +import com.pinHouse.server.platform.domain.notice.NoticeInfra; +import io.micrometer.common.lang.Nullable; +import lombok.Builder; +import java.util.List; + +/** + * 인프라 관련 응답 DTO 클래스입니다. + */ +public record InfraDTO() { + + /** + * 도서관 정보 응답 객체입니다. + * + * @param libraryName 도서관 이름 + * @param address 도서관 주소 + */ + + @Builder + public record LibraryResponse( + String libraryName, + String address + ) { + + /// 정적 팩토리 메서드 + public static LibraryResponse from(Library library) { + return LibraryResponse.builder() + .libraryName(library.getName()) + .address(library.getAddress()) + .build(); + } + + /// 정적 팩토리 메서드 + public static List from(List libraries) { + return libraries.stream() + .map(LibraryResponse::from) + .toList(); + } + } + + /** + * 동물 관련 시설 응답 객체입니다. + * + * @param name 시설명 + * @param category 카테고리명 + */ + @Builder + public record AnimalResponse( + String name, + String category, + String address + ) { + /** 도메인 Animal → 응답 객체 변환 */ + public static AnimalResponse from(Animal animal) { + return AnimalResponse.builder() + .name(animal.getName()) + .category(animal.getCategory()) + .address(animal.getAddress()) + .build(); + } + + /** 도메인 List → 응답 목록 변환 */ + public static List from(List animals) { + return animals.stream() + .map(AnimalResponse::from) + .toList(); + } + } + + /** + * 스포츠 시설 응답 객체입니다. + * + * @param name 시설명 + * @param type 시설 유형명 + */ + @Builder + public record SportResponse( + String name, + String type, + String address + ) { + /** 도메인 Sport → 응답 객체 변환 */ + public static SportResponse from(Sport sport) { + return SportResponse.builder() + .name(sport.getName()) + .type(sport.getFacilityTypeName()) + .address(sport.getAddress()) + .build(); + } + + /** 도메인 List → 응답 목록 변환 */ + public static List from(List sports) { + return sports.stream() + .map(SportResponse::from) + .toList(); + } + } + + /** + * 공원 시설 응답 객체입니다. + * + * @param name 공원 명칭 + * @param category 분류명 + */ + @Builder + public record ParkResponse( + String name, + String category + ) { + /** 도메인 Park → 응답 객체 변환 */ + public static ParkResponse from(Park park) { + return ParkResponse.builder() + .name(park.getName()) + .category(park.getCategory()) + .build(); + } + + /** 도메인 List → 응답 목록 변환 */ + public static List from(List parks) { + return parks.stream() + .map(ParkResponse::from) + .toList(); + } + } + + /** + * 산책로 정보 응답 객체입니다. + * + * @param name 코스명 + * @param level 난이도 + */ + @Builder + public record WalkingResponse( + String name, + String level, + String address + ) { + /** 도메인 Walking → 응답 객체 변환 */ + public static WalkingResponse from(Walking walking) { + return WalkingResponse.builder() + .name(walking.getWalkingCourseName()) + .level(walking.getCourseLevelName()) + .address(walking.getAddress()) + .build(); + } + + /** 도메인 List → 응답 목록 변환 */ + public static List from(List walkings) { + return walkings.stream() + .map(WalkingResponse::from) + .toList(); + } + } + + /** + * 공지 인프라 통합 응답 객체입니다. + * + * @param libraries 도서관 응답 목록 + * @param animals 동물 응답 목록 + * @param sports 스포츠 응답 목록 + * @param parks 공원 응답 목록 + * @param walkings 산책로 응답 목록 + */ + @Builder + public record NoticeInfraResponse( + @Nullable List libraries, + @Nullable List animals, + @Nullable List sports, + @Nullable List parks, + @Nullable List walkings + ) { + /** NoticeInfra → 응답 DTO로 일괄 변환 */ + public static NoticeInfraResponse from(NoticeInfra noticeInfra) { + return NoticeInfraResponse.builder() + .libraries(LibraryResponse.from(noticeInfra.getLibraries())) + .animals(AnimalResponse.from(noticeInfra.getAnimals())) + .sports(SportResponse.from(noticeInfra.getSports())) + .parks(ParkResponse.from(noticeInfra.getParks())) + .walkings(WalkingResponse.from(noticeInfra.getWalkings())) + .build(); + } + } +} diff --git a/src/main/java/com/pinHouse/server/platform/adapter/in/web/swagger/NoticeInfraApiSpec.java b/src/main/java/com/pinHouse/server/platform/adapter/in/web/swagger/NoticeInfraApiSpec.java new file mode 100644 index 0000000..c66686a --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/adapter/in/web/swagger/NoticeInfraApiSpec.java @@ -0,0 +1,22 @@ +package com.pinHouse.server.platform.adapter.in.web.swagger; + +import com.pinHouse.server.core.response.response.ApiResponse; +import com.pinHouse.server.platform.adapter.in.web.dto.response.InfraDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +@Tag(name = "공고 주변 인프라 API", description = "인프라를 바탕으로 조회하는 API 입니다.") +public interface NoticeInfraApiSpec { + + @Operation( + summary = "공고 주변 인프라 조회 API", + description = "공고 주변 3KM내 존재하는 인프라를 확인하는 API입니다." + ) + @GetMapping("/{noticeId}") + ApiResponse showNotice( + @Parameter(example = "18407") + @PathVariable String noticeId); +} diff --git a/src/main/java/com/pinHouse/server/platform/adapter/out/FacilityMongoAdapter.java b/src/main/java/com/pinHouse/server/platform/adapter/out/FacilityMongoAdapter.java new file mode 100644 index 0000000..4761c0f --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/adapter/out/FacilityMongoAdapter.java @@ -0,0 +1,56 @@ +package com.pinHouse.server.platform.adapter.out; + +import com.pinHouse.server.platform.adapter.out.mongo.facility.*; +import com.pinHouse.server.platform.application.out.facility.FacilityPort; +import com.pinHouse.server.platform.domain.facility.*; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +@RequiredArgsConstructor +public class FacilityMongoAdapter implements FacilityPort { + + /// 외부 의존성 + private final LibraryDocumentRepository libraryRepository; + private final ParkDocumentRepository parkRepository; + private final SportDocumentRepository sportRepository; + private final WalkingDocumentRepository walkingRepository; + private final AnimalDocumentRepository animalRepository; + + @Override + public List loadLibrariesNearBy(double longitude, double latitude, double radiusInKm) { + return libraryRepository.findByLocation(longitude, latitude,radiusInKm).stream() + .map(LibraryDocument::toDomain) + .toList(); + } + + @Override + public List loadParksNearBy(double longitude, double latitude, double radiusInKm) { + return parkRepository.findByLocation(longitude, latitude,radiusInKm).stream() + .map(ParkDocument::toDomain) + .toList(); + } + + @Override + public List loadSportsNearBy(double longitude, double latitude, double radiusInKm) { + return sportRepository.findByLocation(longitude, latitude,radiusInKm).stream() + .map(SportDocument::toDomain) + .toList(); + } + + @Override + public List loadWalkingsNearBy(double longitude, double latitude, double radiusInKm) { + return walkingRepository.findByLocation(longitude, latitude,radiusInKm).stream() + .map(WalkingDocument::toDomain) + .toList(); + } + + @Override + public List loadAnimalsNearBy(double longitude, double latitude, double radiusInKm) { + return animalRepository.findByLocation(longitude, latitude,radiusInKm).stream() + .map(AnimalDocument::toDomain) + .toList(); + } +} diff --git a/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/AnimalDocument.java b/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/AnimalDocument.java new file mode 100644 index 0000000..9579862 --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/AnimalDocument.java @@ -0,0 +1,103 @@ +package com.pinHouse.server.platform.adapter.out.mongo.facility; + +import com.pinHouse.server.platform.domain.facility.Animal; +import com.pinHouse.server.platform.domain.location.Location; +import jakarta.persistence.Id; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; +import org.springframework.data.mongodb.core.index.GeoSpatialIndexed; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.Field; + +@Document(collection = "animal") +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AnimalDocument { + + /** MongoDB ObjectId */ + @Id + @Field("_id") + private String id; + + /** 시설명 */ + @Field("FCLTY_NM") + private String facilityName; + + /** 3차 카테고리명 (ex. 펜션) */ + @Field("CTGRY_THREE_NM") + private String categoryThreeName; + + @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) + private Location location; + + /** 우편번호 */ + @Field("ZIP_NO") + private Integer zipNo; + + /** 도로명주소 */ + @Field("RDNMADR_NM") + private String roadAddressName; + + /** 휴무/운영 안내 */ + @Field("RSTDE_GUID_CN") + private String restGuide; + + /** 운영시간 */ + @Field("OPER_TIME") + private String operateTime; + + /** 반려동물 동반 가능 여부 */ + @Field("PET_POSBL_AT") + private String petPossibleAt; + + /** 입장 가능 반려동물 크기 (ex. 7kg 미만) */ + @Field("ENTRN_POSBL_PET_SIZE_VALUE") + private String enterPossiblePetSizeValue; + + /** 반려동물 제한사항 */ + @Field("PET_LMTT_MTR_CN") + private String petLimitMatterContent; + + /** 실내 입장 가능 여부 */ + @Field("IN_PLACE_ACP_POSBL_AT") + private String inPlaceAcceptPossibleAt; + + /** 실외 입장 가능 여부 */ + @Field("OUT_PLACE_ACP_POSBL_AT") + private String outPlaceAcceptPossibleAt; + + /** 시설 안내/특징 */ + @Field("FCLTY_INFO_DC") + private String facilityInfoDescription; + + /** 반려동물 추가 요금 안내 */ + @Field("PET_ACP_ADIT_CHRGE_VALUE") + private String petAcceptAdditionalChargeValue; + + /// 도메인 변환 + public Animal toDomain(){ + return Animal.builder() + .name(facilityName) + .category(categoryThreeName) + .location(Location.builder() + .type(location.getType()) + .coordinates(location.getCoordinates()) + .build()) + .address(roadAddressName) + .restGuide(restGuide) + .operateTime(operateTime) + .petPossibleAt(petPossibleAt) + .enterPossiblePetSizeValue(enterPossiblePetSizeValue) + .petLimitMatterContent(petLimitMatterContent) + .inPlaceAcceptPossibleAt(inPlaceAcceptPossibleAt) + .outPlaceAcceptPossibleAt(outPlaceAcceptPossibleAt) + .facilityInfoDescription(facilityInfoDescription) + .build(); + } + +} diff --git a/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/AnimalDocumentRepository.java b/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/AnimalDocumentRepository.java new file mode 100644 index 0000000..5d7d842 --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/AnimalDocumentRepository.java @@ -0,0 +1,14 @@ +package com.pinHouse.server.platform.adapter.out.mongo.facility; + +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.data.mongodb.repository.Query; + +import java.util.List; + +public interface AnimalDocumentRepository extends MongoRepository { + + // 좌표 주변의 특정 반경이상 주소 검색 + @Query(value = "{ 'location': { $geoWithin: { $centerSphere: [ [ ?0, ?1 ], ?2 ] } } }") + List findByLocation(double longitude, double latitude, double radiusInRadians); + +} diff --git a/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/LibraryDocument.java b/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/LibraryDocument.java new file mode 100644 index 0000000..ef44969 --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/LibraryDocument.java @@ -0,0 +1,104 @@ +package com.pinHouse.server.platform.adapter.out.mongo.facility; + +import com.pinHouse.server.platform.domain.facility.Library; +import com.pinHouse.server.platform.domain.location.Location; +import jakarta.persistence.Id; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; +import org.springframework.data.mongodb.core.index.GeoSpatialIndexed; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.Field; + +/** + * MongoDB library document. + */ +@Document(collection = "library") +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class LibraryDocument { + + /** + * MongoDB unique identifier (_id) + */ + @Id + @Field("_id") + private String id; + + /** + * Library code (도서관코드) + */ + @Field("LBRRY_CD") + private Integer libraryCode; + + /** + * Library name (도서관명) + */ + @Field("LBRRY_NM") + private String libraryName; + + /** + * Library address (도서관주소) + */ + @Field("LBRRY_ADDR") + private String libraryAddress; + + @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) + private Location location; + + /** + * Province/State name (광역지역명) + */ + @Field("ONE_AREA_NM") + private String regionName; + + /** + * City/District name (시군구명) + */ + @Field("TWO_AREA_NM") + private String cityName; + + /** + * Library number (도서관번호) + */ + @Field("LBRRY_NO") + private Integer libraryNumber; + + /** + * Opening hours (운영시간) + */ + @Field("OPNNG_TIME") + private String openingTime; + + /** + * Closed days (휴관일) + */ + @Field("CLOSEDON_DC") + private String closedDays; + + /** + * Converts this document to the domain model. + * @return Library domain object + */ + public Library toDomain() { + return Library.builder() + .id(id) + .code(libraryCode) + .name(libraryName) + .address(libraryAddress) + .location(Location.builder() + .type(location.getType()) + .coordinates(location.getCoordinates()) + .build()) + .area(regionName) + .city(cityName) + .number(libraryNumber) + .openTime(openingTime) + .closedDay(closedDays) + .build(); + } +} diff --git a/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/LibraryDocumentRepository.java b/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/LibraryDocumentRepository.java new file mode 100644 index 0000000..7e25140 --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/LibraryDocumentRepository.java @@ -0,0 +1,14 @@ +package com.pinHouse.server.platform.adapter.out.mongo.facility; + +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.data.mongodb.repository.Query; + +import java.util.List; + +public interface LibraryDocumentRepository extends MongoRepository { + + // 좌표 주변의 특정 반경이상 주소 검색 + @Query(value = "{ 'location': { $geoWithin: { $centerSphere: [ [ ?0, ?1 ], ?2 ] } } }") + List findByLocation(double longitude, double latitude, double radiusInRadians); + +} diff --git a/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/ParkDocument.java b/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/ParkDocument.java new file mode 100644 index 0000000..a81cbe8 --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/ParkDocument.java @@ -0,0 +1,64 @@ +package com.pinHouse.server.platform.adapter.out.mongo.facility; + +import com.pinHouse.server.platform.domain.facility.Park; +import com.pinHouse.server.platform.domain.location.Location; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; +import org.springframework.data.mongodb.core.index.GeoSpatialIndexed; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.Field; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * MongoDB 공원 도큐멘트 + */ + +@Document(collection = "park") +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ParkDocument { + + @Id + @Field("_id") + private String id; + + /** 공원 시스템 ID */ + @Field("ID") + private String parkId; + + /** POI 명칭 */ + @Field("POI_NM") + private String poiName; + + /** 공원 분류 명칭 */ + @Field("CL_NM") + private String categoryName; + + /** PNU (법정동+지번코드) */ + @Field("PNU") + private String pnu; + + @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) + private Location location; + + /// 도메인 변환 + public Park toDomain() { + return Park.builder() + .id(id) + .parkId(parkId) + .name(poiName) + .category(categoryName) + .pnu(pnu) + .location(Location.builder() + .type(location.getType()) + .coordinates(location.getCoordinates()) + .build()) + .build(); + } + +} diff --git a/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/ParkDocumentRepository.java b/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/ParkDocumentRepository.java new file mode 100644 index 0000000..a831aa7 --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/ParkDocumentRepository.java @@ -0,0 +1,14 @@ +package com.pinHouse.server.platform.adapter.out.mongo.facility; + +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.data.mongodb.repository.Query; + +import java.util.List; + +public interface ParkDocumentRepository extends MongoRepository { + + // 좌표 주변의 특정 반경이상 주소 검색 + @Query(value = "{ 'location': { $geoWithin: { $centerSphere: [ [ ?0, ?1 ], ?2 ] } } }") + List findByLocation(double longitude, double latitude, double radiusInRadians); + +} diff --git a/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/SportDocument.java b/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/SportDocument.java new file mode 100644 index 0000000..3a98c5b --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/SportDocument.java @@ -0,0 +1,86 @@ +package com.pinHouse.server.platform.adapter.out.mongo.facility; + +import com.pinHouse.server.platform.domain.facility.Sport; +import com.pinHouse.server.platform.domain.location.Location; +import jakarta.persistence.Id; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; +import org.springframework.data.mongodb.core.index.GeoSpatialIndexed; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.Field; + +@Document(collection = "sports") +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class SportDocument { + + /** MongoDB ObjectId */ + @Id + @Field("_id") + private String id; + + /** 시설 명칭 */ + @Field("FCLTY_NM") + private String facilityName; + + /** 업종 명칭 */ + @Field("INDUTY_NM") + private String industryName; + + /** 시설 유형 명칭 */ + @Field("FCLTY_TY_NM") + private String facilityTypeName; + + /** 시설 상태값 (예: 정상운영 등) */ + @Field("FCLTY_STATE_VALUE") + private String facilityStateValue; + + /** 도로명주소-1 */ + @Field("RDNMADR_ONE_NM") + private String roadAddressOneName; + + /** 도로명주소-2 */ + @Field("RDNMADR_TWO_NM") + private String roadAddressTwoName; + + @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) + private Location location; + + /** 시설 운영 형태 값 */ + @Field("FCLTY_OPER_STLE_VALUE") + private String facilityOperStyleValue; + + /** 시설 기준일자(생성 기준일) */ + @Field("FCLTY_CRTN_STDR_DE") + private String facilityCreationStandardDate; + + /** 국가 공공 시설 여부 */ + @Field("NATION_ALSFC_AT") + private String nationPublicFacilityAt; + + + /// toDomain + public Sport toDomain() { + return Sport.builder() + .id(id) + .name(facilityName) + .industryName(industryName) + .facilityTypeName(facilityTypeName) + .facilityStateValue(facilityStateValue) + .address(roadAddressOneName) + .location(Location.builder() + .type(location.getType()) + .coordinates(location.getCoordinates()) + .build()) + .facilityOperStyleValue(facilityOperStyleValue) + .facilityCreationStandardDate(facilityCreationStandardDate) + .nationPublicFacilityAt(nationPublicFacilityAt) + .build(); + + } +} diff --git a/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/SportDocumentRepository.java b/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/SportDocumentRepository.java new file mode 100644 index 0000000..42cce39 --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/SportDocumentRepository.java @@ -0,0 +1,14 @@ +package com.pinHouse.server.platform.adapter.out.mongo.facility; + +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.data.mongodb.repository.Query; + +import java.util.List; + +public interface SportDocumentRepository extends MongoRepository { + + // 좌표 주변의 특정 반경이상 주소 검색 + @Query(value = "{ 'location': { $geoWithin: { $centerSphere: [ [ ?0, ?1 ], ?2 ] } } }") + List findByLocation(double longitude, double latitude, double radiusInRadians); + +} diff --git a/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/WalkingDocument.java b/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/WalkingDocument.java new file mode 100644 index 0000000..f91a679 --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/WalkingDocument.java @@ -0,0 +1,114 @@ +package com.pinHouse.server.platform.adapter.out.mongo.facility; + +import com.pinHouse.server.platform.domain.facility.Walking; +import com.pinHouse.server.platform.domain.location.Location; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; +import org.springframework.data.mongodb.core.index.GeoSpatialIndexed; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.Field; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + + +@Document(collection = "walking") +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class WalkingDocument { + + /** MongoDB ObjectId */ + @Id + @Field("_id") + private String id; + + /** 코스 고유 식별자 */ + @Field("ESNTL_ID") + private String esntlId; + + /** 걷기 코스 플래그명 (코스 대표 명칭) */ + @Field("WLK_COURS_FLAG_NM") + private String walkingCourseFlagName; + + /** 걷기 코스명 */ + @Field("WLK_COURS_NM") + private String walkingCourseName; + + /** 코스 상세설명 */ + @Field("COURS_DC") + private String courseDescription; + + /** 행정구역명(시/군/구) */ + @Field("SIGNGU_NM") + private String districtName; + + /** 코스 난이도 */ + @Field("COURS_LEVEL_NM") + private String courseLevelName; + + /** 코스 거리 구간명 (예:10~15㎞미만) */ + @Field("COURS_LT_CN") + private String courseLengthDescription; + + /** 상세 거리 (단위: km, 실수 값) */ + @Field("COURS_DETAIL_LT_CN") + private Double courseDetailLengthKm; + + /** 추가 설명 (코스 특성, 지역 건강증진 등) */ + @Field("ADIT_DC") + private String additionalDescription; + + /** 예상 소요 시간 (예: 4시간) */ + @Field("COURS_TIME_CN") + private String courseTime; + + /** 편의시설 및 옵션 안내 */ + @Field("OPTN_DC") + private String optionDescription; + + /** 화장실 안내 */ + @Field("TOILET_DC") + private String toiletDescription; + + /** 주변 편의점/휴게시설 안내 */ + @Field("CVNTL_NM") + private String convenienceName; + + /** 코스 시작/대표 주소 */ + @Field("LNM_ADDR") + private String address; + + @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) + private Location location; + + /// toDomain + public Walking toDomain() { + return Walking.builder() + .id(id) + .esntlId(esntlId) + .walkingCourseFlagName(walkingCourseFlagName) + .walkingCourseName(walkingCourseName) + .courseDescription(courseDescription) + .districtName(districtName) + .courseLevelName(courseLevelName) + .courseLengthDescription(courseLengthDescription) + .courseDetailLengthKm(courseDetailLengthKm) + .courseLevelName(courseLevelName) + .courseLengthDescription(courseLengthDescription) + .courseDetailLengthKm(courseDetailLengthKm) + .courseTime(courseTime) + .optionDescription(optionDescription) + .toiletDescription(toiletDescription) + .convenienceName(convenienceName) + .address(address) + .location(Location.builder() + .type(location.getType()) + .coordinates(location.getCoordinates()) + .build()) + .build(); + } + +} diff --git a/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/WalkingDocumentRepository.java b/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/WalkingDocumentRepository.java new file mode 100644 index 0000000..f795f21 --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/adapter/out/mongo/facility/WalkingDocumentRepository.java @@ -0,0 +1,13 @@ +package com.pinHouse.server.platform.adapter.out.mongo.facility; + +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.data.mongodb.repository.Query; + +import java.util.List; + +public interface WalkingDocumentRepository extends MongoRepository { + + // 좌표 주변의 특정 반경이상 주소 검색 + @Query(value = "{ 'location': { $geoWithin: { $centerSphere: [ [ ?0, ?1 ], ?2 ] } } }") + List findByLocation(double longitude, double latitude, double radiusInRadians); +} diff --git a/src/main/java/com/pinHouse/server/platform/application/in/NoticeInfraUseCase.java b/src/main/java/com/pinHouse/server/platform/application/in/NoticeInfraUseCase.java new file mode 100644 index 0000000..188a4b3 --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/application/in/NoticeInfraUseCase.java @@ -0,0 +1,17 @@ +package com.pinHouse.server.platform.application.in; + +import com.pinHouse.server.platform.domain.notice.Notice; +import com.pinHouse.server.platform.domain.notice.NoticeInfra; + +/** + * - 공고 주변의 인프라 목록 조회할 인터페이스 + */ +public interface NoticeInfraUseCase { + + /// 조회 + // 주변의 인프라 개수 조회 + NoticeInfra getNoticeInfraById(String noticeId); + + // 원하는 인프라 바탕으로 많이 존재하는 공고 조회 + Notice getNoticeByInfra(); +} diff --git a/src/main/java/com/pinHouse/server/platform/application/out/facility/FacilityPort.java b/src/main/java/com/pinHouse/server/platform/application/out/facility/FacilityPort.java new file mode 100644 index 0000000..c12281f --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/application/out/facility/FacilityPort.java @@ -0,0 +1,18 @@ +package com.pinHouse.server.platform.application.out.facility; + +import com.pinHouse.server.platform.domain.facility.*; + +import java.util.List; + +public interface FacilityPort { + + List loadLibrariesNearBy(double longitude, double latitude, double radiusInKm); + + List loadParksNearBy(double longitude, double latitude, double radiusInKm); + + List loadSportsNearBy(double longitude, double latitude, double radiusInKm); + + List loadWalkingsNearBy(double longitude, double latitude, double radiusInKm); + + List loadAnimalsNearBy(double longitude, double latitude, double radiusInKm); +} diff --git a/src/main/java/com/pinHouse/server/platform/application/service/NoticeInfraService.java b/src/main/java/com/pinHouse/server/platform/application/service/NoticeInfraService.java new file mode 100644 index 0000000..7d63218 --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/application/service/NoticeInfraService.java @@ -0,0 +1,103 @@ +package com.pinHouse.server.platform.application.service; + +import com.pinHouse.server.core.response.response.ErrorCode; +import com.pinHouse.server.platform.application.in.NoticeInfraUseCase; +import com.pinHouse.server.platform.application.out.facility.FacilityPort; +import com.pinHouse.server.platform.application.out.notice.NoticePort; +import com.pinHouse.server.platform.domain.facility.*; +import com.pinHouse.server.platform.domain.notice.Notice; +import com.pinHouse.server.platform.domain.notice.NoticeInfra; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.NoSuchElementException; + +@Service +@Transactional +@RequiredArgsConstructor +public class NoticeInfraService implements NoticeInfraUseCase { + + /// 공고 의존성 + private final NoticePort noticePort; + + /// 인프라 의존성 + private final FacilityPort facilityPort; + + /// 상수 + private final double radiusKm = 1.5; + private final double radiusInRadians = radiusKm / 6371.0; + + // ================= + // 주변 인프라 조회 + // ================= + + /// 주변의 인프라 개수 조회 + @Override + public NoticeInfra getNoticeInfraById(String noticeId) { + + /// 예외 처리 + Notice notice = getNotice(noticeId); + + /// 주변에 존재하는 도서관 가져오기 + List libraries = facilityPort.loadLibrariesNearBy( + notice.getLocation().getLongitude(), + notice.getLocation().getLatitude(), + radiusInRadians); + + /// 주변에 존재하는 동물 관련 시설 가져오기 + List animals = facilityPort.loadAnimalsNearBy( + notice.getLocation().getLongitude(), + notice.getLocation().getLatitude(), + radiusInRadians); + + + /// 주변에 존재하는 공원 정보 시설 가져오기 + List parks = facilityPort.loadParksNearBy( + notice.getLocation().getLongitude(), + notice.getLocation().getLatitude(), + radiusInRadians); + + + /// 주변에 존재하는 산책로 관련 시설 가져오기 + List walkings = facilityPort.loadWalkingsNearBy( + notice.getLocation().getLongitude(), + notice.getLocation().getLatitude(), + radiusInRadians); + + + /// 주변에 존재하는 스포츠 정보 시설 가져오기 + List sports = facilityPort.loadSportsNearBy( + notice.getLocation().getLongitude(), + notice.getLocation().getLatitude(), + radiusInRadians); + + + /// 객체 생성 + return NoticeInfra.of(notice, libraries, animals, sports, walkings, parks); + } + + /// + @Override + public Notice getNoticeByInfra() { + return null; + } + + // ================= + // 인프라 바탕으로 공고 조회 + // ================= + + + + + // ================= + // 내부 함수 + // ================= + + private Notice getNotice(String noticeId) { + return noticePort.loadById(noticeId) + .orElseThrow(() -> new NoSuchElementException(ErrorCode.NOT_NOTICE.getMessage())); + } + +} diff --git a/src/main/java/com/pinHouse/server/platform/domain/facility/Animal.java b/src/main/java/com/pinHouse/server/platform/domain/facility/Animal.java new file mode 100644 index 0000000..d6fccdf --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/domain/facility/Animal.java @@ -0,0 +1,54 @@ +package com.pinHouse.server.platform.domain.facility; + +import com.pinHouse.server.platform.domain.location.Location; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class Animal { + + /// 아이디 + private String id; + + /// 시설명 + private String name; + + /// 종류 + private String category; + + /// 좌표 + private Location location; + + /// 주소 + private String address; + + /// 휴무,운영 + private String restGuide; + + /// 운영시간 + private String operateTime; + + /// 가능여부 + private String petPossibleAt; + + /// 반려동물 크기 + private String enterPossiblePetSizeValue; + + /// 반려동물 제한사항 + private String petLimitMatterContent; + + /// 실내 입장 가능 여부 + private String inPlaceAcceptPossibleAt; + + /// 실외 입장 가능 여부 + private String outPlaceAcceptPossibleAt; + + /// 시설 안내 + private String facilityInfoDescription; + +} diff --git a/src/main/java/com/pinHouse/server/platform/domain/facility/Library.java b/src/main/java/com/pinHouse/server/platform/domain/facility/Library.java new file mode 100644 index 0000000..0d7f1fb --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/domain/facility/Library.java @@ -0,0 +1,25 @@ +package com.pinHouse.server.platform.domain.facility; + +import com.pinHouse.server.platform.domain.location.Location; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class Library { + + private String id; + private Integer code; + private String name; + private String address; + private Location location; + private String area; + private String city; + private Integer number; + private String openTime; + private String closedDay; +} diff --git a/src/main/java/com/pinHouse/server/platform/domain/facility/Park.java b/src/main/java/com/pinHouse/server/platform/domain/facility/Park.java new file mode 100644 index 0000000..e0abdb0 --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/domain/facility/Park.java @@ -0,0 +1,32 @@ +package com.pinHouse.server.platform.domain.facility; + +import com.pinHouse.server.platform.domain.location.Location; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@AllArgsConstructor +@Builder +public class Park { + + /// 아이디 + private String id; + + /// 공원 시스템 ID + private String parkId; + + /// 명칭 + private String name; + + /// 공원 분류 명칭 + private String category; + + /// PNU (법정동+지번코드) + private String pnu; + + /// 좌표 + private Location location; + + +} diff --git a/src/main/java/com/pinHouse/server/platform/domain/facility/Sport.java b/src/main/java/com/pinHouse/server/platform/domain/facility/Sport.java new file mode 100644 index 0000000..ce3a1e9 --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/domain/facility/Sport.java @@ -0,0 +1,43 @@ +package com.pinHouse.server.platform.domain.facility; + +import com.pinHouse.server.platform.domain.location.Location; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class Sport { + + private String id; + + /** 시설 명칭 */ + private String name; + + /** 업종 명칭 */ + private String industryName; + + /** 시설 유형 명칭 */ + private String facilityTypeName; + + /** 시설 상태값 (예: 정상운영 등) */ + private String facilityStateValue; + + /** 도로명주소-1 */ + private String address; + + /// 좌표 + private Location location; + + /** 시설 운영 형태 값 */ + private String facilityOperStyleValue; + + /** 시설 기준일자(생성 기준일) */ + private String facilityCreationStandardDate; + + /** 국가 공공 시설 여부 */ + private String nationPublicFacilityAt; +} diff --git a/src/main/java/com/pinHouse/server/platform/domain/facility/Walking.java b/src/main/java/com/pinHouse/server/platform/domain/facility/Walking.java new file mode 100644 index 0000000..108c8e4 --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/domain/facility/Walking.java @@ -0,0 +1,61 @@ +package com.pinHouse.server.platform.domain.facility; + +import com.pinHouse.server.platform.domain.location.Location; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class Walking { + + private String id; + + /// 코스 고유 식별자 + private String esntlId; + + ///걷기 코스 플래그명 (코스 대표 명칭) + private String walkingCourseFlagName; + + /// 걷기 코스명 + private String walkingCourseName; + + /// 코스 상세설명 */ + private String courseDescription; + + /// 행정구역명(시/군/구) */ + private String districtName; + + /// 코스 난이도 */ + private String courseLevelName; + + /// 코스 거리 구간명 (예: 10~15㎞미만) */ + private String courseLengthDescription; + + /// 상세 거리 (단위: km, 실수 값) */ + private Double courseDetailLengthKm; + + /** 추가 설명 (코스 특성, 지역 건강증진 등) */ + private String additionalDescription; + + /** 예상 소요 시간 (예: 4시간) */ + private String courseTime; + + /** 편의시설 및 옵션 안내 */ + private String optionDescription; + + /** 화장실 안내 */ + private String toiletDescription; + + /** 주변 편의점/휴게시설 안내 */ + private String convenienceName; + + /** 코스 시작/대표 주소 */ + private String address; + + /// 좌표 + private Location location; +} diff --git a/src/main/java/com/pinHouse/server/platform/domain/notice/NoticeInfra.java b/src/main/java/com/pinHouse/server/platform/domain/notice/NoticeInfra.java new file mode 100644 index 0000000..7bf9c30 --- /dev/null +++ b/src/main/java/com/pinHouse/server/platform/domain/notice/NoticeInfra.java @@ -0,0 +1,67 @@ +package com.pinHouse.server.platform.domain.notice; + +import com.pinHouse.server.platform.domain.facility.*; +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +/** + *
+ * 공지사항 및 주변 인프라 리스트 정보를 담는 도메인 객체입니다.
+ *
+ * - 공지 객체와 주변 도서관, 동물, 스포츠, 산책로, 공원 목록을 함께 제공합니다.
+ * - 실무 현장에서 다양한 시설 데이터 연동에 용이하게 활용합니다.
+ * 
+ */ +@Builder +@Getter +public class NoticeInfra { + + /** 공지사항 엔티티 */ + private Notice notice; + + /** 주변 도서관 리스트 */ + private List libraries; + + /** 주변 동물 관련 시설 리스트 */ + private List animals; + + /** 주변 스포츠 시설 리스트 */ + private List sports; + + /** 주변 산책로 리스트 */ + private List walkings; + + /** 주변 공원 리스트 */ + private List parks; + + /** + * NoticeInfra 팩토리 메서드 + * + * @param notice 공지사항 엔티티 + * @param libraries 주변 도서관 리스트 + * @param animals 주변 동물 관련 시설 리스트 + * @param sports 주변 스포츠 시설 리스트 + * @param walkings 주변 산책로 리스트 + * @param parks 주변 공원 리스트 + * @return NoticeInfra 도메인 객체 + */ + public static NoticeInfra of( + Notice notice, + List libraries, + List animals, + List sports, + List walkings, + List parks + ) { + return NoticeInfra.builder() + .notice(notice) + .libraries(libraries) + .animals(animals) + .sports(sports) + .walkings(walkings) + .parks(parks) + .build(); + } +} diff --git a/src/main/java/com/pinHouse/server/security/config/RequestMatcherHolder.java b/src/main/java/com/pinHouse/server/security/config/RequestMatcherHolder.java index f6cc9ff..ea3b124 100644 --- a/src/main/java/com/pinHouse/server/security/config/RequestMatcherHolder.java +++ b/src/main/java/com/pinHouse/server/security/config/RequestMatcherHolder.java @@ -32,7 +32,7 @@ public class RequestMatcherHolder { new RequestInfo(POST, "/api/v1/auth/logout", Role.USER), // 상품 관련 - new RequestInfo(GET, "/api/v1/products/**", null), + new RequestInfo(GET, "/api/v1/notices/**", null), // static resources new RequestInfo(GET, "/docs/**", null),