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

[BE] 나의 미팅 조회 :: 리펙터링, 쿼리최적화 #588

Open
wants to merge 20 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
bfd85e3
feat: 시간을 mock할 수 있게 AttendanceScheduler 를 변경
progress0407 Oct 25, 2022
9493f1e
test(MeetingServiceTest): 나의 미팅 조회(findAllByUserId)를 엄격히 검증할 수 있게 테스트…
progress0407 Oct 25, 2022
dad9ced
refactor(MeetingService): 나의 미팅 조회 리펙터링
progress0407 Oct 25, 2022
e5b3a4b
fix: 불완전한 DateTime mocking으로 인해 깨진 테스트 코드 수정
progress0407 Oct 25, 2022
300adb6
refactor: 1. 도메인 합성 리포 도입 (CompositionRepository) 2.findAllByMe 리펙터링
progress0407 Oct 26, 2022
fdbc053
refactor: 1. CompositoryRepository 성능 위주 리펙터링
progress0407 Oct 26, 2022
013d9fa
test(CompositionRepository, ParticipantRepository): 테스트 코드 작성
progress0407 Oct 26, 2022
46b8bb5
Merge branch 'dev' into feature/be/find-all-by-me and fix(Composition…
progress0407 Nov 8, 2022
243c462
fix(ComposiionRepositoryTest): 테스트 코드 수정
progress0407 Nov 8, 2022
b6f5f00
refactor: 나의 미팅 조회 테스트 코드 작성 및 리펙터링
progress0407 Nov 10, 2022
647e5a7
Merge branch 'dev' into feature/be/find-all-by-me
progress0407 Nov 10, 2022
1511b3c
refactor(MeetingService): 사용하지 않는 메서드 제거
progress0407 Nov 10, 2022
7a3791e
fix(AttendanceAcceptanceTest): 10시 이전에 실패하는 showAttendanceAfterClosed…
progress0407 Nov 11, 2022
6fbd977
test(Meeting, Participant): 테스트 코드 추가
progress0407 Nov 11, 2022
a208474
refactor: 불필요한 클래스 정리 (Custom Repository)
progress0407 Nov 11, 2022
c838c82
Merge branch 'dev' into feature/be/find-all-by-me
progress0407 Nov 11, 2022
618eb38
build: MeetingAttendance 테스트에서 제외
progress0407 Nov 13, 2022
ebc92d0
refactor(CompositionRepository): 메서드 추출
progress0407 Nov 13, 2022
1859cea
style(MeetingService):공백 제거, static import 추가
progress0407 Nov 13, 2022
4ddf2c9
refactor: 리뷰 내용 반영 (unused method, final modifier)
progress0407 Nov 18, 2022
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
4 changes: 3 additions & 1 deletion backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,9 @@ jacocoTestCoverageVerification {
}

excludes = ['*infrastructure*', '*support*', '*config*', '*dto*', '*exception*', '*logging*', '*Application*',
'*ControllerAdvice', '*SessionAttributeNames', '*Patterns', '*ValidationMessages', '*StatusCheckController']
'*ControllerAdvice', '*SessionAttributeNames', '*Patterns', '*ValidationMessages', '*StatusCheckController',
'*MeetingAttendances'
]
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,19 @@ public class AttendanceScheduler {
private static final Logger log = LoggerFactory.getLogger(AttendanceScheduler.class);

private final AttendanceRepository attendanceRepository;
private final ServerTimeManager serverTimeManager;

public AttendanceScheduler(final AttendanceRepository attendanceRepository) {
public AttendanceScheduler(final AttendanceRepository attendanceRepository,
final ServerTimeManager serverTimeManager) {
this.attendanceRepository = attendanceRepository;
this.serverTimeManager = serverTimeManager;
}

@Scheduled(cron = "5 */5 * * * *")
@Transactional
public void updateToTardyAtAttendanceClosingTime() {
final LocalDateTime now = LocalDateTime.now();
attendanceRepository.updateByEventDateTimeAndStatus(now.toLocalDate(), now.toLocalTime());
final LocalDateTime dateTime = serverTimeManager.getDateAndTime();
attendanceRepository.updateByEventDateTimeAndStatus(dateTime.toLocalDate(), dateTime.toLocalTime());
log.info("출석 마감 처리 완료");
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.woowacourse.moragora.application;

import static java.util.stream.Collectors.counting;
import static java.util.stream.Collectors.groupingBy;

import com.woowacourse.moragora.domain.attendance.Attendance;
import com.woowacourse.moragora.domain.attendance.AttendanceRepository;
import com.woowacourse.moragora.domain.attendance.CoffeeStatRepository;
Expand Down Expand Up @@ -119,14 +122,14 @@ public CoffeeStatsResponse countUsableCoffeeStack(final Long meetingId) {
final int numberOfParticipants = participants.size();

final PageRequest pageRequest = PageRequest.ofSize(numberOfParticipants);
final List<Attendance> attendancesForCoffeeStack = coffeeStatRepository.findCoffeeStackOrderedByParticipant(
pageRequest, participants);
final List<Attendance> attendancesForCoffeeStack =
coffeeStatRepository.findCoffeeStackOrderedByParticipant(pageRequest, participants);

validateCoffeeTime(numberOfParticipants, attendancesForCoffeeStack.size());

final Map<User, Long> coffeeStackByUser = attendancesForCoffeeStack.stream()
.collect(Collectors.groupingBy(attendance -> attendance.getParticipant().getUser(),
Collectors.counting()));
final Map<User, Long> coffeeStackByUser =
attendancesForCoffeeStack.stream()
.collect(groupingBy(attendance -> attendance.getParticipant().getUser(), counting()));

return CoffeeStatsResponse.from(coffeeStackByUser);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
package com.woowacourse.moragora.application;

import com.woowacourse.moragora.domain.attendance.Attendance;
import static java.util.stream.Collectors.toList;

import com.woowacourse.moragora.domain.attendance.AttendanceRepository;
import com.woowacourse.moragora.domain.attendance.MeetingAttendances;
import com.woowacourse.moragora.domain.attendance.ParticipantAttendances;
import com.woowacourse.moragora.domain.event.Event;
import com.woowacourse.moragora.domain.event.EventRepository;
import com.woowacourse.moragora.domain.geolocation.Beacon;
import com.woowacourse.moragora.domain.geolocation.BeaconRepository;
import com.woowacourse.moragora.domain.meeting.Meeting;
import com.woowacourse.moragora.domain.meeting.MeetingRepository;
import com.woowacourse.moragora.domain.participant.Participant;
import com.woowacourse.moragora.domain.participant.ParticipantAndCount;
import com.woowacourse.moragora.domain.participant.ParticipantRepository;
import com.woowacourse.moragora.domain.query.QueryRepository;
import com.woowacourse.moragora.domain.global.CompositionRepository;
import com.woowacourse.moragora.domain.user.User;
import com.woowacourse.moragora.domain.user.UserRepository;
import com.woowacourse.moragora.dto.request.meeting.BeaconRequest;
import com.woowacourse.moragora.dto.request.meeting.MasterRequest;
import com.woowacourse.moragora.dto.request.meeting.MeetingRequest;
import com.woowacourse.moragora.dto.request.meeting.MeetingUpdateRequest;
import com.woowacourse.moragora.dto.response.event.EventResponse;
import com.woowacourse.moragora.dto.response.meeting.MeetingActiveResponse;
import com.woowacourse.moragora.dto.response.meeting.MeetingResponse;
import com.woowacourse.moragora.dto.response.meeting.MyMeetingResponse;
Expand All @@ -32,7 +29,7 @@
import com.woowacourse.moragora.exception.participant.ParticipantNotFoundException;
import com.woowacourse.moragora.exception.user.UserNotFoundException;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
Expand All @@ -54,7 +51,7 @@ public class MeetingService {
private final AttendanceRepository attendanceRepository;
private final UserRepository userRepository;
private final BeaconRepository beaconRepository;
private final QueryRepository queryRepository;
private final CompositionRepository compositionRepository;
private final ServerTimeManager serverTimeManager;

public MeetingService(final MeetingRepository meetingRepository,
Expand All @@ -63,15 +60,15 @@ public MeetingService(final MeetingRepository meetingRepository,
final AttendanceRepository attendanceRepository,
final UserRepository userRepository,
final BeaconRepository beaconRepository,
final QueryRepository queryRepository,
final CompositionRepository compositionRepository,
final ServerTimeManager serverTimeManager) {
this.meetingRepository = meetingRepository;
this.eventRepository = eventRepository;
this.participantRepository = participantRepository;
this.attendanceRepository = attendanceRepository;
this.userRepository = userRepository;
this.beaconRepository = beaconRepository;
this.queryRepository = queryRepository;
this.compositionRepository = compositionRepository;
this.serverTimeManager = serverTimeManager;
}

Expand All @@ -93,12 +90,7 @@ public Long save(final MeetingRequest request, final Long loginId) {

public MeetingResponse findById(final Long meetingId, final Long loginId) {
final LocalDate today = serverTimeManager.getDate();
final Meeting meeting = meetingRepository.findMeetingAndParticipantsById(meetingId)
.orElseThrow(MeetingNotFoundException::new);

final List<ParticipantAndCount> participantAndCounts = queryRepository
.countParticipantsTardy(meeting.getParticipants(), today);
meeting.allocateParticipantsTardyCount(participantAndCounts);
final Meeting meeting = compositionRepository.meetingWithTardyCount(meetingId);

final long attendedEventCount = eventRepository.countByMeetingIdAndDateLessThanEqual(meetingId, today);
final Participant loginParticipant = meeting.findParticipant(loginId)
Expand All @@ -107,14 +99,12 @@ public MeetingResponse findById(final Long meetingId, final Long loginId) {
return MeetingResponse.of(meeting, attendedEventCount, loginParticipant);
}

public MyMeetingsResponse findAllByUserId(final Long userId) {
final List<Participant> participants = participantRepository.findByUserId(userId);

final List<MyMeetingResponse> myMeetingResponses = participants.stream()
.map(participant -> generateMyMeetingResponse(participant, serverTimeManager.getDate()))
.collect(Collectors.toList());
public MyMeetingsResponse findAllByMe(final Long loginUserId) {
progress0407 marked this conversation as resolved.
Show resolved Hide resolved
final LocalDate today = serverTimeManager.getDate();
final List<Meeting> meetings = compositionRepository.meetingsWithTardyCount(loginUserId);
final List<MyMeetingResponse> myMeetingsResponse = createMyMeetingsResponse(loginUserId, today, meetings);

return new MyMeetingsResponse(myMeetingResponses);
return new MyMeetingsResponse(myMeetingsResponse);
}

@Transactional
Expand Down Expand Up @@ -179,15 +169,11 @@ public void addBeacons(final Long meetingId, final List<BeaconRequest> beaconsRe

final List<Beacon> beacons = beaconsRequest.stream()
.map((BeaconRequest beaconRequest) -> beaconRequest.toEntity(meeting))
.collect(Collectors.toList());
.collect(toList());

beaconRepository.saveAll(beacons);
}

private boolean validateMaximumBeacon(final List<BeaconRequest> beaconsRequest) {
return beaconsRequest.size() > MAX_BEACON_SIZE;
}

public MeetingActiveResponse checkActive(final Long meetingId) {
validateMeetingExists(meetingId);

Expand All @@ -197,6 +183,32 @@ public MeetingActiveResponse checkActive(final Long meetingId) {
return new MeetingActiveResponse(isActive);
}

private void saveParticipants(final Meeting meeting, final User loginUser, final List<User> users) {
final Participant loginParticipant = new Participant(loginUser, meeting, true);
final List<Participant> participants = users.stream()
.map(user -> new Participant(user, meeting, false))
.collect(toList());
participants.add(loginParticipant);

for (Participant participant : participants) {
participant.mapMeeting(meeting);
participantRepository.save(participant);
}
}

private List<MyMeetingResponse> createMyMeetingsResponse(final Long loginUserId,
final LocalDate today,
final List<Meeting> meetings) {
final List<MyMeetingResponse> myMeetingsResponse = new ArrayList<>();

for (final Meeting meeting : meetings) {
final Event upcomingEvent = findUpComingEvent(meeting, today);
myMeetingsResponse.add(MyMeetingResponse.of(meeting, upcomingEvent, loginUserId, serverTimeManager));
}

return myMeetingsResponse;
}

/**
* 참가자 userIds 내부에 loginId가 있는지 검증해야 userIds.size()가 0인지 검증이 정상적으로 이루어집니다.
*/
Expand All @@ -214,62 +226,20 @@ private void validateUserIds(final List<Long> userIds, final Long loginId) {
}
}

private void validateUsersExists(final List<Long> userIds, final List<User> users) {
if (users.size() != userIds.size()) {
throw new UserNotFoundException();
}
}

private void saveParticipants(final Meeting meeting, final User loginUser, final List<User> users) {
final Participant loginParticipant = new Participant(loginUser, meeting, true);
final List<Participant> participants = users.stream()
.map(user -> new Participant(user, meeting, false))
.collect(Collectors.toList());
participants.add(loginParticipant);

for (Participant participant : participants) {
participant.mapMeeting(meeting);
participantRepository.save(participant);
}
private Event findUpComingEvent(final Meeting meeting, final LocalDate today) {
return eventRepository
.findFirstByMeetingIdAndDateGreaterThanEqualOrderByDate(meeting.getId(), today)
.orElse(null);
}

private MeetingAttendances getMeetingAttendances(final Meeting meeting, final LocalDate today) {
final List<Long> participantIds = meeting.getParticipantIds();
final List<Attendance> foundAttendances = attendanceRepository
.findByParticipantIdInAndEventDateLessThanEqual(participantIds, today);
return new MeetingAttendances(foundAttendances, participantIds.size());
private boolean validateMaximumBeacon(final List<BeaconRequest> beaconsRequest) {
return beaconsRequest.size() > MAX_BEACON_SIZE;
}

private MyMeetingResponse generateMyMeetingResponse(final Participant participant, final LocalDate today) {
final Meeting meeting = participant.getMeeting();
final boolean isLoginUserMaster = participant.getIsMaster();

final MeetingAttendances meetingAttendances = getMeetingAttendances(meeting, today);
final boolean isCoffeeTime = meetingAttendances.isTardyStackFull();
final int tardyCount = countTardyByParticipant(participant, meetingAttendances);

final Optional<Event> upcomingEvent = eventRepository
.findFirstByMeetingIdAndDateGreaterThanEqualOrderByDate(meeting.getId(), today);
if (upcomingEvent.isEmpty()) {
return MyMeetingResponse.of(
meeting, tardyCount, isLoginUserMaster, isCoffeeTime, false, null
);
private void validateUsersExists(final List<Long> userIds, final List<User> users) {
if (users.size() != userIds.size()) {
throw new UserNotFoundException();
}
final Event event = upcomingEvent.get();
final LocalTime startTime = event.getStartTime();
final boolean isActive = event.isSameDate(today) && serverTimeManager.isAttendanceOpen(startTime);
final LocalTime attendanceOpenTime = serverTimeManager.calculateOpenTime(startTime);
final LocalTime attendanceClosedTime = serverTimeManager.calculateAttendanceCloseTime(startTime);
return MyMeetingResponse.of(
meeting, tardyCount, isLoginUserMaster, isCoffeeTime, isActive,
EventResponse.of(event, attendanceOpenTime, attendanceClosedTime)
);
}

private int countTardyByParticipant(final Participant participant, final MeetingAttendances meetingAttendances) {
final ParticipantAttendances participantAttendances = meetingAttendances
.extractAttendancesByParticipant(participant);
return participantAttendances.countTardy();
}

private void validateAssignee(final Long loginId, final Long participantId) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.woowacourse.moragora.config;

import com.woowacourse.moragora.application.AttendanceScheduler;
import com.woowacourse.moragora.application.ServerTimeManager;
import com.woowacourse.moragora.domain.attendance.AttendanceRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
Expand All @@ -11,17 +12,20 @@ public class SchedulerConfig {

private final AttendanceRepository attendanceRepository;
private final boolean isScheduler;
private final ServerTimeManager serverTimeManager;

public SchedulerConfig(final AttendanceRepository attendanceRepository,
@Value("${jvm.args.scheduler}") final boolean isScheduler) {
this.attendanceRepository = attendanceRepository;
public SchedulerConfig(@Value("${jvm.args.scheduler}") final boolean isScheduler,
final AttendanceRepository attendanceRepository,
final ServerTimeManager serverTimeManager) {
this.isScheduler = isScheduler;
this.attendanceRepository = attendanceRepository;
this.serverTimeManager = serverTimeManager;
}

@Bean
public AttendanceScheduler scheduleService() {
if (isScheduler) {
return new AttendanceScheduler(attendanceRepository);
return new AttendanceScheduler(attendanceRepository, serverTimeManager);
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ List<Attendance> findByParticipantIdInAndEventDateLessThanEqual(
void deleteByParticipantIdIn(@Param("participantIds") final List<Long> participantIds);

@Modifying(flushAutomatically = true, clearAutomatically = true)
@Query("update Attendance a set a.status ='TARDY' where a.status = 'NONE' and a.event in "
+ "(select e from Event e where e.date = :today and e.startTime <= :now)")
int updateByEventDateTimeAndStatus(@Param("today") final LocalDate today, @Param("now") final LocalTime now);
@Query("update Attendance a "
+ " set a.status ='TARDY' "
+ " where a.status = 'NONE' "
+ " and a.event in "
+ " (select e from Event e "
+ " where e.date = :date "
+ " and e.startTime <= :time) ")
int updateByEventDateTimeAndStatus(@Param("date") final LocalDate date,
@Param("time") final LocalTime time);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,6 @@ public MeetingAttendances(final List<Attendance> values, final int numberOfParti
this.numberOfParticipants = numberOfParticipants;
}

public ParticipantAttendances extractAttendancesByParticipant(final Participant participant) {
final List<Attendance> attendances = values.stream()
.filter(attendance -> attendance.getParticipant().equals(participant))
.collect(Collectors.toList());
return new ParticipantAttendances(attendances);
}

public int countTardy() {
return (int) values.stream()
.filter(Attendance::isEnabled)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.woowacourse.moragora.domain.event;

import com.woowacourse.moragora.application.ServerTimeManager;
import com.woowacourse.moragora.domain.meeting.Meeting;
import com.woowacourse.moragora.exception.event.IllegalAlreadyStartedEventException;
import com.woowacourse.moragora.exception.meeting.IllegalEntranceLeaveTimeException;
Expand Down Expand Up @@ -96,6 +97,18 @@ private void validateAlreadyStart(final LocalDateTime now) {
}
}

public boolean isActive(final LocalDate today, final ServerTimeManager serverTimeManager) {
return isSameDate(today) && serverTimeManager.isAttendanceOpen(startTime);
}

public LocalTime getOpenTime(final ServerTimeManager serverTimeManager) {
return serverTimeManager.calculateOpenTime(startTime);
}

public LocalTime getCloseTime(final ServerTimeManager serverTimeManager) {
return serverTimeManager.calculateAttendanceCloseTime(startTime);
}

private void validateStartEndTime(final LocalTime entranceTime, final LocalTime leaveTime) {
if (entranceTime.isAfter(leaveTime)) {
throw new IllegalEntranceLeaveTimeException();
Expand Down
Loading