Skip to content

Commit

Permalink
Merge pull request #267 from techeer-sv/BE/#261
Browse files Browse the repository at this point in the history
Be/#261 채용공고 목록 조회 기능 구현
  • Loading branch information
baekhangyeol authored Oct 10, 2023
2 parents 17182de + f3c591c commit d22b165
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 204 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
package com.graphy.backend.domain.job.controller;

import com.graphy.backend.domain.job.dto.response.GetJobResponse;
import com.graphy.backend.domain.job.service.JobService;
import com.graphy.backend.global.common.PageRequest;
import com.graphy.backend.global.error.ErrorCode;
import com.graphy.backend.global.error.exception.EmptyResultException;
import com.graphy.backend.global.result.ResultCode;
import com.graphy.backend.global.result.ResultResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@Tag(name = "EmploymentController", description = "신규 채용 공고 API")
Expand All @@ -15,18 +23,13 @@
public class JobController {
private final JobService jobService;

@Operation(summary = "Save JobInfo", description = "신규 공고 저장")
@Scheduled(cron = "0 0 0 */3 * *")
@PostMapping
public void save(){
jobService.save();
}

@Operation(summary = "findJobList", description = "채용공고 조회")
@GetMapping
public ResponseEntity<ResultResponse> jobList(PageRequest pageRequest) {
Pageable pageable = pageRequest.jobOf();
List<GetJobResponse> result = jobService.findJobList(pageable);
if (result.isEmpty()) throw new EmptyResultException(ErrorCode.JOB_DELETED_OR_NOT_EXIST);

@Operation(summary = "Delete Job", description = "만료일이 지난 공고 삭제")
@Scheduled(cron = "0 0 0 * * *")
@DeleteMapping
public void deleteExpiredJobs(){
jobService.deleteExpiredJobs();
return ResponseEntity.ok(ResultResponse.of(ResultCode.JOB_PAGING_GET_SUCCESS, result));
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.graphy.backend.domain.job.dto.response;

import com.graphy.backend.domain.job.domain.Job;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.domain.Page;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class GetJobResponse {

private Long id;

private String companyName;

private String title;

private String url;

private LocalDateTime expirationDate;

public static GetJobResponse from(Job job) {
return GetJobResponse.builder()
.id(job.getId())
.companyName(job.getCompanyName())
.title(job.getTitle())
.url(job.getUrl())
.expirationDate(job.getExpirationDate())
.build();
}

public static Page<GetJobResponse> listOf(Page<Job> jobs) {
return jobs.map(GetJobResponse::from);
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package com.graphy.backend.domain.job.repository;

import com.graphy.backend.domain.job.domain.Job;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;

import java.time.LocalDateTime;

public interface JobRepository extends JpaRepository<Job, Long> {
@Modifying
@Query("DELETE FROM Job j WHERE j.expirationDate < :today")
void deleteAllExpiredSince(LocalDateTime today);

@Override
Page<Job> findAll(Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -1,106 +1,23 @@
package com.graphy.backend.domain.job.service;

import com.graphy.backend.domain.job.domain.Job;
import com.graphy.backend.domain.job.dto.JobDto;
import com.graphy.backend.domain.job.dto.response.GetJobResponse;
import com.graphy.backend.domain.job.repository.JobRepository;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.json.JSONObject;
import org.json.JSONArray;
import org.springframework.transaction.annotation.Transactional;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;


@Service
@Transactional
@RequiredArgsConstructor
public class JobService {
@Value("${job.key}")
String accessKey;
private final JobRepository jobRepository;


public void save() {
StringBuffer response = new StringBuffer();

try {
String text = URLEncoder.encode("", "UTF-8");
String apiURL = "https://oapi.saramin.co.kr/job-search?access-key=" + accessKey + "&bbs_gb=0&job_type=&edu_lv=&job_mid_cd=2";

URL url = new URL(apiURL);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setRequestProperty("Accept", "application/json");

int responseCode = con.getResponseCode();
BufferedReader br;
if (responseCode == 200) { // 정상 호출
br = new BufferedReader(new InputStreamReader(con.getInputStream()));
} else { // 에러 발생
br = new BufferedReader(new InputStreamReader(con.getErrorStream()));
}

String inputLine;
while ((inputLine = br.readLine()) != null) {
response.append(inputLine);
}
br.close();
} catch (Exception e) {
System.out.println(e);
}

saveJobInfo(response.toString());
System.out.println(response);
}

private void saveJobInfo(String response) {

JSONObject jsonObject = new JSONObject(response);

JSONArray jobsArray = jsonObject.getJSONObject("jobs").getJSONArray("job");

for (int i = 0; i < jobsArray.length(); i++) {
JSONObject jobObject = jobsArray.getJSONObject(i);

// 공고 ID
Long jobId = jobObject.getLong("id");

// 회사 이름
String companyName = jobObject.getJSONObject("company")
.getJSONObject("detail")
.getString("name");

// 공고 제목
String jobTitle = jobObject.getJSONObject("position")
.getString("title");

// URL
String companyInfoURL = jobObject.getJSONObject("company")
.getJSONObject("detail")
.getString("href");

// 만료일
long expirationTimestampLong = jobObject.getLong("expiration-timestamp");
LocalDateTime expirationTimestamp = LocalDateTime.ofInstant(
Instant.ofEpochSecond(expirationTimestampLong),
ZoneId.systemDefault());

JobDto.CreateJobInfoRequest dto = new JobDto.CreateJobInfoRequest(jobId, companyName, jobTitle, companyInfoURL, expirationTimestamp);
jobRepository.save(dto.toEntity());
}
}


public void deleteExpiredJobs() {
jobRepository.deleteAllExpiredSince(LocalDateTime.now());
public List<GetJobResponse> findJobList(Pageable pageable) {
Page<Job> jobs = jobRepository.findAll(pageable);
return GetJobResponse.listOf(jobs).getContent();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,11 @@ public org.springframework.data.domain.PageRequest of() {
}
return org.springframework.data.domain.PageRequest.of(page - 1, size, direction, "createdAt");
}

public org.springframework.data.domain.PageRequest jobOf() {
if (size <= 0) {
throw new BusinessException(INPUT_INVALID_VALUE);
}
return org.springframework.data.domain.PageRequest.of(page - 1, size, direction, "expirationDate");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@ public enum ErrorCode {
MESSAGE_NOT_EXIST(HttpStatus.NOT_FOUND, "MSG001", "존재하지 않는 메세지"),

// Notification
SEND_EMAIL_FAIL(HttpStatus.INTERNAL_SERVER_ERROR, "EM001", "이메일 전송 실패");
SEND_EMAIL_FAIL(HttpStatus.INTERNAL_SERVER_ERROR, "EM001", "이메일 전송 실패"),

// Job
JOB_DELETED_OR_NOT_EXIST(HttpStatus.NOT_FOUND, "J001", "이미 삭제되거나 존재하지 않는 채용공고");
private final HttpStatus status;
private final String errorCode;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ public enum ResultCode {
MESSAGE_PAGING_GET_SUCCESS("MSG003", "쪽지 페이징 조회 성공"),

// Notification
NOTIFICATION_PAGING_GET_SUCCESS("N001", "알림 페이징 조회 성공");
NOTIFICATION_PAGING_GET_SUCCESS("N001", "알림 페이징 조회 성공"),

// Job
JOB_PAGING_GET_SUCCESS("J001", "채용공고 페이징 조회 성공");

private final String code;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
import com.graphy.backend.domain.job.service.JobService;
import com.graphy.backend.test.MockApiTest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
Expand All @@ -14,13 +12,6 @@
import org.springframework.restdocs.RestDocumentationExtension;
import org.springframework.web.context.WebApplicationContext;

import static org.mockito.Mockito.*;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(JobController.class)
@ExtendWith(RestDocumentationExtension.class)
Expand All @@ -34,29 +25,4 @@ class JobControllerTest extends MockApiTest {
public void setup(RestDocumentationContextProvider provider) {
this.mvc = buildMockMvc(context, provider);
}


@Test
@DisplayName("공고를 저장한다.")
void saveJobInfo() throws Exception {
doNothing().when(jobService).save();
mvc.perform(post("/api/v1/jobs"))
.andExpect(status().isOk())
.andDo(document("JobInfo-Save",
preprocessResponse(prettyPrint()))
);
verify(jobService, times(1)).save();
}

@Test
@DisplayName("공고를 삭제한다.")
public void deleteJobInfo() throws Exception {
doNothing().when(jobService).deleteExpiredJobs();
mvc.perform(delete("/api/v1/jobs"))
.andExpect(status().isOk())
.andDo(document("JobInfo-Delete",
preprocessResponse(prettyPrint()))
);
verify(jobService, times(1)).deleteExpiredJobs();
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,12 @@
package com.graphy.backend.domain.job.service;

import com.graphy.backend.domain.job.domain.Job;
import com.graphy.backend.domain.job.repository.JobRepository;
import com.graphy.backend.test.MockApiTest;
import com.graphy.backend.test.MockTest;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.time.LocalDateTime;

import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
public class JobServiceTest extends MockTest {
@InjectMocks
Expand All @@ -28,14 +15,4 @@ public class JobServiceTest extends MockTest {
@Mock
JobRepository jobRepository;


@Test
@DisplayName("공고가 삭제된다.")
public void saveTest() throws Exception {
doNothing().when(jobRepository).deleteAllExpiredSince(any(LocalDateTime.class));

jobService.deleteExpiredJobs();

verify(jobRepository, times(1)).deleteAllExpiredSince(any(LocalDateTime.class));
}
}

0 comments on commit d22b165

Please sign in to comment.