diff --git a/src/main/java/com/ecolink/core/CoreApplication.java b/src/main/java/com/ecolink/core/CoreApplication.java index db0cd4a3..7c66fdf0 100644 --- a/src/main/java/com/ecolink/core/CoreApplication.java +++ b/src/main/java/com/ecolink/core/CoreApplication.java @@ -1,8 +1,10 @@ package com.ecolink.core; import org.springframework.boot.SpringApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.boot.autoconfigure.SpringBootApplication; +@EnableFeignClients @SpringBootApplication public class CoreApplication { diff --git a/src/main/java/com/ecolink/core/common/constant/Address.java b/src/main/java/com/ecolink/core/common/constant/Address.java index 54a75609..213bb332 100644 --- a/src/main/java/com/ecolink/core/common/constant/Address.java +++ b/src/main/java/com/ecolink/core/common/constant/Address.java @@ -35,4 +35,11 @@ public class Address { @Schema(description = "지번 주소", example = "흑석동 54-149 1층") private String lotNumber; + public Address(String province, String city, String roadName, String lotNumber) { + this.province = province; + this.city = city; + this.roadName = roadName; + this.lotNumber = lotNumber; + } + } diff --git a/src/main/java/com/ecolink/core/map/controller/StoreMapController.java b/src/main/java/com/ecolink/core/map/controller/StoreMapController.java new file mode 100644 index 00000000..9211652e --- /dev/null +++ b/src/main/java/com/ecolink/core/map/controller/StoreMapController.java @@ -0,0 +1,49 @@ +package com.ecolink.core.map.controller; + +import com.ecolink.core.map.util.ContentDetailFetcher; +import com.ecolink.core.map.util.ContentListFetcher; +import com.ecolink.core.map.util.StoreDetailFetcher; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; + +@RestController +public class StoreMapController { + + private final ContentListFetcher contentListFetcher; + private final ContentDetailFetcher contentDetailFetcher; + private final StoreDetailFetcher storeDetailFetcher; + + public StoreMapController(ContentListFetcher contentListFetcher, ContentDetailFetcher contentDetailFetcher, + StoreDetailFetcher storeDetailFetcher) { + this.contentListFetcher = contentListFetcher; + this.contentDetailFetcher = contentDetailFetcher; + this.storeDetailFetcher = storeDetailFetcher; + } + + @Tag(name = "${swagger.map-data}") + @Operation(summary = "스마트서울맵 데이터 조회 API", description = "테마 내 모든 콘텐츠 리스트 API") + @GetMapping("/list") + public void getContentsListAll() { + contentListFetcher.fetchDataAndStore(); + + } + + @Tag(name = "${swagger.map-data}") + @Operation(summary = "스마트서울맵 데이터 조회 API", description = "테마 내 모든 콘텐츠 상세 정보 API") + @GetMapping("/data") + public void getContentsDetailAll() { + contentDetailFetcher.fetchAndStoreContentDetail(); + } + + @Tag(name = "${swagger.map-data}") + @Operation(summary = "상점 데이터 등록 API", description = "상점 상세 데이터 조회 및 등록 API") + @GetMapping("/store") + public void getStoreDetailAll() { + storeDetailFetcher.fetchAndStoreInfoDetail(); + } + +} diff --git a/src/main/java/com/ecolink/core/map/domain/Map.java b/src/main/java/com/ecolink/core/map/domain/Map.java new file mode 100644 index 00000000..b5a66dea --- /dev/null +++ b/src/main/java/com/ecolink/core/map/domain/Map.java @@ -0,0 +1,35 @@ +package com.ecolink.core.map.domain; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import jakarta.persistence.*; + +import java.time.LocalDateTime; + +@Getter +@Setter +@NoArgsConstructor +@Entity +public class Map { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String mapThemeId; + private String mapContentId; + private String mapSubCategoryId; + private String mapContentStatus; + private LocalDateTime updateDate; + private LocalDateTime registrationDate; + + public Map(String mapThemeId, String mapContentId, String mapSubCategoryId, String mapContentStatus, LocalDateTime updateDate, LocalDateTime registrationDate) { + this.mapThemeId = mapThemeId; + this.mapContentId = mapContentId; + this.mapSubCategoryId = mapSubCategoryId; + this.mapContentStatus = mapContentStatus; + this.updateDate = updateDate; + this.registrationDate = registrationDate; + } +} \ No newline at end of file diff --git a/src/main/java/com/ecolink/core/map/domain/MapContent.java b/src/main/java/com/ecolink/core/map/domain/MapContent.java new file mode 100644 index 00000000..812db064 --- /dev/null +++ b/src/main/java/com/ecolink/core/map/domain/MapContent.java @@ -0,0 +1,121 @@ +package com.ecolink.core.map.domain; + +import org.locationtech.jts.geom.Point; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@Entity +public class MapContent { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String cotContsName; + private String cotValue03; + private String cotValue04; + private String cotValue05; + private String cotValue06; + private String cotValue07; + private String cotAddrFullNew; + private String cotAddrFullOld; + private String cotTelNo; + private String cotRegDate; + private String cotUpdateDate; + private String cotThemeId; + private String cotContsId; + private String cotGuName; + private String cotDongName; + private String cotSanName; + private String cotMasterNo; + private String cotSlaveNo; + private String cotExtraName; + private String cotNationBaseArea; + private String cotNationPointNumber; + private Point cotCoordData; + private String cotCoordType; + private String cotCoordX; + private String cotCoordY; + private String cotContsStat; + private String cotWriter; + private String cotThemeSubId; + private String cotExtraData01; + private String cotExtraData02; + private String cotMovieUrl; + private String cotVoiceUrl; + private String cotContsDetail; + private String cotImgMainUrl; + private String cotImgMainUrl2; + private String cotImgMainUrl3; + private String cotImgMainUrl4; + private String cotImgMainUrl5; + private String cotCoordStyle; + private String cotLinePattern; + private String cotLineWeight; + private String cotLineColor; + + public MapContent(String cotContsName, String cotValue03, String cotValue04, String cotValue05, String cotValue06, String cotValue07, + String cotAddrFullNew, String cotAddrFullOld, String cotTelNo, String cotRegDate, String cotUpdateDate, + String cotThemeId, String cotContsId, String cotGuName, String cotDongName, String cotSanName, + String cotMasterNo, String cotSlaveNo, String cotExtraName, String cotNationBaseArea, + String cotNationPointNumber, String cotCoordType, String cotCoordX, + String cotCoordY, String cotContsStat, String cotWriter, String cotThemeSubId, String cotExtraData01, + String cotExtraData02, String cotMovieUrl, String cotVoiceUrl, String cotContsDetail, + String cotImgMainUrl, String cotImgMainUrl2, String cotImgMainUrl3, String cotImgMainUrl4, + String cotImgMainUrl5, String cotCoordStyle, String cotLinePattern, String cotLineWeight, + String cotLineColor) { + this.cotContsName = cotContsName; + this.cotValue03 = cotValue03; + this.cotValue04 = cotValue04; + this.cotValue05 = cotValue05; + this.cotValue06 = cotValue06; + this.cotValue07 = cotValue07; + this.cotAddrFullNew = cotAddrFullNew; + this.cotAddrFullOld = cotAddrFullOld; + this.cotTelNo = cotTelNo; + this.cotRegDate = cotRegDate; + this.cotUpdateDate = cotUpdateDate; + this.cotThemeId = cotThemeId; + this.cotContsId = cotContsId; + this.cotGuName = cotGuName; + this.cotDongName = cotDongName; + this.cotSanName = cotSanName; + this.cotMasterNo = cotMasterNo; + this.cotSlaveNo = cotSlaveNo; + this.cotExtraName = cotExtraName; + this.cotNationBaseArea = cotNationBaseArea; + this.cotNationPointNumber = cotNationPointNumber; + this.cotCoordType = cotCoordType; + this.cotCoordX = cotCoordX; + this.cotCoordY = cotCoordY; + this.cotContsStat = cotContsStat; + this.cotWriter = cotWriter; + this.cotThemeSubId = cotThemeSubId; + this.cotExtraData01 = cotExtraData01; + this.cotExtraData02 = cotExtraData02; + this.cotMovieUrl = cotMovieUrl; + this.cotVoiceUrl = cotVoiceUrl; + this.cotContsDetail = cotContsDetail; + this.cotImgMainUrl = cotImgMainUrl; + this.cotImgMainUrl2 = cotImgMainUrl2; + this.cotImgMainUrl3 = cotImgMainUrl3; + this.cotImgMainUrl4 = cotImgMainUrl4; + this.cotImgMainUrl5 = cotImgMainUrl5; + this.cotCoordStyle = cotCoordStyle; + this.cotLinePattern = cotLinePattern; + this.cotLineWeight = cotLineWeight; + this.cotLineColor = cotLineColor; + } + +} diff --git a/src/main/java/com/ecolink/core/map/dto/ContentDetailDto.java b/src/main/java/com/ecolink/core/map/dto/ContentDetailDto.java new file mode 100644 index 00000000..ec1d172c --- /dev/null +++ b/src/main/java/com/ecolink/core/map/dto/ContentDetailDto.java @@ -0,0 +1,145 @@ +package com.ecolink.core.map.dto; + +import java.math.BigDecimal; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@ToString +@Getter +@Setter +@JsonIgnoreProperties(ignoreUnknown = true) +public class ContentDetailDto { + + @Schema(description = "상점이름", example = "호랑이상점") + private String cotContsName; + + @Schema(description = "운영시간", example = "매일 11:00-21:30/ 비건 베이커리만 화 휴무") + private String cotValue_03; + + @Schema(description = "취급품목(메뉴)", example = "제로웨이스트 제품, 비건 식품, 리필 세제류, 비건베이커리") + private String cotValue_04; + + @Schema(description = "인스타그램", example = "http://www.instagram.com/zerowaste_jigu") + private String cotValue_05; + + @Schema(description = "제로페이", example = "가능") + private String cotValue_06; + + @Schema(description = "인터넷 쇼핑몰", example = "https://smartstore.naver.com/peaceontable") + private String cotValue_07; + + @Schema(description = "매장 주소 (새 주소)", example = "서울특별시 마포구 성미산로 155") + private String cotAddrFullNew; + + @Schema(description = "매장 주소 (구 주소)", example = "서울특별시 마포구 연남동 240-23") + private String cotAddrFullOld; + + @Schema(description = "전화번호", example = "070-7721-5748") + private String cotTelNo; + + @Schema(description = "등록일", example = "2023-10-31 10:04:59") + private String cotRegDate; + + @Schema(description = "업데이트 일", example = "2023-10-31 13:02:54") + private String cotUpdateDate; + + @Schema(description = "테마 ID", example = "11103395") + private String cotThemeId; + + @Schema(description = "콘텐츠 ID", example = "zerowaste_0004") + private String cotContsId; + + @Schema(description = "구명", example = "마포구") + private String cotGuName; + + @Schema(description = "동명", example = "") + private String cotDongName; + + @Schema(description = "산지 명", example = "") + private String cotSanName; + + @Schema(description = "주 번지", example = "") + private String cotMasterNo; + + @Schema(description = "부 번지", example = "") + private String cotSlaveNo; + + @Schema(description = "나머지 주소", example = "") + private String cotExtraName; + + @Schema(description = "국가 기초 구역", example = "03958") + private String cotNationBaseArea; + + @Schema(description = "국가 지점 번호", example = "다사49085184") + private String cotNationPointNumber; + + @Schema(description = "좌표 정보 (GeoJson)") + private List cotCoordData; + + @Schema(description = "콘텐츠 좌표 타입", example = "1") + private String cotCoordType; + + @Schema(description = "X 좌표", example = "126.923533416") + private String cotCoordX; + + @Schema(description = "Y 좌표", example = "37.564525958") + private String cotCoordY; + + @Schema(description = "사용 유무", example = "1") + private String cotContsStat; + + @Schema(description = "등록자/수정자", example = "motif77") + private String cotWriter; + + @Schema(description = "콘텐츠 서브카테고리", example = "4") + private String cotThemeSubId; + + @Schema(description = "기타 정보 1", example = "") + private String cotExtraData_01; + + @Schema(description = "링크 URL", example = "https://www.jigushop.co.kr/") + private String cotExtraData_02; + + @Schema(description = "동영상 URL", example = "") + private String cotMovieUrl; + + @Schema(description = "음성파일 URL", example = "") + private String cotVoiceUrl; + + @Schema(description = "콘텐츠 상세", example = "콘텐츠 상세 내용") + private String cotContsDetail; + + @Schema(description = "이미지 URL1") + private String cotImgMainUrl; + + @Schema(description = "이미지 URL2") + private String cotImgMainUrl2; + + @Schema(description = "이미지 URL3") + private String cotImgMainUrl3; + + @Schema(description = "이미지 URL4") + private String cotImgMainUrl4; + + @Schema(description = "이미지 URL5") + private String cotImgMainUrl5; + + @Schema(description = "코디네이트 스타일") + private String cotCoordStyle; + + @Schema(description = "라인 패턴", example = "L") + private String cotLinePattern; + + @Schema(description = "라인 무게", example = "4") + private String cotLineWeight; + + @Schema(description = "라인 색상", example = "#0000FF") + private String cotLineColor; + +} diff --git a/src/main/java/com/ecolink/core/map/dto/ContentItemDto.java b/src/main/java/com/ecolink/core/map/dto/ContentItemDto.java new file mode 100644 index 00000000..ad06f9e3 --- /dev/null +++ b/src/main/java/com/ecolink/core/map/dto/ContentItemDto.java @@ -0,0 +1,31 @@ +package com.ecolink.core.map.dto; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class) +public class ContentItemDto { + @Schema(description = "등록일", example = "2023-10-31 10:04:58.0") + private String cotRegDate; + + @Schema(description = "콘텐츠 테마 서브 ID", example = "4") + private String cotThemeSubId; + + @Schema(description = "업데이트 일", example = "2023-10-31 13:12:19.0") + private String cotUpdateDate; + + @Schema(description = "콘텐츠 테마 ID", example = "11103395") + private String cotThemeId; + + @Schema(description = "콘텐츠 ID", example = "zerowaste_0005") + private String cotContsId; + + @Schema(description = "콘텐츠 상태", example = "0") + private String cotContsStat; +} diff --git a/src/main/java/com/ecolink/core/map/dto/ContentListDetailDto.java b/src/main/java/com/ecolink/core/map/dto/ContentListDetailDto.java new file mode 100644 index 00000000..261fc889 --- /dev/null +++ b/src/main/java/com/ecolink/core/map/dto/ContentListDetailDto.java @@ -0,0 +1,23 @@ +package com.ecolink.core.map.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +@Getter +@Setter +@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class) +public class ContentListDetailDto { + @Schema(description = "성공 오류 응답 코드", example = "0") + private String retCode; + + @Schema(description = "데이터 양", example = "99") + private int dataCount; + + @Schema(description = "콘텐츠 상세 정보") + private List body; +} \ No newline at end of file diff --git a/src/main/java/com/ecolink/core/map/dto/ContentListDto.java b/src/main/java/com/ecolink/core/map/dto/ContentListDto.java new file mode 100644 index 00000000..afb18c61 --- /dev/null +++ b/src/main/java/com/ecolink/core/map/dto/ContentListDto.java @@ -0,0 +1,23 @@ +package com.ecolink.core.map.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +@Getter +@Setter +@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class) +public class ContentListDto { + @Schema(description = "성공 오류 응답 코드", example = "0") + private String retCode; + + @Schema(description = "데이터 양", example = "99") + private int dataCount; + + @Schema(description = "콘텐츠 리스트") + private List body; +} \ No newline at end of file diff --git a/src/main/java/com/ecolink/core/map/repository/ContentDetailRepository.java b/src/main/java/com/ecolink/core/map/repository/ContentDetailRepository.java new file mode 100644 index 00000000..8492ddcb --- /dev/null +++ b/src/main/java/com/ecolink/core/map/repository/ContentDetailRepository.java @@ -0,0 +1,9 @@ +package com.ecolink.core.map.repository; + +import com.ecolink.core.map.domain.MapContent; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ContentDetailRepository extends JpaRepository { +} diff --git a/src/main/java/com/ecolink/core/map/repository/ContentListRepository.java b/src/main/java/com/ecolink/core/map/repository/ContentListRepository.java new file mode 100644 index 00000000..3827147f --- /dev/null +++ b/src/main/java/com/ecolink/core/map/repository/ContentListRepository.java @@ -0,0 +1,10 @@ +package com.ecolink.core.map.repository; + +import com.ecolink.core.map.domain.Map; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ContentListRepository extends JpaRepository { + +} \ No newline at end of file diff --git a/src/main/java/com/ecolink/core/map/service/ContentDetailService.java b/src/main/java/com/ecolink/core/map/service/ContentDetailService.java new file mode 100644 index 00000000..e1d9ca88 --- /dev/null +++ b/src/main/java/com/ecolink/core/map/service/ContentDetailService.java @@ -0,0 +1,95 @@ +package com.ecolink.core.map.service; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ecolink.core.geo.service.GeometryService; +import com.ecolink.core.map.domain.MapContent; +import com.ecolink.core.map.dto.ContentDetailDto; +import com.ecolink.core.map.repository.ContentDetailRepository; +import com.ecolink.core.store.dto.request.MapQueryRequest; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; + +@Service +@Transactional +public class ContentDetailService { + + private final ContentDetailRepository contentDetailRepository; + private final ObjectMapper mapper; + private final GeometryService geometryService; + + public ContentDetailService(ContentDetailRepository contentDetailRepository, GeometryService geometryService) { + this.contentDetailRepository = contentDetailRepository; + this.geometryService = geometryService; + this.mapper = new ObjectMapper().setPropertyNamingStrategy(PropertyNamingStrategies.UPPER_SNAKE_CASE); + } + + public void saveContentDetail(String responseData) { + try { + JsonNode jsonNode = mapper.readValue(responseData, JsonNode.class); + JsonNode body = jsonNode.get("body"); + + for (JsonNode contents : body) { + ContentDetailDto dto = mapper.treeToValue(contents, ContentDetailDto.class); + MapContent mapContent = mapDetailToMapEntity(dto); + contentDetailRepository.save(mapContent); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + private MapContent mapDetailToMapEntity(ContentDetailDto dto) { + MapContent mapContent = new MapContent(); + mapContent.setCotContsName(dto.getCotContsName()); + mapContent.setCotValue03(dto.getCotValue_03()); + mapContent.setCotValue04(dto.getCotValue_04()); + mapContent.setCotValue05(dto.getCotValue_05()); + mapContent.setCotValue06(dto.getCotValue_06()); + mapContent.setCotValue07(dto.getCotValue_07()); + mapContent.setCotAddrFullNew(dto.getCotAddrFullNew()); + mapContent.setCotAddrFullOld(dto.getCotAddrFullOld()); + mapContent.setCotTelNo(dto.getCotTelNo()); + mapContent.setCotRegDate(dto.getCotRegDate()); + mapContent.setCotUpdateDate(dto.getCotUpdateDate()); + mapContent.setCotThemeId(dto.getCotThemeId()); + mapContent.setCotContsId(dto.getCotContsId()); + mapContent.setCotGuName(dto.getCotGuName()); + mapContent.setCotDongName(dto.getCotDongName()); + mapContent.setCotSanName(dto.getCotSanName()); + mapContent.setCotMasterNo(dto.getCotMasterNo()); + mapContent.setCotSlaveNo(dto.getCotSlaveNo()); + mapContent.setCotExtraName(dto.getCotExtraName()); + mapContent.setCotNationBaseArea(dto.getCotNationBaseArea()); + mapContent.setCotNationPointNumber(dto.getCotNationPointNumber()); + if (!dto.getCotCoordX().isEmpty() && !dto.getCotCoordY().isEmpty()) + mapContent.setCotCoordData( + geometryService.getPoint( + new MapQueryRequest(Double.valueOf(dto.getCotCoordX()), Double.valueOf(dto.getCotCoordY())))); + mapContent.setCotCoordType(dto.getCotCoordType()); + mapContent.setCotCoordX(dto.getCotCoordX()); + mapContent.setCotCoordY(dto.getCotCoordY()); + mapContent.setCotContsStat(dto.getCotContsStat()); + mapContent.setCotWriter(dto.getCotWriter()); + mapContent.setCotThemeSubId(dto.getCotThemeSubId()); + mapContent.setCotExtraData01(dto.getCotExtraData_01()); + mapContent.setCotExtraData02(dto.getCotExtraData_02()); + mapContent.setCotMovieUrl(dto.getCotMovieUrl()); + mapContent.setCotVoiceUrl(dto.getCotVoiceUrl()); + mapContent.setCotContsDetail(dto.getCotContsDetail()); + mapContent.setCotImgMainUrl(dto.getCotImgMainUrl()); + mapContent.setCotImgMainUrl2(dto.getCotImgMainUrl2()); + mapContent.setCotImgMainUrl3(dto.getCotImgMainUrl3()); + mapContent.setCotImgMainUrl4(dto.getCotImgMainUrl4()); + mapContent.setCotImgMainUrl5(dto.getCotImgMainUrl5()); + mapContent.setCotCoordStyle(dto.getCotCoordStyle()); + mapContent.setCotLinePattern(dto.getCotLinePattern()); + mapContent.setCotLineWeight(dto.getCotLineWeight()); + mapContent.setCotLineColor(dto.getCotLineColor()); + + return mapContent; + } + +} diff --git a/src/main/java/com/ecolink/core/map/service/ContentListService.java b/src/main/java/com/ecolink/core/map/service/ContentListService.java new file mode 100644 index 00000000..b8b803a8 --- /dev/null +++ b/src/main/java/com/ecolink/core/map/service/ContentListService.java @@ -0,0 +1,68 @@ +package com.ecolink.core.map.service; + +import com.ecolink.core.map.domain.Map; +import com.ecolink.core.map.dto.ContentItemDto; +import com.ecolink.core.map.dto.ContentListDto; +import com.ecolink.core.map.repository.ContentListRepository; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + +@Service +@Transactional +public class ContentListService { + + private final ContentListRepository contentListRepository; + private final ObjectMapper objectMapper; + + @Autowired + public ContentListService(ContentListRepository contentListRepository, ObjectMapper objectMapper) { + this.contentListRepository = contentListRepository; + this.objectMapper = objectMapper; + } + + public void saveContentsListAll(String responseData) { + try { + ContentListDto responseDTO = objectMapper.readValue(responseData, ContentListDto.class); + List contentList = responseDTO.getBody(); + + for (ContentItemDto contentItem : contentList) { + Map map = mapContentToMapEntity(contentItem); + contentListRepository.save(map); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + public List getContentIds() { + List maps = contentListRepository.findAll(); + List contentIds = new ArrayList<>(); + for (Map map : maps) { + contentIds.add(map.getMapContentId()); + } + return contentIds; + } + + private Map mapContentToMapEntity(ContentItemDto contentItem) { + Map map = new Map(); + map.setMapThemeId(contentItem.getCotThemeId()); + map.setMapContentId(contentItem.getCotContsId()); + map.setMapSubCategoryId(contentItem.getCotThemeSubId()); + map.setMapContentStatus(contentItem.getCotContsStat()); + + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.S"); + LocalDateTime updateDate = LocalDateTime.parse(contentItem.getCotUpdateDate(), formatter); + LocalDateTime regDate = LocalDateTime.parse(contentItem.getCotRegDate(), formatter); + + map.setUpdateDate(updateDate); + map.setRegistrationDate(regDate); + return map; + } +} \ No newline at end of file diff --git a/src/main/java/com/ecolink/core/map/util/ContentDetailFetcher.java b/src/main/java/com/ecolink/core/map/util/ContentDetailFetcher.java new file mode 100644 index 00000000..1339ac57 --- /dev/null +++ b/src/main/java/com/ecolink/core/map/util/ContentDetailFetcher.java @@ -0,0 +1,57 @@ +package com.ecolink.core.map.util; + +import java.net.URI; +import java.util.List; + +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.ecolink.core.map.service.ContentDetailService; +import com.ecolink.core.map.service.ContentListService; + +@Component +public class ContentDetailFetcher { + + private final ContentDetailService contentDetailService; + private final ContentListService contentListService; + + @Autowired + public ContentDetailFetcher(ContentDetailService contentDetailService, ContentListService contentListService) { + this.contentDetailService = contentDetailService; + this.contentListService = contentListService; + } + + public void fetchAndStoreContentDetail() { + List contentIds = contentListService.getContentIds(); + + HttpClient httpClient = HttpClients.createDefault(); + for (String contentId : contentIds) { + + try { + URI uri = + new URI("https", + "//map.seoul.go.kr/smgis/apps/poi.do?cmd=getNewContentsDetail&key=90d9ef047980430297ec7fa3a8377710&theme_id=11103395&conts_id=" + + contentId, null); + HttpGet httpGet = new HttpGet(uri); + HttpResponse response = httpClient.execute(httpGet); + int statusCode = response.getStatusLine().getStatusCode(); + + if (statusCode == 200) { + String responseData = EntityUtils.toString(response.getEntity()); + + contentDetailService.saveContentDetail(responseData); + } else { + System.err.println( + "Failed to fetch data for content id: " + contentId + ", Status Code: " + statusCode); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } +} diff --git a/src/main/java/com/ecolink/core/map/util/ContentListFetcher.java b/src/main/java/com/ecolink/core/map/util/ContentListFetcher.java new file mode 100644 index 00000000..6f01df41 --- /dev/null +++ b/src/main/java/com/ecolink/core/map/util/ContentListFetcher.java @@ -0,0 +1,41 @@ +package com.ecolink.core.map.util; + +import com.ecolink.core.map.service.ContentListService; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ContentListFetcher { + + private final ContentListService contentListService; + + @Autowired + public ContentListFetcher(ContentListService contentListService) { + this.contentListService = contentListService; + } + + public void fetchDataAndStore() { + HttpClient httpClient = HttpClients.createDefault(); + HttpGet httpGet = new HttpGet("https://map.seoul.go.kr/smgis/apps/theme.do?cmd=getContentsListAll&key=90d9ef047980430297ec7fa3a8377710&theme_id=11103395"); + + try { + HttpResponse response = httpClient.execute(httpGet); + int statusCode = response.getStatusLine().getStatusCode(); + + if (statusCode == 200) { + String responseData = EntityUtils.toString(response.getEntity()); + + contentListService.saveContentsListAll(responseData); + } else { + System.err.println("Failed to fetch data, Status Code: " + statusCode); + } + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ecolink/core/map/util/StoreDetailFetcher.java b/src/main/java/com/ecolink/core/map/util/StoreDetailFetcher.java new file mode 100644 index 00000000..ecc1f67e --- /dev/null +++ b/src/main/java/com/ecolink/core/map/util/StoreDetailFetcher.java @@ -0,0 +1,57 @@ +package com.ecolink.core.map.util; + +import java.net.URI; +import java.util.List; + +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.ecolink.core.map.service.ContentListService; +import com.ecolink.core.store.service.StoreMapService; + +@Component +public class StoreDetailFetcher { + + private final StoreMapService storeMapService; + private final ContentListService contentListService; + + @Autowired + public StoreDetailFetcher(StoreMapService storeMapService, ContentListService contentListService) { + this.storeMapService = storeMapService; + this.contentListService = contentListService; + } + + public void fetchAndStoreInfoDetail() { + List contentIds = contentListService.getContentIds(); + + HttpClient httpClient = HttpClients.createDefault(); + for (String contentId : contentIds) { + + try { + URI uri = + new URI("https", + "//map.seoul.go.kr/smgis/apps/poi.do?cmd=getNewContentsDetail&key=90d9ef047980430297ec7fa3a8377710&theme_id=11103395&conts_id=" + + contentId, null); + HttpGet httpGet = new HttpGet(uri); + HttpResponse response = httpClient.execute(httpGet); + int statusCode = response.getStatusLine().getStatusCode(); + + if (statusCode == 200) { + String responseData = EntityUtils.toString(response.getEntity()); + + storeMapService.saveStoreDetail(responseData); + } else { + System.err.println( + "Failed to fetch data for content id: " + contentId + ", Status Code: " + statusCode); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } +} diff --git a/src/main/java/com/ecolink/core/store/domain/Store.java b/src/main/java/com/ecolink/core/store/domain/Store.java index 0d08ba07..d8831dac 100644 --- a/src/main/java/com/ecolink/core/store/domain/Store.java +++ b/src/main/java/com/ecolink/core/store/domain/Store.java @@ -28,12 +28,13 @@ import jakarta.persistence.Table; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; -import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; @Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Setter +@NoArgsConstructor @Entity @Table(name = "store", indexes = @Index(name = "idx_store_name", columnList = "name")) public class Store extends BaseTimeEntity implements MultiPhotoContainer { @@ -126,4 +127,14 @@ public void addProductCnt() { this.productCnt++; } + public Store(String name, Address address, String contact, String homepageUrl, String description, + String instagramUrl) { + this.name = name; + this.address = address; + this.contact = contact; + this.homepageUrl = homepageUrl; + this.description = description; + this.instagramUrl = instagramUrl; + } + } diff --git a/src/main/java/com/ecolink/core/store/service/StoreMapService.java b/src/main/java/com/ecolink/core/store/service/StoreMapService.java new file mode 100644 index 00000000..7508827b --- /dev/null +++ b/src/main/java/com/ecolink/core/store/service/StoreMapService.java @@ -0,0 +1,72 @@ +package com.ecolink.core.store.service; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ecolink.core.common.constant.Address; +import com.ecolink.core.geo.service.GeometryService; +import com.ecolink.core.map.dto.ContentDetailDto; +import com.ecolink.core.store.domain.Store; +import com.ecolink.core.store.dto.request.MapQueryRequest; +import com.ecolink.core.store.repository.StoreRepository; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; + +@Service +@Transactional +public class StoreMapService { + + private final StoreRepository storeRepository; + private final ObjectMapper mapper; + private final GeometryService geometryService; + + public StoreMapService(StoreRepository storeRepository, GeometryService geometryService) { + this.storeRepository = storeRepository; + this.geometryService = geometryService; + this.mapper = new ObjectMapper().setPropertyNamingStrategy(PropertyNamingStrategies.UPPER_SNAKE_CASE); + } + + public void saveStoreDetail(String responseData) { + try { + JsonNode jsonNode = mapper.readValue(responseData, JsonNode.class); + JsonNode body = jsonNode.get("body"); + + for (JsonNode contents : body) { + ContentDetailDto dto = mapper.treeToValue(contents, ContentDetailDto.class); + Store store = mapDetailToMapEntity(dto); + storeRepository.save(store); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + private Store mapDetailToMapEntity(ContentDetailDto dto) { + Store store = new Store(); + store.setName(dto.getCotContsName()); + store.setContact(dto.getCotTelNo()); + store.setHomepageUrl(dto.getCotExtraData_02()); + store.setDescription(dto.getCotValue_04()); + store.setInstagramUrl(dto.getCotValue_05()); + store.setAddress( + new Address("서울특별시", + dto.getCotGuName() == null || dto.getCotGuName().isBlank() ? "구정보 없음" : dto.getCotGuName(), + parse(dto.getCotAddrFullNew()), parse(dto.getCotAddrFullOld()))); + if (!dto.getCotCoordX().isEmpty() && !dto.getCotCoordY().isEmpty()) + store.setCoordinates( + geometryService.getPoint( + new MapQueryRequest(Double.valueOf(dto.getCotCoordX()), Double.valueOf(dto.getCotCoordY())))); + return store; + } + + private String parse(String fullAddress) { + String[] split = fullAddress.split(" ", 3); + if (fullAddress == null || fullAddress.isBlank() || split.length <= 2 || split[2].isBlank()) { + System.out.println("split = " + fullAddress); + return "파싱 불가능"; + } + return split[2]; + } + +} diff --git a/src/main/resources/application-swagger.yml b/src/main/resources/application-swagger.yml index 0f6954de..3804a4a0 100644 --- a/src/main/resources/application-swagger.yml +++ b/src/main/resources/application-swagger.yml @@ -11,4 +11,5 @@ swagger: map: '09. 맵 페이지' tag: '10. 태그' home: '11. 홈 화면 페이지' + map-data: '12. 서울맵 데이터' admin: 'A. 어드민 관련 API'