Skip to content
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,60 @@
package in.koreatech.koin.domain.team.recruitment.scheduler;

import static in.koreatech.koin.domain.team.recruitment.enums.TeamRecruitmentStatus.RECRUITING;

import in.koreatech.koin.domain.team.recruitment.model.TeamRecruitment;
import in.koreatech.koin.domain.team.recruitment.repository.TeamRecruitmentRepository;
import java.time.Clock;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.List;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@RequiredArgsConstructor
public class TeamRecruitmentDeadlineCloseCoordinator {

private static final ZoneId KST = ZoneId.of("Asia/Seoul");
private static final int CANDIDATE_BATCH_SIZE = 100;
private static final Pageable CANDIDATE_PAGE = PageRequest.of(
0,
CANDIDATE_BATCH_SIZE,
Sort.by(Sort.Direction.ASC, "id")
);

private final TeamRecruitmentRepository recruitmentRepository;
private final TeamRecruitmentDeadlineCloseProcessor closeProcessor;
private final Clock clock;

public void closeExpiredRecruitments() {
LocalDate today = LocalDate.now(clock.withZone(KST));
Page<TeamRecruitment> candidatePage = recruitmentRepository
.findAllByStatusAndDeadlineDateBefore(RECRUITING, today, CANDIDATE_PAGE);
if (candidatePage == null) {
return;
}

List<Integer> candidateIds = candidatePage
.getContent()
.stream()
.map(TeamRecruitment::getId)
.filter(Objects::nonNull)
.toList();

for (Integer recruitmentId : candidateIds) {
try {
closeProcessor.closeIfExpired(recruitmentId, today);
} catch (Exception exception) {
log.error("팀원 모집글 마감 처리에 실패했습니다. recruitmentId={}", recruitmentId, exception);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,35 +24,21 @@
import in.koreatech.koin.domain.team.recruitment.repository.TeamRecruitmentNotificationRepository;
import in.koreatech.koin.domain.team.recruitment.repository.TeamRecruitmentOutboxEventRepository;
import in.koreatech.koin.domain.team.recruitment.repository.TeamRecruitmentRepository;
import java.time.Clock;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
@RequiredArgsConstructor
public class TeamRecruitmentDeadlineCloseProcessor {

private static final ZoneId KST = ZoneId.of("Asia/Seoul");
private static final int CANDIDATE_BATCH_SIZE = 100;
private static final Pageable CANDIDATE_PAGE = PageRequest.of(
0,
CANDIDATE_BATCH_SIZE,
Sort.by(Sort.Direction.ASC, "id")
);

private static final String RECRUITMENT_CLOSED_REASON = "RECRUITMENT_CLOSED";
private static final String OUTBOX_EVENT_TYPE = "TEAM_RECRUITMENT_NOTIFICATION";
private static final String AGGREGATE_TYPE = "TEAM_RECRUITMENT";
Expand All @@ -61,30 +47,9 @@ public class TeamRecruitmentDeadlineCloseProcessor {
private final TeamRecruitmentChatRoomRepository chatRoomRepository;
private final TeamRecruitmentNotificationRepository notificationRepository;
private final TeamRecruitmentOutboxEventRepository outboxEventRepository;
private final Clock clock;

@Transactional
public void closeExpiredRecruitments() {
LocalDate today = LocalDate.now(clock.withZone(KST));
Page<TeamRecruitment> candidatePage = recruitmentRepository
.findAllByStatusAndDeadlineDateBefore(RECRUITING, today, CANDIDATE_PAGE);
if (candidatePage == null) {
return;
}

List<Integer> candidateIds = candidatePage
.getContent()
.stream()
.map(TeamRecruitment::getId)
.filter(Objects::nonNull)
.toList();

for (Integer recruitmentId : candidateIds) {
closeIfExpired(recruitmentId, today);
}
}

private void closeIfExpired(Integer recruitmentId, LocalDate today) {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void closeIfExpired(Integer recruitmentId, LocalDate today) {
Optional<TeamRecruitment> lockedRecruitment = recruitmentRepository.findByIdWithLock(recruitmentId);
if (lockedRecruitment.isEmpty()) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
@RequiredArgsConstructor
public class TeamRecruitmentDeadlineScheduler {

private final TeamRecruitmentDeadlineCloseProcessor deadlineCloseProcessor;
private final TeamRecruitmentDeadlineCloseCoordinator deadlineCloseCoordinator;

@Scheduled(fixedDelayString = "${team-recruitment.deadline-scheduler.fixed-delay-ms:60000}")
public void closeExpiredRecruitments() {
try {
deadlineCloseProcessor.closeExpiredRecruitments();
deadlineCloseCoordinator.closeExpiredRecruitments();
} catch (Exception exception) {
log.error("팀원 모집 마감 스케줄러 처리 중 오류가 발생했습니다.", exception);
}
Expand Down
Loading
Loading