Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

공공데이터 저장 기능의 코드 구조 개선 #129

Merged
merged 5 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
import lombok.Getter;
import org.springframework.http.HttpStatus;

import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.HttpStatus.*;

@Getter
@AllArgsConstructor
Expand All @@ -25,11 +24,10 @@ public enum ErrorCode {
NOT_FOUND_COLLECTING_BOX(NOT_FOUND, "해당 수거함이 존재하지 않습니다."),

// 임시
NOT_FOUND_TAG(NOT_FOUND, "해당 이름과 일치하는 수거함 태그가 없습니다.");

// 409
NOT_FOUND_TAG(NOT_FOUND, "해당 이름과 일치하는 수거함 태그가 없습니다."),

// 500
UNEXPECTED_ERROR_EXTERNAL_API(INTERNAL_SERVER_ERROR, "외부 API 호출 시 알 수 없는 예외가 발생했습니다.");

private final HttpStatus httpStatus;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
package contest.collectingbox.module.collectingbox.domain.repository;

import contest.collectingbox.module.collectingbox.domain.CollectingBox;
import contest.collectingbox.module.collectingbox.domain.Tag;
import contest.collectingbox.module.location.domain.DongInfo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface CollectingBoxRepository extends JpaRepository<CollectingBox, Long>, CollectingBoxRepositoryCustom {

@Query("select c from CollectingBox c join c.location l where l.dongInfo = :dongInfo and c.tag in :tags")
List<CollectingBox> findAllByDongInfoAndTags(@Param("dongInfo") DongInfo dongInfo,
@Param("tags") List<Tag> tags);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
package contest.collectingbox.module.publicdata;
package contest.collectingbox.module.publicdata.application;

import com.opencsv.CSVReader;
import com.opencsv.exceptions.CsvValidationException;
import contest.collectingbox.module.collectingbox.domain.repository.CollectingBoxRepository;
import contest.collectingbox.module.collectingbox.domain.Tag;
import contest.collectingbox.module.collectingbox.domain.repository.CollectingBoxRepository;
import contest.collectingbox.module.location.domain.repository.DongInfoRepository;
import contest.collectingbox.module.publicdata.domain.*;
import contest.collectingbox.module.publicdata.dto.AddressInfoDto;
import contest.collectingbox.module.publicdata.dto.LoadCsvPublicDataRequest;
import contest.collectingbox.module.publicdata.dto.LoadPublicDataRequest;
import contest.collectingbox.module.publicdata.dto.SavePublicDataApiInfoRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.io.InputStreamReader;
Expand All @@ -24,59 +30,62 @@
public class PublicDataService {

private static final String CSV_FILE_PATH = "csv/";

private final PublicDataApiInfoRepository publicDataApiInfoRepository;
private final OpenDataApiManager openDataApiManager;
private final PublicDataExtract publicDataExtract;
private final KakaoApiManager kakaoApiManager;
private final CollectingBoxRepository collectingBoxRepository;
private final DongInfoRepository dongInfoRepository;

@Transactional
public void savePublicDataApiInfo(List<SavePublicDataApiInfoRequest> requests) {
for (SavePublicDataApiInfoRequest request : requests) {
publicDataApiInfoRepository.save(request.toEntity());
}
publicDataApiInfoRepository.saveAll(getEntities(requests));
}

public long loadPublicData(JSONObject jsonObject, String sigungu, Tag tag) {
long loadedDataCount = 0;
JSONArray jsonArray = (JSONArray) jsonObject.get("data");
private List<PublicDataApiInfo> getEntities(List<SavePublicDataApiInfoRequest> requests) {
return requests.stream()
.map(SavePublicDataApiInfoRequest::toEntity)
.toList();
}

Set<String> querySet = new HashSet<>();
for (Object o : jsonArray) {
JSONObject object = (JSONObject) o;
querySet.add(publicDataExtract.extractQuery(object, sigungu, tag));
}
@Transactional
public long loadPublicData(List<LoadPublicDataRequest> requests) {
long loadedDataCount = 0;
for (LoadPublicDataRequest request : requests) {
log.info("======= {} - {} =======", request.getSigungu(), request.getTag().getLabel());

for (String query : querySet) {
// 검색 키워드 null 체크
if (query == null) {
continue;
}
String sigungu = request.getSigungu();
Tag tag = request.getTag();

// 카카오 주소 검색 API 호출
AddressInfoDto addressInfo = kakaoApiManager.fetchAddressInfo(query, tag);
int totalCount = openDataApiManager.fetchTotalCount(request.getCallAddress());
JSONArray jsonArray = openDataApiManager.fetchOpenData(request.getCallAddress(), totalCount);

// 카카오 주소 검색 API 응답 null 체크
if (addressInfo == null) {
continue;
Set<String> querySet = new HashSet<>();
for (Object o : jsonArray) {
publicDataExtract.extractQuery((JSONObject) o, sigungu, tag)
.ifPresent(querySet::add);
}

if (addressInfo.hasNull()) {
throw new RuntimeException("kakao API response has null");
}
for (String query : querySet) {
// 카카오 주소 검색 API 호출
AddressInfoDto addressInfo = kakaoApiManager.fetchAddressInfo(query, tag);

// 카카오 주소 검색 API 응답 출력
log.info("query = {}, response = {}", query, addressInfo);
// 카카오 주소 검색 API 응답 출력
log.info("query = {}, response = {}", query, addressInfo);

// insert DB
if (equals(addressInfo.getSigungu(), sigungu)) {
loadedDataCount++;
collectingBoxRepository.save(addressInfo.toCollectingBox(dongInfoRepository));
// insert DB
if (addressInfo != null && addressInfo.isSigunguEquals(sigungu)) {
collectingBoxRepository.save(addressInfo.toCollectingBox(dongInfoRepository));
loadedDataCount++;
}
}
}

return loadedDataCount;
}

@Transactional
public long loadCsvPublicData(LoadCsvPublicDataRequest request) {
String fileName = CSV_FILE_PATH + request.getFileName();
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package contest.collectingbox.module.publicdata;
package contest.collectingbox.module.publicdata.domain;

import contest.collectingbox.module.collectingbox.domain.Tag;
import contest.collectingbox.module.publicdata.dto.AddressInfoDto;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.json.JSONObject;

import static contest.collectingbox.module.publicdata.AddressInfoDto.*;
import static contest.collectingbox.module.publicdata.dto.AddressInfoDto.*;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class AddressInfoMapper {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package contest.collectingbox.module.publicdata;
package contest.collectingbox.module.publicdata.domain;

import contest.collectingbox.module.collectingbox.domain.Tag;
import contest.collectingbox.module.publicdata.dto.AddressInfoDto;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONArray;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package contest.collectingbox.module.publicdata.domain;

import contest.collectingbox.global.exception.CollectingBoxException;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import static contest.collectingbox.global.exception.ErrorCode.UNEXPECTED_ERROR_EXTERNAL_API;

@Component
public class OpenDataApiManager {

private static final String API_URL = "https://api.odcloud.kr/api%s?serviceKey=%s&perPage=%d";

@Value("${public-data.api.key}")
private String apiKey;

public int fetchTotalCount(String callAddress) {
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.getForEntity(getUrl(callAddress, 1), String.class);
if (isError(response)) {
throw new CollectingBoxException(UNEXPECTED_ERROR_EXTERNAL_API);
}
return new JSONObject(response.getBody()).getInt("totalCount");
}

public JSONArray fetchOpenData(String callAddress, int perPage) {
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.getForEntity(getUrl(callAddress, perPage), String.class);
if (isError(response)) {
throw new CollectingBoxException(UNEXPECTED_ERROR_EXTERNAL_API);
}
return new JSONObject(response.getBody()).getJSONArray("data");
}

private String getUrl(String callAddress, int perPage) {
return String.format(API_URL, callAddress, apiKey, perPage);
}

private boolean isError(ResponseEntity<String> response) {
return response.getStatusCode().isError();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package contest.collectingbox.module.publicdata;
package contest.collectingbox.module.publicdata.domain;

import contest.collectingbox.module.collectingbox.domain.Tag;
import jakarta.persistence.*;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package contest.collectingbox.module.publicdata;
package contest.collectingbox.module.publicdata.domain;

import org.springframework.data.jpa.repository.JpaRepository;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,45 @@
package contest.collectingbox.module.publicdata;

import static contest.collectingbox.module.collectingbox.domain.Tag.*;
package contest.collectingbox.module.publicdata.domain;

import contest.collectingbox.module.collectingbox.domain.Tag;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONObject;
import org.springframework.stereotype.Component;

import java.util.Optional;
import java.util.Set;

import static contest.collectingbox.module.collectingbox.domain.Tag.TRASH;

@Slf4j
@Component
public class PublicDataExtract {

private static final String[] KEYWORDS = {"도로", "지번", "주소", "소재지", "위치", "장소"};

public String extractQuery(JSONObject jsonObject, String sigungu, Tag tag) {
public Optional<String> extractQuery(JSONObject jsonObject, String sigungu, Tag tag) {
Optional<String> specialValue = extractSpecialCaseValue(jsonObject, sigungu, tag);
if (specialValue.isPresent()) {
return specialValue.get();
return specialValue;
}

Set<String> keySet = jsonObject.keySet();
for (String keyword : KEYWORDS) {
for (String key : keySet) {
if (key.contains(keyword) && !jsonObject.isNull(key)) {
return jsonObject.get(key).toString();
return Optional.ofNullable(jsonObject.get(key).toString());
}
}
}

log.warn("Not contains anything in {}", jsonObject);
return null;
return Optional.empty();
}

private Optional<String> extractSpecialCaseValue(JSONObject jsonObject, String sigungu, Tag tag) {
if (sigungu.equals("강북구") && tag == TRASH) {
return Optional.of(jsonObject.optString("세부 위치"));
}
return Optional.empty();
}

public int extractCsvQueryIndex(String[] columnNames, String sigungu, Tag tag) {
Expand All @@ -52,13 +59,6 @@ public int extractCsvQueryIndex(String[] columnNames, String sigungu, Tag tag) {
return -1;
}

private Optional<String> extractSpecialCaseValue(JSONObject jsonObject, String sigungu, Tag tag) {
if (sigungu.equals("강북구") && tag == TRASH) {
return Optional.of(jsonObject.optString("세부 위치"));
}
return Optional.empty();
}

private int extractSpecialCaseIndex(String sigungu, Tag tag) {
if (sigungu.equals("구로구") && tag == TRASH) {
return 1; // 소재지가로명주소
Expand Down
Loading
Loading