Skip to content

Commit 1588bd7

Browse files
authored
Merge pull request #213 from prgrms-web-devcourse-final-project/feature/EA3-134-study-optimal-time-vote
[EA3-134] fix: TimeVoteStat 저장 시 version=null 방지를 위한 flush 및 기본값 설정 + 투표 기간 생성시 마지막날 23:59:59로 자동 보정되게 수정
2 parents 55d97ee + e6ae9ac commit 1588bd7

File tree

7 files changed

+37
-13
lines changed

7 files changed

+37
-13
lines changed

src/main/java/grep/neogulcoder/domain/timevote/controller/TimeVoteController.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ public ApiResponse<TimeVotePeriodResponse> createPeriod(
4141
@RequestBody @Valid TimeVotePeriodCreateRequest request,
4242
@AuthenticationPrincipal Principal userDetails
4343
) {
44-
TimeVotePeriod saved = timeVotePeriodService.createTimeVotePeriodAndReturn(request, studyId, userDetails.getUserId());
45-
return ApiResponse.success(TimeVotePeriodResponse.from(saved));
44+
TimeVotePeriodResponse response = timeVotePeriodService.createTimeVotePeriodAndReturn(request, studyId, userDetails.getUserId());
45+
return ApiResponse.success(response);
4646
}
4747

4848
@GetMapping("/votes")

src/main/java/grep/neogulcoder/domain/timevote/dto/request/TimeVotePeriodCreateRequest.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,15 @@ private TimeVotePeriodCreateRequest(LocalDateTime startDate, LocalDateTime endDa
2727
this.endDate = endDate;
2828
}
2929

30-
public TimeVotePeriod toEntity(Long studyId) {
30+
public TimeVotePeriod toEntity(Long studyId, LocalDateTime adjustedEndDate) {
3131
return TimeVotePeriod.builder()
3232
.startDate(this.startDate)
33-
.endDate(this.endDate)
33+
.endDate(adjustedEndDate)
3434
.studyId(studyId)
3535
.build();
3636
}
37+
38+
public static TimeVotePeriodCreateRequest of(LocalDateTime startDate, LocalDateTime endDate) {
39+
return new TimeVotePeriodCreateRequest(startDate, endDate);
40+
}
3741
}

src/main/java/grep/neogulcoder/domain/timevote/entity/TimeVoteStat.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,25 @@ public class TimeVoteStat extends BaseEntity {
3333
private Long voteCount;
3434

3535
@Version
36+
@Column(nullable = false)
3637
private Long version;
3738

3839
protected TimeVoteStat() {};
3940

4041
@Builder
41-
public TimeVoteStat(TimeVotePeriod period, LocalDateTime timeSlot, Long voteCount) {
42+
public TimeVoteStat(TimeVotePeriod period, LocalDateTime timeSlot, Long voteCount, Long version) {
4243
this.period = period;
4344
this.timeSlot = timeSlot;
4445
this.voteCount = voteCount;
46+
this.version = version;
4547
}
4648

4749
public static TimeVoteStat of(TimeVotePeriod period, LocalDateTime timeSlot, Long voteCount) {
4850
return TimeVoteStat.builder()
4951
.period(period)
5052
.timeSlot(timeSlot)
5153
.voteCount(voteCount)
54+
.version(0L)
5255
.build();
5356
}
5457

src/main/java/grep/neogulcoder/domain/timevote/repository/TimeVoteStatQueryRepository.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public void incrementOrInsert(TimeVotePeriod period, LocalDateTime slot, Long co
5151
} else {
5252
TimeVoteStat newStat = TimeVoteStat.of(period, slot, countToAdd);
5353
em.persist(newStat);
54+
em.flush();
5455
}
5556
}
5657
}

src/main/java/grep/neogulcoder/domain/timevote/service/TimeVotePeriodService.java

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
import grep.neogulcoder.domain.study.repository.StudyMemberRepository;
99
import grep.neogulcoder.domain.study.repository.StudyRepository;
1010
import grep.neogulcoder.domain.timevote.dto.request.TimeVotePeriodCreateRequest;
11+
import grep.neogulcoder.domain.timevote.dto.response.TimeVotePeriodResponse;
1112
import grep.neogulcoder.domain.timevote.entity.TimeVotePeriod;
1213
import grep.neogulcoder.domain.timevote.repository.TimeVotePeriodRepository;
1314
import grep.neogulcoder.domain.timevote.repository.TimeVoteRepository;
1415
import grep.neogulcoder.domain.timevote.repository.TimeVoteStatRepository;
1516
import grep.neogulcoder.global.exception.business.BusinessException;
1617
import java.time.LocalDateTime;
18+
import java.time.LocalTime;
1719
import java.time.temporal.ChronoUnit;
1820
import lombok.RequiredArgsConstructor;
1921
import org.springframework.stereotype.Service;
@@ -25,38 +27,45 @@
2527
public class TimeVotePeriodService {
2628

2729
private final TimeVotePeriodRepository timeVotePeriodRepository;
28-
private final StudyRepository studyRepository;
2930
private final TimeVoteRepository timeVoteRepository;
3031
private final TimeVoteStatRepository timeVoteStatRepository;
32+
private final StudyRepository studyRepository;
3133
private final StudyMemberRepository studyMemberRepository;
3234

33-
public TimeVotePeriod createTimeVotePeriodAndReturn(TimeVotePeriodCreateRequest request, Long studyId, Long userId) {
35+
public TimeVotePeriodResponse createTimeVotePeriodAndReturn(TimeVotePeriodCreateRequest request, Long studyId, Long userId) {
3436
StudyMember studyMember = getValidStudyMember(studyId, userId);
3537
validateStudyLeader(studyMember);
3638

3739
validateStartDateNotPast(request.getStartDate());
3840
validatePeriodRange(request.getStartDate(), request.getEndDate());
3941
validateMaxPeriod(request.getStartDate(), request.getEndDate());
4042

43+
LocalDateTime adjustedEndDate = adjustEndDate(request.getEndDate());
44+
4145
if (timeVotePeriodRepository.existsByStudyId(studyId)) {
4246
deleteAllTimeVoteDate(studyId);
4347
}
4448

45-
TimeVotePeriod savedPeriod = timeVotePeriodRepository.save(request.toEntity(studyId));
49+
TimeVotePeriod savedPeriod = timeVotePeriodRepository.save(request.toEntity(studyId, adjustedEndDate));
4650

47-
return savedPeriod;
51+
return TimeVotePeriodResponse.from(savedPeriod);
4852
}
4953

5054
public void deleteAllTimeVoteDate(Long studyId) {
51-
Study study = studyRepository.findById(studyId)
52-
.orElseThrow(() -> new BusinessException(STUDY_NOT_FOUND));
55+
getValidStudy(studyId);
5356

5457
timeVoteRepository.deleteAllByPeriod_StudyId(studyId);
5558
timeVoteStatRepository.deleteAllByPeriod_StudyId(studyId);
5659
timeVotePeriodRepository.deleteAllByStudyId(studyId);
5760
}
5861

5962
// ================================= 검증 로직 ================================
63+
// 주어진 ID의 스터디가 존재하는지 검증 (존재하지 않으면 예외 발생)
64+
private Study getValidStudy(Long studyId) {
65+
return studyRepository.findById(studyId)
66+
.orElseThrow(() -> new BusinessException(STUDY_NOT_FOUND));
67+
}
68+
6069
// 유효한 스터디 멤버인지 확인 (활성화된 멤버만 허용)
6170
private StudyMember getValidStudyMember(Long studyId, Long userId) {
6271
return studyMemberRepository.findByStudyIdAndUserIdAndActivatedTrue(studyId, userId)
@@ -79,6 +88,11 @@ private void validateMaxPeriod(LocalDateTime startDate, LocalDateTime endDate) {
7988
}
8089
}
8190

91+
// 투표 기간 종료일을 마지막 날 23:59:59로 보정
92+
private LocalDateTime adjustEndDate(LocalDateTime endDate) {
93+
return endDate.with(LocalTime.of(23, 59, 59));
94+
}
95+
8296
// 시작일이 종료일보다 늦은 비정상적인 입력 방지
8397
private void validatePeriodRange(LocalDateTime startDate, LocalDateTime endDate) {
8498
if (startDate.isAfter(endDate)) {

src/main/java/grep/neogulcoder/domain/timevote/service/TimeVoteService.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import grep.neogulcoder.domain.timevote.repository.TimeVoteQueryRepository;
1616
import grep.neogulcoder.global.exception.business.BusinessException;
1717
import java.time.LocalDateTime;
18+
import java.time.LocalTime;
1819
import java.util.HashSet;
1920
import java.util.List;
2021
import java.util.Set;
@@ -119,7 +120,8 @@ private TimeVotePeriod getValidTimeVotePeriod(Long studyId) {
119120
// 각 투표 시간이 투표 기간 내에 포함되는지 확인
120121
private void validateVoteWithinPeriod(TimeVotePeriod period, List<LocalDateTime> dateTimes) {
121122
for (LocalDateTime dateTime : dateTimes) {
122-
if (dateTime.isBefore(period.getStartDate()) || dateTime.isAfter(period.getEndDate())) {
123+
if (dateTime.isBefore(period.getStartDate()) || dateTime.isAfter(period.getEndDate().with(
124+
LocalTime.of(23, 59, 59)))) {
123125
throw new BusinessException(TIME_VOTE_OUT_OF_RANGE);
124126
}
125127
}

src/main/resources/data.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ INSERT INTO team_calendar (study_id, user_id, calendar_id, activated) VALUES (1,
6565
INSERT INTO team_calendar (study_id, user_id, calendar_id, activated) VALUES (3, 3, 4, TRUE);
6666
INSERT INTO team_calendar (study_id, user_id, calendar_id, activated) VALUES (4, 4, 5, TRUE);
6767

68-
INSERT INTO time_vote_period (study_id, start_date, end_date, activated) VALUES (6, '2025-07-25 00:00:00', '2025-07-30 00:00:00', TRUE);
68+
INSERT INTO time_vote_period (study_id, start_date, end_date, activated) VALUES (6, '2025-07-25 00:00:00', '2025-07-30 23:59:59', TRUE);
6969

7070
-- 2025-07-25 10:00:00 - 2명
7171
INSERT INTO time_vote (period_id, study_member_id, time_slot, activated) VALUES (1, 6, '2025-07-25 10:00:00', TRUE);

0 commit comments

Comments
 (0)