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

채용공고, 뉴스 복원 완료 #445

Merged
merged 1 commit into from
Aug 4, 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
@@ -0,0 +1,32 @@
package page.clab.api.domain.community.jobPosting.adapter.in.web;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import page.clab.api.domain.community.jobPosting.application.dto.response.JobPostingDetailsResponseDto;
import page.clab.api.domain.community.jobPosting.application.port.in.RetrieveJobPostingDetailsUseCase;
import page.clab.api.global.common.dto.ApiResponse;

@RestController
@RequestMapping("/api/v1/job-postings")
@RequiredArgsConstructor
@Tag(name = "Community - Job Posting", description = "커뮤니티 채용 공고")
public class JobPostingDetailsRetrievalController {

private final RetrieveJobPostingDetailsUseCase retrieveJobPostingDetailsUseCase;

@Operation(summary = "[U] 채용 공고 상세 조회", description = "ROLE_USER 이상의 권한이 필요함")
@Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_SUPER" })
@GetMapping("/{jobPostingId}")
public ApiResponse<JobPostingDetailsResponseDto> retrieveJobPostingDetails(
@PathVariable(name = "jobPostingId") Long jobPostingId
) {
JobPostingDetailsResponseDto jobPosting = retrieveJobPostingDetailsUseCase.retrieveJobPostingDetails(jobPostingId);
return ApiResponse.success(jobPosting);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package page.clab.api.domain.community.jobPosting.adapter.in.web;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import page.clab.api.domain.community.jobPosting.application.dto.response.JobPostingResponseDto;
import page.clab.api.domain.community.jobPosting.application.port.in.RetrieveJobPostingsByConditionsUseCase;
import page.clab.api.domain.community.jobPosting.domain.CareerLevel;
import page.clab.api.domain.community.jobPosting.domain.EmploymentType;
import page.clab.api.global.common.dto.ApiResponse;
import page.clab.api.global.common.dto.PagedResponseDto;
import page.clab.api.global.exception.InvalidColumnException;
import page.clab.api.global.exception.SortingArgumentException;
import page.clab.api.global.util.PageableUtils;

import java.util.List;

@RestController
@RequestMapping("/api/v1/job-postings")
@RequiredArgsConstructor
@Tag(name = "Community - Job Posting", description = "커뮤니티 채용 공고")
public class JobPostingsByConditionsRetrievalController {

private final RetrieveJobPostingsByConditionsUseCase retrieveJobPostingsByConditionsUseCase;
private final PageableUtils pageableUtils;

@Operation(summary = "[U] 채용 공고 목록 조회(공고명, 기업명, 경력, 근로 조건 기준)", description = "ROLE_USER 이상의 권한이 필요함<br>" +
"4개의 파라미터를 자유롭게 조합하여 필터링 가능<br>" +
"공고명, 기업명, 경력, 근로 조건 중 하나라도 입력하지 않으면 전체 조회됨<br>" +
"DTO의 필드명을 기준으로 정렬 가능하며, 정렬 방향은 오름차순(asc)과 내림차순(desc)이 가능함")
@Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_SUPER" })
@GetMapping("")
public ApiResponse<PagedResponseDto<JobPostingResponseDto>> retrieveJobPostingsByConditions(
@RequestParam(name = "title", required = false) String title,
@RequestParam(name = "companyName", required = false) String companyName,
@RequestParam(name = "careerLevel", required = false) CareerLevel careerLevel,
@RequestParam(name = "employmentType", required = false) EmploymentType employmentType,
@RequestParam(name = "page", defaultValue = "0") int page,
@RequestParam(name = "size", defaultValue = "20") int size,
@RequestParam(name = "sortBy", defaultValue = "createdAt") List<String> sortBy,
@RequestParam(name = "sortDirection", defaultValue = "desc") List<String> sortDirection
) throws SortingArgumentException, InvalidColumnException {
Pageable pageable = pageableUtils.createPageable(page, size, sortBy, sortDirection, JobPostingResponseDto.class);
PagedResponseDto<JobPostingResponseDto> jobPostings = retrieveJobPostingsByConditionsUseCase.retrieveJobPostings(title, companyName, careerLevel, employmentType, pageable);
return ApiResponse.success(jobPostings);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package page.clab.api.domain.community.jobPosting.adapter.out.persistence;

import org.mapstruct.Mapper;
import page.clab.api.domain.community.jobPosting.domain.JobPosting;

@Mapper(componentModel = "spring")
public interface JobPostingMapper {

JobPostingJpaEntity toJpaEntity(JobPosting domain);

JobPosting toDomain(JobPostingJpaEntity entity);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package page.clab.api.domain.community.jobPosting.adapter.out.persistence;

import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;
import page.clab.api.domain.community.jobPosting.application.port.out.RetrieveJobPostingPort;
import page.clab.api.domain.community.jobPosting.domain.CareerLevel;
import page.clab.api.domain.community.jobPosting.domain.EmploymentType;
import page.clab.api.domain.community.jobPosting.domain.JobPosting;
import page.clab.api.global.exception.NotFoundException;

@Component
@RequiredArgsConstructor
public class JobPostingPersistenceAdapter implements
RetrieveJobPostingPort {

private final JobPostingRepository repository;
private final JobPostingMapper jobPostingMapper;

@Override
public JobPosting findByIdOrThrow(Long jobPostingId) {
return repository.findById(jobPostingId)
.map(jobPostingMapper::toDomain)
.orElseThrow(() -> new NotFoundException("[JobPosting] id: " + jobPostingId + "에 해당하는 채용 공고가 존재하지 않습니다."));
}

@Override
public Page<JobPosting> findByConditions(String title, String companyName, CareerLevel careerLevel, EmploymentType employmentType, Pageable pageable) {
return repository.findByConditions(title, companyName, careerLevel, employmentType, pageable).map(jobPostingMapper::toDomain);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package page.clab.api.domain.community.jobPosting.adapter.out.persistence;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.stereotype.Repository;

@Repository
public interface JobPostingRepository extends JpaRepository<JobPostingJpaEntity, Long>, JobPostingRepositoryCustom, QuerydslPredicateExecutor<JobPostingJpaEntity> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package page.clab.api.domain.community.jobPosting.adapter.out.persistence;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import page.clab.api.domain.community.jobPosting.domain.CareerLevel;
import page.clab.api.domain.community.jobPosting.domain.EmploymentType;

public interface JobPostingRepositoryCustom {
Page<JobPostingJpaEntity> findByConditions(String title, String companyName, CareerLevel careerLevel, EmploymentType employmentType, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package page.clab.api.domain.community.jobPosting.adapter.out.persistence;

import com.querydsl.core.BooleanBuilder;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
import page.clab.api.domain.community.jobPosting.domain.CareerLevel;
import page.clab.api.domain.community.jobPosting.domain.EmploymentType;
import page.clab.api.global.util.OrderSpecifierUtil;

import java.util.List;

@Repository
@RequiredArgsConstructor
public class JobPostingRepositoryImpl implements JobPostingRepositoryCustom {

private final JPAQueryFactory queryFactory;

@Override
public Page<JobPostingJpaEntity> findByConditions(String title, String companyName, CareerLevel careerLevel, EmploymentType employmentType, Pageable pageable) {
QJobPostingJpaEntity jobPosting = QJobPostingJpaEntity.jobPostingJpaEntity;
BooleanBuilder builder = new BooleanBuilder();

if (title != null && !title.isEmpty()) builder.and(jobPosting.title.containsIgnoreCase(title));
if (companyName != null && !companyName.isEmpty())
builder.and(jobPosting.companyName.containsIgnoreCase(companyName));
if (careerLevel != null) builder.and(jobPosting.careerLevel.eq(careerLevel));
if (employmentType != null) builder.and(jobPosting.employmentType.eq(employmentType));

List<JobPostingJpaEntity> jobPostings = queryFactory.selectFrom(jobPosting)
.where(builder)
.orderBy(OrderSpecifierUtil.getOrderSpecifiers(pageable, jobPosting))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();

long total = queryFactory.query().from(jobPosting)
.where(builder)
.fetchCount();

return new PageImpl<>(jobPostings, pageable, total);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package page.clab.api.domain.community.jobPosting.application.dto.response;

import lombok.Builder;
import lombok.Getter;
import page.clab.api.domain.community.jobPosting.domain.CareerLevel;
import page.clab.api.domain.community.jobPosting.domain.EmploymentType;
import page.clab.api.domain.community.jobPosting.domain.JobPosting;

import java.time.LocalDateTime;

@Getter
@Builder
public class JobPostingDetailsResponseDto {

private Long id;
private String title;
private CareerLevel careerLevel;
private EmploymentType employmentType;
private String companyName;
private String recruitmentPeriod;
private String jobPostingUrl;
private LocalDateTime createdAt;

public static JobPostingDetailsResponseDto toDto(JobPosting jobPosting) {
return JobPostingDetailsResponseDto.builder()
.id(jobPosting.getId())
.title(jobPosting.getTitle())
.careerLevel(jobPosting.getCareerLevel())
.employmentType(jobPosting.getEmploymentType())
.companyName(jobPosting.getCompanyName())
.recruitmentPeriod(jobPosting.getRecruitmentPeriod())
.jobPostingUrl(jobPosting.getJobPostingUrl())
.createdAt(jobPosting.getCreatedAt())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package page.clab.api.domain.community.jobPosting.application.dto.response;

import lombok.Builder;
import lombok.Getter;
import page.clab.api.domain.community.jobPosting.domain.JobPosting;

import java.time.LocalDateTime;
import java.util.List;

@Getter
@Builder
public class JobPostingResponseDto {

private Long id;
private String title;
private String recruitmentPeriod;
private String jobPostingUrl;
private LocalDateTime createdAt;

public static JobPostingResponseDto toDto(JobPosting jobPosting) {
return JobPostingResponseDto.builder()
.id(jobPosting.getId())
.title(jobPosting.getTitle())
.recruitmentPeriod(jobPosting.getRecruitmentPeriod())
.jobPostingUrl(jobPosting.getJobPostingUrl())
.createdAt(jobPosting.getCreatedAt())
.build();
}

public static List<JobPostingResponseDto> toDto(List<JobPosting> jobPostingList) {
return jobPostingList.stream()
.map(JobPostingResponseDto::toDto)
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package page.clab.api.domain.community.jobPosting.application.port.in;

import page.clab.api.domain.community.jobPosting.application.dto.response.JobPostingDetailsResponseDto;

public interface RetrieveJobPostingDetailsUseCase {
JobPostingDetailsResponseDto retrieveJobPostingDetails(Long jobPostingId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package page.clab.api.domain.community.jobPosting.application.port.in;

import org.springframework.data.domain.Pageable;
import page.clab.api.domain.community.jobPosting.application.dto.response.JobPostingResponseDto;
import page.clab.api.domain.community.jobPosting.domain.CareerLevel;
import page.clab.api.domain.community.jobPosting.domain.EmploymentType;
import page.clab.api.global.common.dto.PagedResponseDto;

public interface RetrieveJobPostingsByConditionsUseCase {
PagedResponseDto<JobPostingResponseDto> retrieveJobPostings(String title, String companyName, CareerLevel careerLevel, EmploymentType employmentType, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package page.clab.api.domain.community.jobPosting.application.port.out;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import page.clab.api.domain.community.jobPosting.domain.CareerLevel;
import page.clab.api.domain.community.jobPosting.domain.EmploymentType;
import page.clab.api.domain.community.jobPosting.domain.JobPosting;

public interface RetrieveJobPostingPort {

JobPosting findByIdOrThrow(Long jobPostingId);

Page<JobPosting> findByConditions(String title, String companyName, CareerLevel careerLevel, EmploymentType employmentType, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package page.clab.api.domain.community.jobPosting.application.service;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import page.clab.api.domain.community.jobPosting.application.dto.response.JobPostingDetailsResponseDto;
import page.clab.api.domain.community.jobPosting.application.port.in.RetrieveJobPostingDetailsUseCase;
import page.clab.api.domain.community.jobPosting.application.port.out.RetrieveJobPostingPort;
import page.clab.api.domain.community.jobPosting.domain.JobPosting;

@Service
@RequiredArgsConstructor
public class JobPostingDetailsRetrievalService implements RetrieveJobPostingDetailsUseCase {

private final RetrieveJobPostingPort retrieveJobPostingPort;

@Transactional(readOnly = true)
@Override
public JobPostingDetailsResponseDto retrieveJobPostingDetails(Long jobPostingId) {
JobPosting jobPosting = retrieveJobPostingPort.findByIdOrThrow(jobPostingId);
return JobPostingDetailsResponseDto.toDto(jobPosting);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package page.clab.api.domain.community.jobPosting.application.service;

import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import page.clab.api.domain.community.jobPosting.application.dto.response.JobPostingResponseDto;
import page.clab.api.domain.community.jobPosting.application.port.in.RetrieveJobPostingsByConditionsUseCase;
import page.clab.api.domain.community.jobPosting.application.port.out.RetrieveJobPostingPort;
import page.clab.api.domain.community.jobPosting.domain.CareerLevel;
import page.clab.api.domain.community.jobPosting.domain.EmploymentType;
import page.clab.api.domain.community.jobPosting.domain.JobPosting;
import page.clab.api.global.common.dto.PagedResponseDto;

@Service
@RequiredArgsConstructor
public class JobPostingsByConditionsRetrievalService implements RetrieveJobPostingsByConditionsUseCase {

private final RetrieveJobPostingPort retrieveJobPostingPort;

@Transactional(readOnly = true)
@Override
public PagedResponseDto<JobPostingResponseDto> retrieveJobPostings(String title, String companyName, CareerLevel careerLevel, EmploymentType employmentType, Pageable pageable) {
Page<JobPosting> jobPostings = retrieveJobPostingPort.findByConditions(title, companyName, careerLevel, employmentType, pageable);
return new PagedResponseDto<>(jobPostings.map(JobPostingResponseDto::toDto));
}
}
Loading