diff --git a/src/main/java/darkoverload/itzip/ItzipApplication.java b/src/main/java/darkoverload/itzip/ItzipApplication.java index 07140229..74b6c440 100644 --- a/src/main/java/darkoverload/itzip/ItzipApplication.java +++ b/src/main/java/darkoverload/itzip/ItzipApplication.java @@ -9,7 +9,9 @@ @EnableScheduling @SpringBootApplication public class ItzipApplication { + public static void main(String[] args) { SpringApplication.run(ItzipApplication.class, args); } + } \ No newline at end of file diff --git a/src/main/java/darkoverload/itzip/feature/job/controller/JobInfoController.java b/src/main/java/darkoverload/itzip/feature/job/controller/JobInfoController.java index 5fab6049..f126603f 100644 --- a/src/main/java/darkoverload/itzip/feature/job/controller/JobInfoController.java +++ b/src/main/java/darkoverload/itzip/feature/job/controller/JobInfoController.java @@ -21,7 +21,6 @@ import org.springframework.web.bind.annotation.*; - @Slf4j @RestController @RequestMapping("/job-info") @@ -54,5 +53,4 @@ public String scrapJobInfo(@SwaggerRequestBody(description = "채용정보 스 return jobInfoService.jobInfoScrap(request); } - } diff --git a/src/main/java/darkoverload/itzip/feature/job/repository/custom/CustomJobInfoScrapRepository.java b/src/main/java/darkoverload/itzip/feature/job/repository/custom/CustomJobInfoScrapRepository.java index 79d31bd7..878d35f9 100644 --- a/src/main/java/darkoverload/itzip/feature/job/repository/custom/CustomJobInfoScrapRepository.java +++ b/src/main/java/darkoverload/itzip/feature/job/repository/custom/CustomJobInfoScrapRepository.java @@ -2,8 +2,12 @@ import darkoverload.itzip.feature.job.entity.JobInfoScrapEntity; +import java.util.List; import java.util.Optional; public interface CustomJobInfoScrapRepository { + void bulkDeleteByPositionIds(List positionIds); + Optional findByJobInfoId(Long id,String email); + } diff --git a/src/main/java/darkoverload/itzip/feature/job/repository/custom/CustomJobInfoScrapRepositoryImpl.java b/src/main/java/darkoverload/itzip/feature/job/repository/custom/CustomJobInfoScrapRepositoryImpl.java index 6a00a18a..9c3aca53 100644 --- a/src/main/java/darkoverload/itzip/feature/job/repository/custom/CustomJobInfoScrapRepositoryImpl.java +++ b/src/main/java/darkoverload/itzip/feature/job/repository/custom/CustomJobInfoScrapRepositoryImpl.java @@ -3,32 +3,37 @@ import com.querydsl.jpa.impl.JPAQueryFactory; import darkoverload.itzip.feature.job.entity.JobInfoScrapEntity; -import darkoverload.itzip.feature.job.entity.QJobInfoEntity; import darkoverload.itzip.feature.job.entity.QJobInfoScrapEntity; -import darkoverload.itzip.feature.user.entity.QUserEntity; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; +import java.util.List; import java.util.Optional; @Repository @RequiredArgsConstructor -public class CustomJobInfoScrapRepositoryImpl implements CustomJobInfoScrapRepository{ +public class CustomJobInfoScrapRepositoryImpl implements CustomJobInfoScrapRepository { private final JPAQueryFactory queryFactory; private final QJobInfoScrapEntity jobInfoScrapEntity = QJobInfoScrapEntity.jobInfoScrapEntity; + @Override + public void bulkDeleteByPositionIds(List positionIds) { + queryFactory.delete(jobInfoScrapEntity) + .where(jobInfoScrapEntity.jobInfo.positionId.in(positionIds)) + .execute(); + } + @Override public Optional findByJobInfoId(Long id, String email) { - JobInfoScrapEntity scrap = queryFactory.selectFrom(jobInfoScrapEntity) - .where( - (jobInfoScrapEntity.jobInfo.id.eq(id)) - .and(jobInfoScrapEntity.user.email.eq(email)) + JobInfoScrapEntity scrap = queryFactory.selectFrom(jobInfoScrapEntity) + .where( + (jobInfoScrapEntity.jobInfo.id.eq(id)) + .and(jobInfoScrapEntity.user.email.eq(email)) ).fetchOne(); return Optional.ofNullable(scrap); } - } diff --git a/src/main/java/darkoverload/itzip/feature/job/scheduler/JobInfoScheduler.java b/src/main/java/darkoverload/itzip/feature/job/scheduler/JobInfoScheduler.java index 5a64a540..fcd97a6a 100644 --- a/src/main/java/darkoverload/itzip/feature/job/scheduler/JobInfoScheduler.java +++ b/src/main/java/darkoverload/itzip/feature/job/scheduler/JobInfoScheduler.java @@ -15,10 +15,10 @@ @Component @RequiredArgsConstructor public class JobInfoScheduler { + private static final String JOB_INFO_SCHEDULER_CON = "1 30 0 * * *"; private final JobInfoRepository jobInfoRepository; private final JobInfoConnectService service; - /** * Saramin API에서 최신 JobInfo 데이터를 가져와 데이터베이스와 비교하여 * 삭제, 업데이트, 삽입 작업을 수행하는 메서드입니다. 이 메서드는 매일 00:30에 실행됩니다. @@ -28,8 +28,9 @@ public class JobInfoScheduler { * * @Scheduled(cron = "1 30 0 * * *") 크론 표현식을 사용하여 매일 01:30에 실행됩니다. */ + @Transactional - @Scheduled(cron = "1 30 0 * * *") + @Scheduled(cron = JOB_INFO_SCHEDULER_CON) public void jobInfoConnectApi() { // 데이터베이스에서 모든 JobInfo 데이터를 조회하고, 도메인 객체 리스트로 변환 List dbList = jobInfoRepository.findAll().stream().map(JobInfoEntity::convertToDomain).toList(); diff --git a/src/main/java/darkoverload/itzip/feature/job/service/connect/JobInfoConnectServiceImpl.java b/src/main/java/darkoverload/itzip/feature/job/service/connect/JobInfoConnectServiceImpl.java index bee08a3c..0c405064 100644 --- a/src/main/java/darkoverload/itzip/feature/job/service/connect/JobInfoConnectServiceImpl.java +++ b/src/main/java/darkoverload/itzip/feature/job/service/connect/JobInfoConnectServiceImpl.java @@ -4,6 +4,7 @@ import darkoverload.itzip.feature.job.domain.JobInfo; import darkoverload.itzip.feature.job.entity.JobInfoEntity; import darkoverload.itzip.feature.job.repository.JobInfoRepository; +import darkoverload.itzip.feature.job.repository.JobInfoScrapRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -17,6 +18,7 @@ public class JobInfoConnectServiceImpl implements JobInfoConnectService { private final JobInfoRepository jobInfoRepository; + private final JobInfoScrapRepository jobInfoScrapRepository; @Value("${job.api-url}") private String jobUrl; @@ -63,7 +65,6 @@ public Long jobInfoDelete(List apiDataList, List dbList) { // dbList가 비어있을 경우, 즉시 반환하여 추가 작업을 방지 if(dbList.isEmpty()) return 0L; - List deleteList = makeDeleteList(apiDataList, dbList); @@ -71,6 +72,7 @@ public Long jobInfoDelete(List apiDataList, List dbList) { // batchSize를 설정하여 500개씩 나누어 삭제 작업을 수행 (대량 삭제 시 성능 최적화) for(int i=0; i < deleteList.size(); i+= 500){ List batch = deleteList.subList(i, Math.min(i + 500, deleteList.size())); + jobInfoScrapRepository.bulkDeleteByPositionIds(batch); totalDeletedCount += jobInfoRepository.bulkDeleteByPositionIds(batch); } @@ -169,8 +171,6 @@ private boolean checkNotEquals(JobInfo dbJobInfo, JobInfo apiJobInfo) { || !dbJobInfo.getIndustryName().equals(apiJobInfo.getIndustryName()) // 산업 이름 비교 || !dbJobInfo.getLocationCode().equals(apiJobInfo.getLocationCode()) // 위치 코드 비교 || !dbJobInfo.getLocationName().equals(apiJobInfo.getLocationName()) // 위치 이름 비교 - || !dbJobInfo.getJobTypeCode().equals(apiJobInfo.getJobTypeCode()) // 직무 유형 코드 비교 - || !dbJobInfo.getJobTypeName().equals(apiJobInfo.getJobTypeName()) // 직무 유형 이름 비교 || !dbJobInfo.getJobMidCode().equals(apiJobInfo.getJobMidCode()) // 중간 직무 코드 비교 || !dbJobInfo.getJobMidName().equals(apiJobInfo.getJobMidName()) // 중간 직무 이름 비교 || !dbJobInfo.getJobName().equals(apiJobInfo.getJobName()) // 직무 이름 비교 diff --git a/src/main/java/darkoverload/itzip/feature/resume/controller/ResumeController.java b/src/main/java/darkoverload/itzip/feature/resume/controller/ResumeController.java index 14a9d5eb..eb45d40f 100644 --- a/src/main/java/darkoverload/itzip/feature/resume/controller/ResumeController.java +++ b/src/main/java/darkoverload/itzip/feature/resume/controller/ResumeController.java @@ -49,8 +49,6 @@ public UpdateResumeResponse updateResume(@SwaggerRequestBody(description = "이 schema = @Schema(implementation = UpdateResumeRequest.class) )) @Valid @RequestBody UpdateResumeRequest request) { - - return service.update(request); } diff --git a/src/main/java/darkoverload/itzip/feature/resume/entity/AchievementEntity.java b/src/main/java/darkoverload/itzip/feature/resume/entity/AchievementEntity.java index c1ff19c6..f9d2e0ed 100644 --- a/src/main/java/darkoverload/itzip/feature/resume/entity/AchievementEntity.java +++ b/src/main/java/darkoverload/itzip/feature/resume/entity/AchievementEntity.java @@ -34,7 +34,6 @@ public class AchievementEntity extends AuditingFields { private String content; - public Achievement convertToDomain(){ return Achievement.builder() .achievementId(this.id) @@ -44,4 +43,5 @@ public Achievement convertToDomain(){ .content(this.content) .build(); } + } diff --git a/src/main/java/darkoverload/itzip/feature/resume/service/resume/ResumeServiceImpl.java b/src/main/java/darkoverload/itzip/feature/resume/service/resume/ResumeServiceImpl.java index 1201d427..7de07e8f 100644 --- a/src/main/java/darkoverload/itzip/feature/resume/service/resume/ResumeServiceImpl.java +++ b/src/main/java/darkoverload/itzip/feature/resume/service/resume/ResumeServiceImpl.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; @Slf4j diff --git a/src/main/java/darkoverload/itzip/global/config/security/SecurityConfig.java b/src/main/java/darkoverload/itzip/global/config/security/SecurityConfig.java index e558bf36..2698d649 100644 --- a/src/main/java/darkoverload/itzip/global/config/security/SecurityConfig.java +++ b/src/main/java/darkoverload/itzip/global/config/security/SecurityConfig.java @@ -61,7 +61,7 @@ public class SecurityConfig { // 직업 정보 임시 허용 "/job-info", - "/job-info/scrap", + "/job-info/scrap" }; // 비로그인 유저 허용 페이지 diff --git a/src/test/java/darkoverload/itzip/feature/job/service/connect/JobInfoScheduleTest.java b/src/test/java/darkoverload/itzip/feature/job/service/connect/JobInfoScheduleTest.java new file mode 100644 index 00000000..6f968535 --- /dev/null +++ b/src/test/java/darkoverload/itzip/feature/job/service/connect/JobInfoScheduleTest.java @@ -0,0 +1,50 @@ +package darkoverload.itzip.feature.job.service.connect; + +import darkoverload.itzip.feature.job.domain.JobInfo; +import darkoverload.itzip.feature.job.entity.JobInfoEntity; +import darkoverload.itzip.feature.job.repository.JobInfoRepository; +import darkoverload.itzip.feature.job.service.JobInfoService; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import java.util.List; + +@Slf4j +@SpringBootTest +@ActiveProfiles(profiles = "test") +public class JobInfoScheduleTest { + + @Autowired + private JobInfoRepository jobInfoRepository; + + @Autowired + private JobInfoConnectService service; + + @Test + void 사람인_테스트() { + // 데이터베이스에서 모든 JobInfo 데이터를 조회하고, 도메인 객체 리스트로 변환 + List dbList = jobInfoRepository.findAll().stream().map(JobInfoEntity::convertToDomain).toList(); + + // Saramin API를 호출하여 최신 JobInfo 데이터를 가져옴 + List apiDataList = service.jobInfoConnect(); + + // 데이터베이스에 있는 JobInfo 데이터를 API 데이터와 비교하여 삭제 작업 수행 + Long deletedCount = service.jobInfoDelete(apiDataList, dbList); + + log.info("==== Saramin API Data deleteCount :: {} ====", deletedCount); + + // 데이터베이스에 있는 JobInfo 데이터를 API 데이터와 비교하여 업데이트 작업 수행 + Long updatedCount = service.jobInfoUpdate(apiDataList, dbList); + + log.info("==== Saramin API Data updateCount :: {} ====", updatedCount); + + // API 데이터 중에서 데이터베이스에 없는 데이터를 삽입하는 작업 수행 + Long savedCount = service.jobInfoSave(apiDataList, dbList); + + log.info("==== Saramin API Data updateCount :: {} ====", savedCount); + } + +} diff --git a/src/test/java/darkoverload/itzip/feature/resume/mock/FakeLanguageRepository.java b/src/test/java/darkoverload/itzip/feature/resume/mock/FakeLanguageRepository.java index 0f86ae48..903c1780 100644 --- a/src/test/java/darkoverload/itzip/feature/resume/mock/FakeLanguageRepository.java +++ b/src/test/java/darkoverload/itzip/feature/resume/mock/FakeLanguageRepository.java @@ -80,4 +80,5 @@ private List getLanguageUpdateIds(List languages) { private List getLanguageIds(Long resumeId) { return findAllByResumeId(resumeId).stream().map(Language::getLanguageId).toList(); } + }