Skip to content

Commit 0545ba6

Browse files
committed
feat(recruit-board): 모집글 상태 스케쥴링
- 봉사 시작일 기준 모집완료(CLOSED) 변경 - 봉사 종료일 기준 완료(COMPLETED) 변경
1 parent 9a2cc8a commit 0545ba6

File tree

3 files changed

+164
-73
lines changed

3 files changed

+164
-73
lines changed

src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepository.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package com.somemore.domains.recruitboard.repository;
22

3+
import com.somemore.domains.recruitboard.domain.RecruitBoard;
34
import com.somemore.domains.recruitboard.dto.condition.RecruitBoardNearByCondition;
45
import com.somemore.domains.recruitboard.dto.condition.RecruitBoardSearchCondition;
56
import com.somemore.domains.recruitboard.repository.mapper.RecruitBoardDetail;
67
import com.somemore.domains.recruitboard.repository.mapper.RecruitBoardWithCenter;
78
import com.somemore.domains.recruitboard.repository.mapper.RecruitBoardWithLocation;
8-
import com.somemore.domains.recruitboard.domain.RecruitBoard;
9-
9+
import java.time.LocalDateTime;
1010
import java.util.List;
1111
import java.util.Optional;
1212
import java.util.UUID;
@@ -26,17 +26,20 @@ public interface RecruitBoardRepository {
2626

2727
Page<RecruitBoardDetail> findAllNearby(RecruitBoardNearByCondition condition);
2828

29-
// Page<RecruitBoardDetail> findAllNearbyWithKeyword(RecruitBoardNearByCondition condition);
30-
31-
3229
Page<RecruitBoard> findAllByCenterId(UUID centerId, RecruitBoardSearchCondition condition);
3330

3431
List<Long> findNotCompletedIdsByCenterId(UUID centerId);
3532

3633
List<RecruitBoard> findAllByIds(List<Long> ids);
3734

35+
List<RecruitBoard> findAll();
36+
37+
long updateRecruitingToClosedByStartDate(LocalDateTime startTime, LocalDateTime endTime);
38+
39+
long updateClosedToCompletedByEndDate(LocalDateTime now);
40+
41+
// Page<RecruitBoardDetail> findAllNearbyWithKeyword(RecruitBoardNearByCondition condition);
3842
// Page<RecruitBoardWithCenter> findByRecruitBoardsContaining(RecruitBoardSearchCondition condition);
3943
// void saveDocuments(List<RecruitBoard> recruitBoards);
40-
List<RecruitBoard> findAll();
4144
// void deleteDocument(Long id);
4245
}

src/main/java/com/somemore/domains/recruitboard/repository/RecruitBoardRepositoryImpl.java

Lines changed: 103 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package com.somemore.domains.recruitboard.repository;
22

3+
import static com.somemore.domains.recruitboard.domain.RecruitStatus.CLOSED;
4+
import static com.somemore.domains.recruitboard.domain.RecruitStatus.COMPLETED;
5+
import static com.somemore.domains.recruitboard.domain.RecruitStatus.RECRUITING;
6+
37
import com.querydsl.core.types.ConstructorExpression;
48
import com.querydsl.core.types.OrderSpecifier;
59
import com.querydsl.core.types.Projections;
@@ -8,17 +12,17 @@
812
import com.querydsl.jpa.impl.JPAQueryFactory;
913
import com.somemore.domains.center.domain.QCenter;
1014
import com.somemore.domains.location.domain.QLocation;
15+
import com.somemore.domains.location.utils.GeoUtils;
1116
import com.somemore.domains.recruitboard.domain.QRecruitBoard;
17+
import com.somemore.domains.recruitboard.domain.RecruitBoard;
18+
import com.somemore.domains.recruitboard.domain.RecruitStatus;
19+
import com.somemore.domains.recruitboard.domain.VolunteerCategory;
1220
import com.somemore.domains.recruitboard.dto.condition.RecruitBoardNearByCondition;
1321
import com.somemore.domains.recruitboard.dto.condition.RecruitBoardSearchCondition;
1422
import com.somemore.domains.recruitboard.repository.mapper.RecruitBoardDetail;
1523
import com.somemore.domains.recruitboard.repository.mapper.RecruitBoardWithCenter;
1624
import com.somemore.domains.recruitboard.repository.mapper.RecruitBoardWithLocation;
17-
import com.somemore.domains.location.utils.GeoUtils;
18-
import com.somemore.domains.recruitboard.domain.RecruitBoard;
19-
import com.somemore.domains.recruitboard.domain.RecruitStatus;
20-
import com.somemore.domains.recruitboard.domain.VolunteerCategory;
21-
25+
import java.time.LocalDateTime;
2226
import java.util.List;
2327
import java.util.Optional;
2428
import java.util.UUID;
@@ -35,8 +39,8 @@
3539
public class RecruitBoardRepositoryImpl implements RecruitBoardRepository {
3640

3741
private final RecruitBoardJpaRepository recruitBoardJpaRepository;
38-
// private final RecruitBoardDocumentRepository documentRepository;
3942
private final JPAQueryFactory queryFactory;
43+
// private final RecruitBoardDocumentRepository documentRepository;
4044

4145
private static final QRecruitBoard recruitBoard = QRecruitBoard.recruitBoard;
4246
private static final QLocation location = QLocation.location;
@@ -66,31 +70,6 @@ public Optional<RecruitBoard> findById(Long id) {
6670
return Optional.ofNullable(result);
6771
}
6872

69-
@Override
70-
public List<Long> findNotCompletedIdsByCenterId(UUID centerId) {
71-
72-
BooleanExpression exp = centerIdEq(centerId)
73-
.and(isNotCompleted())
74-
.and(isNotDeleted());
75-
76-
return queryFactory
77-
.select(recruitBoard.id)
78-
.from(recruitBoard)
79-
.where(exp)
80-
.fetch();
81-
}
82-
83-
@Override
84-
public List<RecruitBoard> findAllByIds(List<Long> ids) {
85-
BooleanExpression exp = recruitBoard.id.in(ids)
86-
.and(isNotDeleted());
87-
88-
return queryFactory
89-
.selectFrom(recruitBoard)
90-
.where(exp)
91-
.fetch();
92-
}
93-
9473
@Override
9574
public Optional<RecruitBoardWithLocation> findWithLocationById(Long id) {
9675

@@ -170,6 +149,88 @@ public Page<RecruitBoardDetail> findAllNearby(RecruitBoardNearByCondition condit
170149
return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne);
171150
}
172151

152+
@Override
153+
public Page<RecruitBoard> findAllByCenterId(UUID centerId,
154+
RecruitBoardSearchCondition condition) {
155+
156+
BooleanExpression exp = centerIdEq(centerId)
157+
.and(keywordEq(condition.keyword()))
158+
.and(volunteerCategoryEq(condition.category()))
159+
.and(regionEq(condition.region()))
160+
.and(admittedEq(condition.admitted()))
161+
.and(statusEq(condition.status()))
162+
.and(isNotDeleted());
163+
164+
Pageable pageable = condition.pageable();
165+
166+
List<RecruitBoard> content = queryFactory
167+
.selectFrom(recruitBoard)
168+
.where(exp)
169+
.offset(pageable.getOffset())
170+
.limit(pageable.getPageSize())
171+
.orderBy(toOrderSpecifiers(pageable.getSort()))
172+
.fetch();
173+
174+
JPAQuery<Long> countQuery = queryFactory
175+
.select(recruitBoard.count())
176+
.from(recruitBoard)
177+
.where(exp);
178+
179+
return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne);
180+
}
181+
182+
@Override
183+
public List<Long> findNotCompletedIdsByCenterId(UUID centerId) {
184+
185+
BooleanExpression exp = centerIdEq(centerId)
186+
.and(isNotCompleted())
187+
.and(isNotDeleted());
188+
189+
return queryFactory
190+
.select(recruitBoard.id)
191+
.from(recruitBoard)
192+
.where(exp)
193+
.fetch();
194+
}
195+
196+
@Override
197+
public List<RecruitBoard> findAllByIds(List<Long> ids) {
198+
BooleanExpression exp = recruitBoard.id.in(ids)
199+
.and(isNotDeleted());
200+
201+
return queryFactory
202+
.selectFrom(recruitBoard)
203+
.where(exp)
204+
.fetch();
205+
}
206+
207+
@Override
208+
public List<RecruitBoard> findAll() {
209+
return recruitBoardJpaRepository.findAll();
210+
}
211+
212+
@Override
213+
public long updateRecruitingToClosedByStartDate(LocalDateTime startTime, LocalDateTime endTime) {
214+
BooleanExpression exp = statusEq(RECRUITING)
215+
.and(volunteerStartDateTimeBetween(startTime, endTime));
216+
217+
return queryFactory.update(recruitBoard)
218+
.set(recruitBoard.recruitStatus, CLOSED)
219+
.where(exp)
220+
.execute();
221+
}
222+
223+
@Override
224+
public long updateClosedToCompletedByEndDate(LocalDateTime now) {
225+
BooleanExpression exp = statusEq(CLOSED)
226+
.and(volunteerEndDateTimeBefore(now));
227+
228+
return queryFactory.update(recruitBoard)
229+
.set(recruitBoard.recruitStatus, COMPLETED)
230+
.where(exp)
231+
.execute();
232+
}
233+
173234
// @Override
174235
// public Page<RecruitBoardDetail> findAllNearbyWithKeyword(RecruitBoardNearByCondition condition) {
175236
// QRecruitBoard recruitBoard = QRecruitBoard.recruitBoard;
@@ -209,37 +270,8 @@ public Page<RecruitBoardDetail> findAllNearby(RecruitBoardNearByCondition condit
209270
// .and(predicate));
210271
//
211272
// return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne);
212-
// }
213273

214-
@Override
215-
public Page<RecruitBoard> findAllByCenterId(UUID centerId,
216-
RecruitBoardSearchCondition condition) {
217-
218-
BooleanExpression exp = centerIdEq(centerId)
219-
.and(keywordEq(condition.keyword()))
220-
.and(volunteerCategoryEq(condition.category()))
221-
.and(regionEq(condition.region()))
222-
.and(admittedEq(condition.admitted()))
223-
.and(statusEq(condition.status()))
224-
.and(isNotDeleted());
225-
226-
Pageable pageable = condition.pageable();
227-
228-
List<RecruitBoard> content = queryFactory
229-
.selectFrom(recruitBoard)
230-
.where(exp)
231-
.offset(pageable.getOffset())
232-
.limit(pageable.getPageSize())
233-
.orderBy(toOrderSpecifiers(pageable.getSort()))
234-
.fetch();
235-
236-
JPAQuery<Long> countQuery = queryFactory
237-
.select(recruitBoard.count())
238-
.from(recruitBoard)
239-
.where(exp);
240-
241-
return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne);
242-
}
274+
// }
243275

244276
// @Override
245277
// public Page<RecruitBoardWithCenter> findByRecruitBoardsContaining(RecruitBoardSearchCondition condition) {
@@ -287,11 +319,6 @@ public Page<RecruitBoard> findAllByCenterId(UUID centerId,
287319
// documentRepository.saveAll(recruitBoardDocuments);
288320
// }
289321

290-
@Override
291-
public List<RecruitBoard> findAll() {
292-
return recruitBoardJpaRepository.findAll();
293-
}
294-
295322
// @Override
296323
// public void deleteDocument(Long id) {
297324
// documentRepository.deleteById(id);
@@ -310,7 +337,7 @@ private BooleanExpression isNotDeleted() {
310337
}
311338

312339
private BooleanExpression isNotCompleted() {
313-
return recruitBoard.recruitStatus.in(RecruitStatus.RECRUITING, RecruitStatus.CLOSED);
340+
return recruitBoard.recruitStatus.in(RECRUITING, CLOSED);
314341
}
315342

316343
private BooleanExpression keywordEq(String keyword) {
@@ -354,6 +381,15 @@ private BooleanExpression locationBetween(RecruitBoardNearByCondition condition)
354381
.and(location.longitude.between(minLongitude, maxLongitude));
355382
}
356383

384+
private static BooleanExpression volunteerStartDateTimeBetween(LocalDateTime startTime,
385+
LocalDateTime endTime) {
386+
return recruitBoard.recruitmentInfo.volunteerStartDateTime.between(startTime, endTime);
387+
}
388+
389+
private static BooleanExpression volunteerEndDateTimeBefore(LocalDateTime now) {
390+
return recruitBoard.recruitmentInfo.volunteerEndDateTime.before(now);
391+
}
392+
357393
private OrderSpecifier<?>[] toOrderSpecifiers(Sort sort) {
358394
return sort.stream()
359395
.map(order -> {
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.somemore.domains.recruitboard.scheduler;
2+
3+
import com.somemore.domains.recruitboard.repository.RecruitBoardRepository;
4+
import jakarta.transaction.Transactional;
5+
import java.time.LocalDate;
6+
import java.time.LocalDateTime;
7+
import lombok.RequiredArgsConstructor;
8+
import lombok.extern.slf4j.Slf4j;
9+
import org.springframework.retry.annotation.Backoff;
10+
import org.springframework.retry.annotation.Retryable;
11+
import org.springframework.scheduling.annotation.Scheduled;
12+
import org.springframework.stereotype.Component;
13+
14+
@Slf4j
15+
@RequiredArgsConstructor
16+
@Transactional
17+
@Component
18+
public class RecruitBoardStatusUpdateScheduler {
19+
20+
private final RecruitBoardRepository recruitBoardRepository;
21+
22+
@Retryable(
23+
retryFor = Exception.class,
24+
backoff = @Backoff(delay = 100000)
25+
)
26+
@Scheduled(cron = "0 0 0 * * ?")
27+
public void updateRecruitBoardStatusToClosed() {
28+
log.info("봉사 시작일에 해당하는 모집글 상태를 CLOSED로 변경하는 작업 시작");
29+
LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
30+
LocalDateTime startOfNextDay = LocalDate.now().plusDays(1).atStartOfDay();
31+
32+
long updatedCount = recruitBoardRepository.updateRecruitingToClosedByStartDate(startOfDay,
33+
startOfNextDay);
34+
log.info("총 {}개의 모집글 상태를 CLOSED로 변경 완료", updatedCount);
35+
36+
}
37+
38+
@Retryable(
39+
retryFor = Exception.class,
40+
backoff = @Backoff(delay = 100000)
41+
)
42+
@Scheduled(cron = "0 0 0 * * ?")
43+
public void updateRecruitBoardStatusToCompleted() {
44+
log.info("봉사 종료일이 지난 모집글 상태를 COMPLETED로 변경하는 작업 시작");
45+
LocalDateTime now = LocalDateTime.now();
46+
47+
long updatedCount = recruitBoardRepository.updateClosedToCompletedByEndDate(now);
48+
log.info("총 {}개의 모집글 상태를 COMPLETED로 변경 완료", updatedCount);
49+
50+
}
51+
52+
}

0 commit comments

Comments
 (0)