diff --git a/dodam-application/dodam-rest-api/build.gradle b/dodam-application/dodam-rest-api/build.gradle index 377938e2..365ca8e4 100644 --- a/dodam-application/dodam-rest-api/build.gradle +++ b/dodam-application/dodam-rest-api/build.gradle @@ -11,10 +11,13 @@ dependencies { implementation project(':dodam-in-system-available:dodam-melon-chart-client') implementation project(':dodam-in-system-available:dodam-ncp-object-storage-client') implementation project(':dodam-in-system-available:dodam-neis-meal-client') + implementation project(':dodam-in-system-available:dodam-neis-schedule-client') + implementation project(':dodam-in-system-available:dodam-neis-client-core') implementation project(':dodam-in-system-available:dodam-token-client') implementation project(':dodam-in-system-available:dodam-youtube-video-client') implementation project(':dodam-in-system-available:dodam-firebase-client') + implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-validation' diff --git a/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/schedule/application/ScheduleUseCase.java b/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/schedule/application/ScheduleUseCase.java index b7ea83c6..5bb77412 100644 --- a/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/schedule/application/ScheduleUseCase.java +++ b/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/schedule/application/ScheduleUseCase.java @@ -4,6 +4,8 @@ import b1nd.dodam.domain.rds.schedule.entity.Schedule; import b1nd.dodam.domain.rds.schedule.enumeration.TargetGrade; import b1nd.dodam.domain.rds.schedule.service.ScheduleService; +import b1nd.dodam.neis.schedule.client.NeisScheduleClient; +import b1nd.dodam.neis.schedule.client.data.NeisSchedule; import b1nd.dodam.restapi.schedule.application.data.req.ScheduleReq; import b1nd.dodam.restapi.schedule.application.data.res.ScheduleRes; import b1nd.dodam.restapi.support.data.Response; @@ -24,6 +26,7 @@ public class ScheduleUseCase { private final ScheduleService scheduleService; + private final NeisScheduleClient neisScheduleClient; @Transactional(rollbackFor = Exception.class) public Response create(ScheduleReq req) { @@ -66,4 +69,24 @@ public ResponseData> getByDate(int year, int month, int day) { return ResponseData.ok("해당 날짜의 일정 조회 성공", ScheduleRes.of(scheduleService.getByDate(LocalDate.of(year, month, day)))); } + @Transactional(rollbackFor = Exception.class) + public Response createScheduleByNeis(LocalDate startDate, LocalDate endDate){ + List neisSchedule = neisScheduleClient.getSchedules(startDate, endDate); + List schedules = neisSchedule + .parallelStream() + .map(this::scheduleBuilderForNeis) + .toList(); + scheduleService.saveAll(schedules); + return ResponseData.ok("neis로 일정 저장 성공"); + } + + private Schedule scheduleBuilderForNeis(NeisSchedule neisSchedule){ + return Schedule.builder() + .name(neisSchedule.eventName()) + .startDate(neisSchedule.startDate()) + .endDate(neisSchedule.endDate()) + .targetGrades(neisSchedule.grades().stream().map(TargetGrade::of).collect(Collectors.toSet())) + .build(); + } + } diff --git a/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/schedule/presentation/ScheduleController.java b/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/schedule/presentation/ScheduleController.java index 6c58ae6a..e08dc784 100644 --- a/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/schedule/presentation/ScheduleController.java +++ b/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/schedule/presentation/ScheduleController.java @@ -63,4 +63,12 @@ public ResponseData> getAllOrderByIdDesc(@RequestParam int pag return useCase.getAllOrderByIdDesc(page, size); } + @PostMapping("/neis") + public Response createByNeis( + @RequestParam LocalDate startDate, + @RequestParam LocalDate endDate + ){ + return useCase.createScheduleByNeis(startDate, endDate); + } + } diff --git a/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/schedule/presentation/ScheduleScheduler.java b/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/schedule/presentation/ScheduleScheduler.java new file mode 100644 index 00000000..90a0bc36 --- /dev/null +++ b/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/schedule/presentation/ScheduleScheduler.java @@ -0,0 +1,24 @@ +package b1nd.dodam.restapi.schedule.presentation; + +import b1nd.dodam.restapi.schedule.application.ScheduleUseCase; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; + +@Slf4j +@Component +@RequiredArgsConstructor +public class ScheduleScheduler { + private final ScheduleUseCase scheduleUseCase; + + @Scheduled(cron = "0 0 7 1 * ?", zone = "Asia/Seoul") + public void flushAllScheduleData() { + LocalDate now = LocalDate.now(); + LocalDate startDate = LocalDate.of(now.getYear(), 3, 2); + LocalDate endDate = LocalDate.of(now.getYear()+1, 3, 1); + scheduleUseCase.createScheduleByNeis(startDate, endDate); + } +} diff --git a/dodam-application/dodam-rest-api/src/main/resources/application.yml b/dodam-application/dodam-rest-api/src/main/resources/application.yml index c69aefdc..96ee1fe3 100644 --- a/dodam-application/dodam-rest-api/src/main/resources/application.yml +++ b/dodam-application/dodam-rest-api/src/main/resources/application.yml @@ -68,6 +68,10 @@ app: neis: apiKey: ${NEIS_KEY} url: ${NEIS_URL} + meal: + meal-endpoint: ${NEIS_MEAL_ENDPOINT} + schedule: + schedule-endpoint: ${NEIS_SCHEDULE_ENDPOINT} codenary: url: ${CODENARY_URL} melon: diff --git a/dodam-in-system-available/dodam-neis-client-core/build.gradle b/dodam-in-system-available/dodam-neis-client-core/build.gradle new file mode 100644 index 00000000..ce18fc23 --- /dev/null +++ b/dodam-in-system-available/dodam-neis-client-core/build.gradle @@ -0,0 +1,7 @@ +dependencies { + implementation 'org.springframework:spring-context:6.0.6' + implementation 'org.springframework:spring-web:6.0.6' + implementation 'org.springframework:spring-tx:6.0.6' + + implementation 'org.springframework.boot:spring-boot:3.0.4' +} diff --git a/dodam-in-system-available/dodam-neis-client-core/gradle/wrapper/gradle-wrapper.jar b/dodam-in-system-available/dodam-neis-client-core/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..a4b76b95 Binary files /dev/null and b/dodam-in-system-available/dodam-neis-client-core/gradle/wrapper/gradle-wrapper.jar differ diff --git a/dodam-in-system-available/dodam-neis-client-core/gradle/wrapper/gradle-wrapper.properties b/dodam-in-system-available/dodam-neis-client-core/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..0aaefbca --- /dev/null +++ b/dodam-in-system-available/dodam-neis-client-core/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/dodam-in-system-available/dodam-neis-meal-client/src/main/java/b1nd/dodam/neis/meal/client/properties/NeisProperties.java b/dodam-in-system-available/dodam-neis-client-core/src/main/java/b1nd/dodam/neis/client/core/NeisCoreProperties.java similarity index 79% rename from dodam-in-system-available/dodam-neis-meal-client/src/main/java/b1nd/dodam/neis/meal/client/properties/NeisProperties.java rename to dodam-in-system-available/dodam-neis-client-core/src/main/java/b1nd/dodam/neis/client/core/NeisCoreProperties.java index 6034aa83..d8ba2ecb 100644 --- a/dodam-in-system-available/dodam-neis-meal-client/src/main/java/b1nd/dodam/neis/meal/client/properties/NeisProperties.java +++ b/dodam-in-system-available/dodam-neis-client-core/src/main/java/b1nd/dodam/neis/client/core/NeisCoreProperties.java @@ -1,4 +1,4 @@ -package b1nd.dodam.neis.meal.client.properties; +package b1nd.dodam.neis.client.core; import lombok.Getter; import lombok.Setter; @@ -9,7 +9,7 @@ @Setter @Configuration @ConfigurationProperties("app.neis") -public class NeisProperties { +public class NeisCoreProperties { private String apiKey; private String url; diff --git a/dodam-in-system-available/dodam-neis-client-core/src/test/java/com/b1nd/dodam/neis/client/core/package-info.java b/dodam-in-system-available/dodam-neis-client-core/src/test/java/com/b1nd/dodam/neis/client/core/package-info.java new file mode 100644 index 00000000..f41a4b5f --- /dev/null +++ b/dodam-in-system-available/dodam-neis-client-core/src/test/java/com/b1nd/dodam/neis/client/core/package-info.java @@ -0,0 +1 @@ +package com.b1nd.dodam.neis.client.core; \ No newline at end of file diff --git a/dodam-in-system-available/dodam-neis-meal-client/build.gradle b/dodam-in-system-available/dodam-neis-meal-client/build.gradle index 68db85e9..8001211a 100644 --- a/dodam-in-system-available/dodam-neis-meal-client/build.gradle +++ b/dodam-in-system-available/dodam-neis-meal-client/build.gradle @@ -1,5 +1,6 @@ dependencies { implementation project(':dodam-in-system-available:dodam-client-core') + implementation project(':dodam-in-system-available:dodam-neis-client-core') implementation 'org.springframework:spring-context:6.0.6' implementation 'org.springframework:spring-web:6.0.6' diff --git a/dodam-in-system-available/dodam-neis-meal-client/src/main/java/b1nd/dodam/neis/meal/client/NeisMealClient.java b/dodam-in-system-available/dodam-neis-meal-client/src/main/java/b1nd/dodam/neis/meal/client/NeisMealClient.java index f8f5c657..63ae5c67 100644 --- a/dodam-in-system-available/dodam-neis-meal-client/src/main/java/b1nd/dodam/neis/meal/client/NeisMealClient.java +++ b/dodam-in-system-available/dodam-neis-meal-client/src/main/java/b1nd/dodam/neis/meal/client/NeisMealClient.java @@ -4,8 +4,9 @@ import b1nd.dodam.neis.meal.client.data.Food; import b1nd.dodam.neis.meal.client.data.FoodDetail; import b1nd.dodam.neis.meal.client.data.Meal; -import b1nd.dodam.neis.meal.client.properties.NeisProperties; +import b1nd.dodam.neis.meal.client.properties.NeisMealProperties; import b1nd.dodam.neis.meal.client.support.MealConverter; +import b1nd.dodam.neis.client.core.NeisCoreProperties; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import org.json.simple.JSONArray; @@ -24,8 +25,9 @@ @RequiredArgsConstructor public class NeisMealClient { - private final NeisProperties neisProperties; + private final NeisMealProperties neisMealProperties; private final WebClientSupport webClient; + private final NeisCoreProperties neisCoreProperties; @Cacheable(value = "meal-day", key = "#year.toString().concat(-#month).concat(-#day)") public Meal getMeal(int year, int month, int day) { @@ -95,8 +97,8 @@ public List getMealOfMonth(int year, int month) { private String getByDate(String date) { return webClient.get( - UriComponentsBuilder.fromUriString(neisProperties.getUrl()) - .build(neisProperties.getApiKey(), date).toString(), + UriComponentsBuilder.fromUriString(neisCoreProperties.getUrl()+neisMealProperties.getMealEndpoint()) + .build(neisCoreProperties.getApiKey(), date).toString(), String.class ).block(); } diff --git a/dodam-in-system-available/dodam-neis-meal-client/src/main/java/b1nd/dodam/neis/meal/client/properties/NeisMealProperties.java b/dodam-in-system-available/dodam-neis-meal-client/src/main/java/b1nd/dodam/neis/meal/client/properties/NeisMealProperties.java new file mode 100644 index 00000000..d02a564b --- /dev/null +++ b/dodam-in-system-available/dodam-neis-meal-client/src/main/java/b1nd/dodam/neis/meal/client/properties/NeisMealProperties.java @@ -0,0 +1,16 @@ +package b1nd.dodam.neis.meal.client.properties; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Getter +@Setter +@Configuration +@ConfigurationProperties("app.neis.meal") +public class NeisMealProperties { + + private String mealEndpoint; + +} diff --git a/dodam-in-system-available/dodam-neis-schedule-client/build.gradle b/dodam-in-system-available/dodam-neis-schedule-client/build.gradle new file mode 100644 index 00000000..ded66828 --- /dev/null +++ b/dodam-in-system-available/dodam-neis-schedule-client/build.gradle @@ -0,0 +1,16 @@ +dependencies { + implementation project(':dodam-in-system-available:dodam-client-core') + implementation project(':dodam-in-system-available:dodam-neis-client-core') + + implementation 'org.springframework:spring-context:6.0.6' + implementation 'org.springframework:spring-web:6.0.6' + implementation 'org.springframework:spring-tx:6.0.6' + + implementation 'org.springframework.boot:spring-boot:3.0.4' + + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.14.2' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.2' + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' + + implementation 'com.googlecode.json-simple:json-simple:1.1.1' +} diff --git a/dodam-in-system-available/dodam-neis-schedule-client/src/main/java/b1nd/dodam/neis/schedule/client/NeisScheduleClient.java b/dodam-in-system-available/dodam-neis-schedule-client/src/main/java/b1nd/dodam/neis/schedule/client/NeisScheduleClient.java new file mode 100644 index 00000000..2c46818f --- /dev/null +++ b/dodam-in-system-available/dodam-neis-schedule-client/src/main/java/b1nd/dodam/neis/schedule/client/NeisScheduleClient.java @@ -0,0 +1,75 @@ +package b1nd.dodam.neis.schedule.client; + +import b1nd.dodam.client.core.WebClientSupport; +import b1nd.dodam.neis.client.core.NeisCoreProperties; +import b1nd.dodam.neis.schedule.client.data.NeisSchedule; +import b1nd.dodam.neis.schedule.client.properties.NeisScheduleProperties; +import b1nd.dodam.neis.schedule.client.util.NeisScheduleUtil; +import lombok.RequiredArgsConstructor; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.springframework.stereotype.Component; +import org.springframework.web.util.UriComponentsBuilder; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Component +@RequiredArgsConstructor +public class NeisScheduleClient { + private final WebClientSupport webClient; + private final NeisCoreProperties coreProperties; + private final NeisScheduleProperties scheduleProperties; + + public List getSchedules(LocalDate startDate, LocalDate endDate) { + String rawResponse = getRawSchedule(startDate, endDate); + JSONArray rawData = NeisScheduleUtil.getRawData(rawResponse); + List scheduleList = new ArrayList<>(); + + for (Object object : rawData) { + JSONObject scheduleData = (JSONObject) object; + + String eventName = String.valueOf(scheduleData.get("EVENT_NM")); + List grades = NeisScheduleUtil.determineGrades(scheduleData); + LocalDate date = NeisScheduleUtil.parseDate(String.valueOf(scheduleData.get("AA_YMD"))); + + scheduleList.add(new NeisSchedule(eventName, grades, date, null)); + } + + return mergeSchedules(scheduleList); + } + + private List mergeSchedules(List scheduleList) { + return scheduleList.stream() + .distinct() + .collect(Collectors.groupingBy(NeisSchedule::eventName)) + .entrySet().stream() + .map(entry -> createMergedSchedule(entry.getKey(), entry.getValue())) + .collect(Collectors.toList()); + } + + private NeisSchedule createMergedSchedule(String eventName, List schedules) { + LocalDate minDate = schedules.stream() + .map(NeisSchedule::startDate) + .min(LocalDate::compareTo) + .orElse(null); + + LocalDate maxDate = schedules.stream() + .map(NeisSchedule::startDate) + .max(LocalDate::compareTo) + .orElse(null); + + List mergedGrades = schedules.get(0).grades(); + return new NeisSchedule(eventName, mergedGrades, minDate, maxDate); + } + + private String getRawSchedule(LocalDate startDate, LocalDate endDate) { + return webClient.get( + UriComponentsBuilder.fromUriString(coreProperties.getUrl() + scheduleProperties.getScheduleEndpoint()) + .build(coreProperties.getApiKey(), startDate, endDate).toString(), + String.class + ).block(); + } +} diff --git a/dodam-in-system-available/dodam-neis-schedule-client/src/main/java/b1nd/dodam/neis/schedule/client/data/NeisSchedule.java b/dodam-in-system-available/dodam-neis-schedule-client/src/main/java/b1nd/dodam/neis/schedule/client/data/NeisSchedule.java new file mode 100644 index 00000000..a13853aa --- /dev/null +++ b/dodam-in-system-available/dodam-neis-schedule-client/src/main/java/b1nd/dodam/neis/schedule/client/data/NeisSchedule.java @@ -0,0 +1,12 @@ +package b1nd.dodam.neis.schedule.client.data; + +import java.time.LocalDate; +import java.util.List; + +public record NeisSchedule( + String eventName, + List grades, + LocalDate startDate, + LocalDate endDate +) { +} diff --git a/dodam-in-system-available/dodam-neis-schedule-client/src/main/java/b1nd/dodam/neis/schedule/client/properties/NeisScheduleProperties.java b/dodam-in-system-available/dodam-neis-schedule-client/src/main/java/b1nd/dodam/neis/schedule/client/properties/NeisScheduleProperties.java new file mode 100644 index 00000000..1a6ec99c --- /dev/null +++ b/dodam-in-system-available/dodam-neis-schedule-client/src/main/java/b1nd/dodam/neis/schedule/client/properties/NeisScheduleProperties.java @@ -0,0 +1,16 @@ +package b1nd.dodam.neis.schedule.client.properties; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Getter +@Setter +@Configuration +@ConfigurationProperties("app.neis.schedule") +public class NeisScheduleProperties { + + private String scheduleEndpoint; + +} diff --git a/dodam-in-system-available/dodam-neis-schedule-client/src/main/java/b1nd/dodam/neis/schedule/client/util/NeisScheduleUtil.java b/dodam-in-system-available/dodam-neis-schedule-client/src/main/java/b1nd/dodam/neis/schedule/client/util/NeisScheduleUtil.java new file mode 100644 index 00000000..3e893363 --- /dev/null +++ b/dodam-in-system-available/dodam-neis-schedule-client/src/main/java/b1nd/dodam/neis/schedule/client/util/NeisScheduleUtil.java @@ -0,0 +1,43 @@ +package b1nd.dodam.neis.schedule.client.util; + +import b1nd.dodam.core.exception.global.InternalServerException; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + +public class NeisScheduleUtil { + + public static List determineGrades(JSONObject scheduleData) { + List grades = new ArrayList<>(); + if ("Y".equals(scheduleData.get("ONE_GRADE_EVENT_YN"))) grades.add("1학년"); + if ("Y".equals(scheduleData.get("TW_GRADE_EVENT_YN"))) grades.add("2학년"); + if ("Y".equals(scheduleData.get("THREE_GRADE_EVENT_YN"))) grades.add("3학년"); + + if (grades.size() == 3) { + grades.clear(); + grades.add("전교생"); + } + return grades; + } + + public static LocalDate parseDate(String dateString) { + return LocalDate.parse(dateString, DateTimeFormatter.ofPattern("yyyyMMdd")); + } + + public static JSONArray getRawData(String response) { + try { + JSONParser jsonParser = new JSONParser(); + JSONObject parse = (JSONObject) jsonParser.parse(response); + JSONArray schoolScheduleInfo = (JSONArray) parse.get("SchoolSchedule"); + JSONObject scheduleInfo = (JSONObject) schoolScheduleInfo.get(1); + return (JSONArray) scheduleInfo.get("row"); + } catch (Exception e) { + throw new InternalServerException(); + } + } +} diff --git a/dodam-in-system-available/dodam-neis-schedule-client/src/test/java/com/b1nd/dodam/neis/schedule/client/package-info.java b/dodam-in-system-available/dodam-neis-schedule-client/src/test/java/com/b1nd/dodam/neis/schedule/client/package-info.java new file mode 100644 index 00000000..df295029 --- /dev/null +++ b/dodam-in-system-available/dodam-neis-schedule-client/src/test/java/com/b1nd/dodam/neis/schedule/client/package-info.java @@ -0,0 +1 @@ +package com.b1nd.dodam.neis.schedule.client; \ No newline at end of file diff --git a/dodam-system-domain/dodam-domain-rds/src/main/java/b1nd/dodam/domain/rds/schedule/service/ScheduleService.java b/dodam-system-domain/dodam-domain-rds/src/main/java/b1nd/dodam/domain/rds/schedule/service/ScheduleService.java index 4b2823ab..6ce64db1 100644 --- a/dodam-system-domain/dodam-domain-rds/src/main/java/b1nd/dodam/domain/rds/schedule/service/ScheduleService.java +++ b/dodam-system-domain/dodam-domain-rds/src/main/java/b1nd/dodam/domain/rds/schedule/service/ScheduleService.java @@ -21,6 +21,10 @@ public void save(Schedule schedule) { repository.save(schedule); } + public void saveAll(List schedules) { + repository.saveAll(schedules); + } + public void deleteById(int id) { repository.deleteById(id); } diff --git a/settings.gradle b/settings.gradle index 0594dbff..f4b00b4f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -19,6 +19,8 @@ include 'dodam-in-system-available:dodam-gabia-client' include 'dodam-in-system-available:dodam-ncp-object-storage-client' include 'dodam-in-system-available:dodam-codenary-client' include 'dodam-in-system-available:dodam-neis-meal-client' +include 'dodam-in-system-available:dodam-neis-schedule-client' +include 'dodam-in-system-available:dodam-neis-client-core' include 'dodam-in-system-available:dodam-firebase-client' //dodam-application