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

fix: Quartz Scheduler 활용하여 팀 상태 변경하기 #272

Merged
merged 4 commits into from
Oct 5, 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
10 changes: 5 additions & 5 deletions .github/workflows/release-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
docker build -t ${{ secrets.DOCKERHUB_USERNAME}}/${{ secrets.DOCKERHUB_REPOSITORY}} ./
docker push ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPOSITORY}}

# Docker Compose 및 deploy.sh 실행
# Docker Compose
- name: Docker Compose
uses: appleboy/ssh-action@master
with:
Expand All @@ -63,8 +63,8 @@ jobs:
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
sudo docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_PASSWORD }}
sudo docker-compose stop ${{ secrets.DOCKER_SERVICE_NAME }}
sudo docker-compose rm -f ${{ secrets.DOCKER_SERVICE_NAME }}
sudo docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPOSITORY}}
cd ~
sudo chmod +x depoly.sh
./depoly.sh
# sudo docker tag ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPOSITORY}} ${{ secrets.DOCKER_IMAGE_NAME }}
sudo docker tag ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPOSITORY}} ${{ secrets.DOCKER_IMAGE_NAME }}
sudo docker compose up -d
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ dependencies {

// Json Type Parsing
implementation 'com.fasterxml.jackson.module:jackson-module-jakarta-xmlbind-annotations'

// Quartz
implementation 'org.springframework.boot:spring-boot-starter-quartz:2.7.5'
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ public record SimpleTeamRes(
@Schema(description = "지원 파트 정보 - 내가 지원한 팀에서는 지원한 포지션 하나만 반환")
List<String> recruitPart,

@Schema(description = "사용자가 지원한 파트")
String applyPart,

@Schema(description = "모집 마감일", example = "2024-12-31")
LocalDate recruitFinishedAt,

Expand Down Expand Up @@ -82,4 +85,32 @@ public static SimpleTeamRes of(Team team) {
.viewCount(team.getViewCount())
.build();
}

public static SimpleTeamRes of(Team team, String applyPart) {
int dDay = 0;
if (team.getRecruitFinishedAt() != null) {
dDay = (int) java.time.temporal.ChronoUnit.DAYS.between(LocalDate.now(), team.getRecruitFinishedAt());
}

List<String> recruitPartList = (team.getRecruitPart() != null) ? team.getRecruitPart().stream()
.map(recruitPart -> recruitPart.position().getKoreanName())
.toList() : null;

return SimpleTeamRes.builder()
.id(team.getId())
.title(team.getTitle())
.leaderId(team.getMember().getId())
.leaderName(team.getMember().getName())
.contestId(team.getContest().getId())
.status(team.getStatus().getDescription())
.recruitPart(recruitPartList)
.applyPart(applyPart)
.recruitFinishedAt(team.getRecruitFinishedAt())
.startedAt(team.getStartedAt())
.finishedAt(team.getFinishedAt())
.dDay(dDay)
.scrapCount(team.getScrapCount())
.viewCount(team.getViewCount())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;

public interface TeamRepositoryCustom {
Expand All @@ -26,4 +28,8 @@ public interface TeamRepositoryCustom {
Page<SimpleTeamRes> findScrapPagination(Long memberId, Pageable pageable);

Boolean equalsLeaderIdAndMember(Portfolio portfolio, Member member);

List<Team> findAllByRecruitFinishedAtAndStartedAtBeforeAndFinishedAtAfter(LocalDate today);

List<Team> findAllByTeamStatusAndFinishedAtBefore(LocalDate today);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.gongjakso.server.domain.team.repository;

import com.gongjakso.server.domain.apply.entity.Apply;
import com.gongjakso.server.domain.apply.enumerate.ApplyStatus;
import com.gongjakso.server.domain.apply.repository.ApplyRepository;
import com.gongjakso.server.domain.member.entity.Member;
import com.gongjakso.server.domain.portfolio.entity.Portfolio;
import com.gongjakso.server.domain.team.dto.response.SimpleTeamRes;
Expand All @@ -16,6 +19,7 @@
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
Expand All @@ -29,6 +33,7 @@
public class TeamRepositoryImpl implements TeamRepositoryCustom {

private final JPAQueryFactory queryFactory;
private final ApplyRepository applyRepository;

public Optional<Team> findTeamById(Long id) {
return Optional.ofNullable(queryFactory
Expand Down Expand Up @@ -185,23 +190,41 @@ public Page<SimpleTeamRes> findApplyPagination(Long memberId, Pageable pageable)
}

public Page<SimpleTeamRes> findParticipatePagination(Long memberId, Pageable pageable) {
List<TeamStatus> teamStatusList = Arrays.asList(TeamStatus.RECRUITING, TeamStatus.EXTENSION, TeamStatus.CANCELED, TeamStatus.CLOSED);
List<TeamStatus> teamStatusList = Arrays.asList(TeamStatus.ACTIVE, TeamStatus.FINISHED);

List<Team> teamList = queryFactory
.select(team)
.from(team)
.innerJoin(apply).on(apply.team.id.eq(team.id).and(apply.deletedAt.isNull()))
.leftJoin(apply).on(apply.team.id.eq(team.id)
.and(apply.member.id.eq(memberId))
.and(apply.deletedAt.isNull()))
.where(
team.status.in(teamStatusList),
team.deletedAt.isNull()
(
team.member.id.eq(memberId)
.and(team.status.in(teamStatusList))
.and(team.deletedAt.isNull())
)
.or(
apply.member.id.eq(memberId)
.and(apply.status.eq(ApplyStatus.ACCEPTED))
.and(apply.team.status.in(teamStatusList))
.and(apply.team.deletedAt.isNull())
)
)
.orderBy(team.createdAt.desc())
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();

List<SimpleTeamRes> content = teamList.stream()
.map(SimpleTeamRes::of)
.map(team -> {
Optional<Apply> apply = applyRepository
.findByTeamIdAndMemberIdAndDeletedAtIsNull(team.getId(), memberId);

String applyPart = apply.map(Apply::getPart).orElse(null);

return SimpleTeamRes.of(team, applyPart);
})
.toList();

Long total = queryFactory.select(team.count())
Expand Down Expand Up @@ -291,4 +314,27 @@ public Boolean equalsLeaderIdAndMember(Portfolio portfolio, Member member) {
.where(booleanBuilder)
.fetchFirst() != null;
}

public List<Team> findAllByRecruitFinishedAtAndStartedAtBeforeAndFinishedAtAfter(LocalDate today) {
return queryFactory
.selectFrom(team)
.where(
team.deletedAt.isNull(),
team.recruitFinishedAt.before(LocalDate.from(today)),
team.startedAt.before(LocalDate.from(today)),
team.finishedAt.after(LocalDate.from(today))
)
.fetch();
}

public List<Team> findAllByTeamStatusAndFinishedAtBefore(LocalDate today) {
return queryFactory
.selectFrom(team)
.where(
team.deletedAt.isNull(),
team.status.eq(TeamStatus.valueOf(TeamStatus.ACTIVE.name())),
team.finishedAt.before(LocalDate.from(today))
)
.fetch();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -349,4 +349,22 @@ public void updatePassCount(Team team) {
int passCount = applyRepository.countByTeamIdAndStatusAndDeletedAtIsNull(team.getId(), ApplyStatus.ACCEPTED);
team.updatePassCount(passCount);
}

@Transactional
public void updateTeamsToActiveStatus() {
List<Team> activeTeams = teamRepository.findAllByRecruitFinishedAtAndStartedAtBeforeAndFinishedAtAfter(LocalDate.now());

if (!activeTeams.isEmpty()) {
activeTeams.forEach(team -> team.updateTeamStatus(TeamStatus.ACTIVE));
teamRepository.saveAll(activeTeams);
}

List<Team> finishedTeams = teamRepository.findAllByTeamStatusAndFinishedAtBefore(LocalDate.now());

if (!finishedTeams.isEmpty()) {
finishedTeams.forEach(team -> team.updateTeamStatus(TeamStatus.FINISHED));
teamRepository.saveAll(finishedTeams);
}
}

}
30 changes: 30 additions & 0 deletions src/main/java/com/gongjakso/server/global/config/QuartzConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.gongjakso.server.global.config;

import com.gongjakso.server.global.util.quartz.UpdateTeamStatusJob;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.quartz.*;
import org.springframework.context.annotation.Configuration;

@Configuration
@RequiredArgsConstructor
public class QuartzConfig {

private final Scheduler scheduler;

@PostConstruct
public void scheduleDailyJob() throws SchedulerException {
JobDetail jobDetail = JobBuilder
.newJob(UpdateTeamStatusJob.class)
.withIdentity("updateTeamStatusJob", "teamStatusGroup")
.build();

Trigger trigger = TriggerBuilder
.newTrigger()
.withIdentity("updateTeamStatusTrigger", "teamStatusGroup")
.withSchedule(CronScheduleBuilder.dailyAtHourAndMinute(0, 0))
.build();

scheduler.scheduleJob(jobDetail, trigger);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.gongjakso.server.global.util.quartz;

import com.gongjakso.server.domain.team.service.TeamService;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class UpdateTeamStatusJob implements Job {

@Autowired
private TeamService teamService;

@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
teamService.updateTeamsToActiveStatus();
}
}
Loading