-
Notifications
You must be signed in to change notification settings - Fork 1
[FEAT] 미완료된 투표 완료 처리 #227
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
[FEAT] 미완료된 투표 완료 처리 #227
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,3 +41,4 @@ out/ | |
|
|
||
| ### application-local.yml | ||
| /src/main/resources/application-local.yml | ||
| .serena | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package com.debatetimer.config; | ||
|
|
||
| import org.springframework.context.annotation.Configuration; | ||
| import org.springframework.scheduling.annotation.EnableScheduling; | ||
|
|
||
| @Configuration | ||
| @EnableScheduling | ||
| public class SchedulerConfig { | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,21 @@ | ||
| package com.debatetimer.repository.poll; | ||
|
|
||
| import com.debatetimer.domain.poll.PollStatus; | ||
| import com.debatetimer.entity.poll.PollEntity; | ||
| import com.debatetimer.exception.custom.DTClientErrorException; | ||
| import com.debatetimer.exception.errorcode.ClientErrorCode; | ||
| import java.time.LocalDateTime; | ||
| import java.util.Optional; | ||
| import org.springframework.data.jpa.repository.JpaRepository; | ||
| import org.springframework.data.jpa.repository.Modifying; | ||
| import org.springframework.data.jpa.repository.Query; | ||
| import org.springframework.data.repository.Repository; | ||
| import org.springframework.data.repository.query.Param; | ||
|
|
||
| public interface PollRepository extends JpaRepository<PollEntity, Long> { | ||
| public interface PollRepository extends Repository<PollEntity, Long> { | ||
|
|
||
| PollEntity save(PollEntity pollEntity); | ||
|
|
||
| Optional<PollEntity> findById(long id); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 미안합니다 |
||
|
|
||
| Optional<PollEntity> findByIdAndMemberId(long id, long memberId); | ||
|
|
||
|
|
@@ -19,4 +28,9 @@ default PollEntity getByIdAndMemberId(long id, long memberId) { | |
| return findByIdAndMemberId(id, memberId) | ||
| .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.POLL_NOT_FOUND)); | ||
| } | ||
|
|
||
| @Modifying(clearAutomatically = true, flushAutomatically = true) | ||
| @Query("UPDATE PollEntity p SET p.status = :doneStatus WHERE p.status = :status AND p.createdAt <= :threshold") | ||
| void updateStatusToDoneForOldPolls(@Param("doneStatus") PollStatus doneStatus, @Param("status") PollStatus status, | ||
| @Param("threshold") LocalDateTime threshold); | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,27 @@ | ||||||
| package com.debatetimer.scheduler; | ||||||
|
|
||||||
| import com.debatetimer.domain.poll.PollStatus; | ||||||
| import com.debatetimer.domainrepository.poll.PollDomainRepository; | ||||||
| import java.time.LocalDateTime; | ||||||
| import lombok.RequiredArgsConstructor; | ||||||
| import org.springframework.scheduling.annotation.Scheduled; | ||||||
| import org.springframework.stereotype.Component; | ||||||
| import org.springframework.transaction.annotation.Transactional; | ||||||
|
|
||||||
| @Component | ||||||
| @RequiredArgsConstructor | ||||||
| public class PollCleanupScheduler { | ||||||
|
|
||||||
| private static final int INTERVAL_HOURS = 12; | ||||||
| private static final long INTERVAL_MILLIS = INTERVAL_HOURS * 60 * 60 * 1000L; | ||||||
| static final int TIMEOUT_HOURS = 3; | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이건 PollCleanupSchedulerTest에서 해당 상수를 사용하기 위해 일부러 default 접근제한자로 설정한 겁니다 |
||||||
|
|
||||||
| private final PollDomainRepository pollDomainRepository; | ||||||
|
|
||||||
| @Scheduled(fixedRate = INTERVAL_MILLIS, zone = "Asia/Seoul") | ||||||
| @Transactional | ||||||
| public void cleanupStalePolls() { | ||||||
| LocalDateTime threshold = LocalDateTime.now().minusHours(TIMEOUT_HOURS); | ||||||
| pollDomainRepository.updateStatusToDoneForOldPolls(PollStatus.PROGRESS, threshold); | ||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| package com.debatetimer.scheduler; | ||
|
|
||
| import static org.assertj.core.api.Assertions.assertThat; | ||
|
|
||
| import com.debatetimer.domain.member.Member; | ||
| import com.debatetimer.domain.poll.PollStatus; | ||
| import com.debatetimer.entity.customize.CustomizeTableEntity; | ||
| import com.debatetimer.entity.poll.PollEntity; | ||
| import com.debatetimer.repository.poll.PollRepository; | ||
| import com.debatetimer.service.BaseServiceTest; | ||
| import java.time.LocalDateTime; | ||
| import org.junit.jupiter.api.Nested; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.springframework.beans.factory.annotation.Autowired; | ||
| import org.springframework.jdbc.core.JdbcTemplate; | ||
|
|
||
| class PollCleanupSchedulerTest extends BaseServiceTest { | ||
|
|
||
| @Autowired | ||
| private PollRepository pollRepository; | ||
|
|
||
| @Autowired | ||
| private PollCleanupScheduler pollCleanupScheduler; | ||
|
|
||
| @Autowired | ||
| private JdbcTemplate jdbcTemplate; | ||
|
|
||
| @Nested | ||
| class CleanupStalePolls { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 테스트 케이스들이 경계값에서 벗어난 시나리오(예: 예를 들어,
과 같이 경계에 근접한 값으로 테스트하면, (참고: 이 제안은
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이거 적용 부탁드립니다.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이건 굳이 싶어서 적용 안 했습니다. 지금 해당 로직은 |
||
|
|
||
| @Test | ||
| void 생성_후_일정_시간_이상_경과한_진행_상태인_투표를_완료_상태로_변경한다() { | ||
| Member member = memberGenerator.generate("email@email.com"); | ||
| CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); | ||
| PollEntity poll = pollEntityGenerator.generate(table, PollStatus.PROGRESS); | ||
| updateCreatedAt(poll.getId(), LocalDateTime.now().minusHours(PollCleanupScheduler.TIMEOUT_HOURS + 1)); | ||
|
|
||
| pollCleanupScheduler.cleanupStalePolls(); | ||
|
|
||
| PollStatus status = pollRepository.getById(poll.getId()).getStatus(); | ||
| assertThat(status).isEqualTo(PollStatus.DONE); | ||
| } | ||
|
|
||
| @Test | ||
| void 생성_후_일정_시간_미만_경과한_진행_상태인_투표는_그대로_유지한다() { | ||
| Member member = memberGenerator.generate("email@email.com"); | ||
| CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); | ||
| PollEntity poll = pollEntityGenerator.generate(table, PollStatus.PROGRESS); | ||
| updateCreatedAt(poll.getId(), LocalDateTime.now().minusHours(PollCleanupScheduler.TIMEOUT_HOURS - 1)); | ||
|
|
||
| pollCleanupScheduler.cleanupStalePolls(); | ||
|
|
||
| PollStatus status = pollRepository.getById(poll.getId()).getStatus(); | ||
| assertThat(status).isEqualTo(PollStatus.PROGRESS); | ||
| } | ||
|
|
||
| @Test | ||
| void 이미_완료_상태인_투표는_영향받지_않는다() { | ||
| Member member = memberGenerator.generate("email@email.com"); | ||
| CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); | ||
| PollEntity poll = pollEntityGenerator.generate(table, PollStatus.DONE); | ||
| updateCreatedAt(poll.getId(), LocalDateTime.now().minusHours(PollCleanupScheduler.TIMEOUT_HOURS + 1)); | ||
|
|
||
| pollCleanupScheduler.cleanupStalePolls(); | ||
|
|
||
| PollStatus status = pollRepository.getById(poll.getId()).getStatus(); | ||
| assertThat(status).isEqualTo(PollStatus.DONE); | ||
| } | ||
|
|
||
| private void updateCreatedAt(Long pollId, LocalDateTime createdAt) { | ||
| jdbcTemplate.update("UPDATE poll SET created_at = ? WHERE id = ?", createdAt, pollId); | ||
| } | ||
|
Comment on lines
+70
to
+72
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (대략적으로 이해는 갈 것 같은데...) 이거 이렇게 하신 이유가 무엇일까요?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Clock을 빈 처리하거나 createAt을 받는 생성자를 만들거나(그런데 private이라 실패) 리플렉션을 이용할까 하다가 너무 불필요한 리소스라는 판단이 들어서 이렇게 구현했습니다. |
||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이거 뭔가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.serana라는 MCP에 관련된 파일들입니다.