스프링과 JPA 기반 웹 어플리케이션 개발/5부 모임
68. 모임 참가 신청 수락 및 출석 체크
Backchus
2020. 5. 6. 19:04
Event의 Enrollment 목록 순서를 정하려면
@OneToMany(mappedBy = "event")
@OrderBy("enrolledAt")
private List<Enrollment> enrollments = new ArrayList<>();
스프링 데이너 JPA가 제공하는 도메인 컨버터 사용하기
@GetMapping("/events/{eventId}/enrollments/{enrollmentId}/reject")
public String rejectEnrollment(@PathVariable Long eventId, @PathVariable Long enrollmentId) {
Event event = eventRepository.findById(eventId).orElseThrow();
Enrollment enrollment = enrollmentRepository.findById(enrollmentId).orElseThrow();
}
@GetMapping("/events/{eventId}/enrollments/{enrollmentId}/reject")
public String rejectEnrollment(@PathVariable("eventId") Event event,
@PathVariable("enrollmentId") Enrollment enrollment) {
}
이전 코드 리팩토링
package me.weekbelt.studyolle.event;
@Service
@Transactional
@RequiredArgsConstructor
public class EventService {
// .......
public void cancelEnrollment(Event event, Account account) {
Enrollment enrollment = enrollmentRepository.findByEventAndAccount(event, account);
if(!enrollment.isAttended()) { // 조건문 추가
event.removeEnrollment(enrollment);
enrollmentRepository.delete(enrollment);
event.acceptNextWaitingEnrollment();
}
}
}
package me.weekbelt.studyolle.domain;
@NamedEntityGraph(
name = "Event.withEnrollments",
attributeNodes = @NamedAttributeNode("enrollments")
)
@NoArgsConstructor
@Getter
@Setter
@EqualsAndHashCode(of = "id")
@Entity
public class Event {
// ......
public boolean isEnrollableFor(UserAccount userAccount) {
return isNotClosed() && !this.isAttended(userAccount) && !isAlreadyEnrolled(userAccount);
}
public boolean isDisenrollableFor(UserAccount userAccount) {
return isNotClosed() && !this.isAttended(userAccount) && isAlreadyEnrolled(userAccount);
}
// .......
}
참가 신청 수락 요청, 출석체크 요청을 처리하는 핸들러 작성
package me.weekbelt.studyolle.event;
@Controller
@RequestMapping("/study/{path}")
@RequiredArgsConstructor
public class EventController {
// .........
private final EnrollmentService enrollmentService;
// .........
// 화면 칸 간격을 맞추기위해 Get요청을보냄 원래 POST요청을 해야함
// 모임 참가 신청 수락
@GetMapping("/events/{eventId}/enrollments/{enrollmentId}/accept")
public String acceptEnrollment(@CurrentAccount Account account, @PathVariable String path,
@PathVariable Long eventId, @PathVariable Long enrollmentId) {
Study study = studyService.getStudyToUpdate(account, path);
Event event = eventService.findEventById(eventId);
Enrollment enrollment = enrollmentService.findEnrollmentById(enrollmentId);
eventService.acceptEnrollment(event, enrollment);
return "redirect:/study/" + study.getEncodedPath() + "/events/" + eventId;
}
// 모임 참가 신청 거절
@GetMapping("/events/{eventId}/enrollments/{enrollmentId}/reject")
public String rejectEnrollment(@CurrentAccount Account account, @PathVariable String path,
@PathVariable Long eventId, @PathVariable Long enrollmentId) {
Study study = studyService.getStudyToUpdate(account, path);
Event event = eventService.findEventById(eventId);
Enrollment enrollment = enrollmentService.findEnrollmentById(enrollmentId);
eventService.rejectEnrollment(event, enrollment);
return "redirect:/study/" + study.getEncodedPath() + "/events/" + eventId;
}
// 출석 체크인
@GetMapping("/events/{eventId}/enrollments/{enrollmentId}/checkin")
public String checkInEnrollment(@CurrentAccount Account account, @PathVariable String path,
@PathVariable Long eventId, @PathVariable Long enrollmentId) {
Study study = studyService.getStudyToUpdate(account, path);
Event event = eventService.findEventById(eventId);
Enrollment enrollment = enrollmentService.findEnrollmentById(enrollmentId);
eventService.checkInEnrollment(enrollment);
return "redirect:/study/" + study.getEncodedPath() + "/events/" + eventId;
}
// 체크인 취소
@GetMapping("/events/{eventId}/enrollments/{enrollmentId}/cancel-checkin")
public String cancelCheckInEnrollment(@CurrentAccount Account account, @PathVariable String path,
@PathVariable Long eventId, @PathVariable Long enrollmentId) {
Study study = studyService.getStudyToUpdate(account, path);
Event event = eventService.findEventById(eventId);
Enrollment enrollment = enrollmentService.findEnrollmentById(enrollmentId);
eventService.cancelCheckInEnrollment(enrollment);
return "redirect:/study/" + study.getEncodedPath() + "/events/" + eventId;
}
}
EnrollmentService에서 enrollmentId의 Enrollment를 불러오기 위한 findEventById메소드 작성
package me.weekbelt.studyolle.event;
@RequiredArgsConstructor
@Transactional
@Service
public class EnrollmentService {
private final EnrollmentRepository enrollmentRepository;
public Enrollment findEnrollmentById(Long enrollmentId) {
return enrollmentRepository.findById(enrollmentId)
.orElseThrow(() -> new IllegalArgumentException("찾는 참가자가 없습니다."));
}
}
참가신청 수락/거절/체크인/체크인 취소를 처리하는 메소드 작성
package me.weekbelt.studyolle.event;
@Service
@Transactional
@RequiredArgsConstructor
public class EventService {
// .......
public void acceptEnrollment(Event event, Enrollment enrollment) {
event.accept(enrollment);
}
public void rejectEnrollment(Event event, Enrollment enrollment) {
event.reject(enrollment);
}
public void checkInEnrollment(Enrollment enrollment) {
enrollment.setAttended(true);
}
public void cancelCheckInEnrollment(Enrollment enrollment) {
enrollment.setAttended(false);
}
}
참가신청 수락을 Event엔티티에 위임시키때문에 accept, reject메소드를 Event엔티티에 작성한다.
package me.weekbelt.studyolle.domain;
@NamedEntityGraph(
name = "Event.withEnrollments",
attributeNodes = @NamedAttributeNode("enrollments")
)
@NoArgsConstructor
@Getter
@Setter
@EqualsAndHashCode(of = "id")
@Entity
public class Event {
// .........
public void accept(Enrollment enrollment) {
if(this.eventType == EventType.CONFIRMATIVE && this.limitOfEnrollments > this.getNumberOfAcceptedEnrollments()) {
enrollment.setAccepted(true);
}
}
public void reject(Enrollment enrollment) {
if(this.eventType == EventType.CONFIRMATIVE) {
enrollment.setAccepted(false);
}
}
}
앞서 작성한 핸들러를 좀더 간단히 만들 수 있다.
package me.weekbelt.studyolle.event;
@Controller
@RequestMapping("/study/{path}")
@RequiredArgsConstructor
public class EventController {
// .......
@GetMapping("/events/{id}")
public String getEvent(@CurrentAccount Account account, @PathVariable String path,
@PathVariable("id") Event event, Model model) {
model.addAttribute(account);
model.addAttribute(event);
model.addAttribute(studyService.getStudy(path));
return "event/view";
}
// .......
@GetMapping("/events/{id}/edit")
public String updateEventForm(@CurrentAccount Account account, @PathVariable String path,
@PathVariable("id") Event event, Model model) {
Study study = studyService.getStudyToUpdate(account, path);
model.addAttribute("account", account);
model.addAttribute("study", study);
model.addAttribute("event", event);
model.addAttribute(modelMapper.map(event, EventForm.class));
return "event/update-form";
}
@PostMapping("/events/{id}/edit")
public String updateEventSubmit(@CurrentAccount Account account, @PathVariable String path,
@PathVariable("id") Event event, @Valid EventForm eventForm,
Errors errors, Model model) {
Study study = studyService.getStudyToUpdate(account, path);
// 화면에 EventType 창이 없어도 서버에 요청이 가능하기 때문에 이것을 방지하기 위해
eventForm.setEventType(event.getEventType());
eventValidator.validateUpdateForm(eventForm, event, errors);
if (errors.hasErrors()) {
model.addAttribute("account", account);
model.addAttribute("study", study);
model.addAttribute("event", event);
return "event/update-form";
}
eventService.updateEvent(event, eventForm);
return "redirect:/study/" + study.getEncodedPath() + "/events/" + event.getId();
}
@DeleteMapping("/events/{id}")
public String cancelEvent(@CurrentAccount Account account, @PathVariable String path,
@PathVariable("id") Event event) {
Study study = studyService.getStudyToUpdateStatus(account, path);
eventService.deleteEvent(event);
return "redirect:/study/" + study.getEncodedPath() + "/events";
}
@PostMapping("/events/{id}/enroll")
public String newEnrollment(@CurrentAccount Account account, @PathVariable String path,
@PathVariable("id") Event event) {
Study study = studyService.getStudyToEnroll(path);
eventService.newEnrollment(event, account);
return "redirect:/study/" + study.getEncodedPath() + "/events/" + event.getId();
}
@PostMapping("/events/{id}/disenroll")
public String cancelEnrollment(@CurrentAccount Account account, @PathVariable String path,
@PathVariable("id") Event event) {
Study study = studyService.getStudyToEnroll(path);
eventService.cancelEnrollment(event, account);
return "redirect:/study/" + study.getEncodedPath() + "/events/" + event.getId();
}
// 화면 칸 간격을 맞추기위해 Get요청을보냄 원래 POST요청을 해야함
@GetMapping("/events/{eventId}/enrollments/{enrollmentId}/accept")
public String acceptEnrollment(@CurrentAccount Account account, @PathVariable String path,
@PathVariable("eventId") Event event, @PathVariable("enrollmentId") Enrollment enrollment) {
Study study = studyService.getStudyToUpdate(account, path);
eventService.acceptEnrollment(event, enrollment);
return "redirect:/study/" + study.getEncodedPath() + "/events/" + event.getId();
}
@GetMapping("/events/{eventId}/enrollments/{enrollmentId}/reject")
public String rejectEnrollment(@CurrentAccount Account account, @PathVariable String path,
@PathVariable("eventId") Event event, @PathVariable("enrollmentId") Enrollment enrollment) {
Study study = studyService.getStudyToUpdate(account, path);
eventService.rejectEnrollment(event, enrollment);
return "redirect:/study/" + study.getEncodedPath() + "/events/" + event.getId();
}
@GetMapping("/events/{eventId}/enrollments/{enrollmentId}/checkin")
public String checkInEnrollment(@CurrentAccount Account account, @PathVariable String path,
@PathVariable("eventId") Event event, @PathVariable("enrollmentId") Enrollment enrollment) {
Study study = studyService.getStudyToUpdate(account, path);
eventService.checkInEnrollment(enrollment);
return "redirect:/study/" + study.getEncodedPath() + "/events/" + event.getId();
}
@GetMapping("/events/{eventId}/enrollments/{enrollmentId}/cancel-checkin")
public String cancelCheckInEnrollment(@CurrentAccount Account account, @PathVariable String path,
@PathVariable("eventId") Event event, @PathVariable("enrollmentId") Enrollment enrollment) {
Study study = studyService.getStudyToUpdate(account, path);
eventService.cancelCheckInEnrollment(enrollment);
return "redirect:/study/" + study.getEncodedPath() + "/events/" + event.getId();
}
}
참고: https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-JPA-%EC%9B%B9%EC%95%B1#
스프링과 JPA 기반 웹 애플리케이션 개발 - 인프런
이 강좌에서 여러분은 실제로 운영 중인 서비스를 스프링, JPA 그리고 타임리프를 비롯한 여러 자바 기반의 여러 오픈 소스 기술을 사용하여 웹 애플리케이션을 개발하는 과정을 학습할 수 있습니다. 이 강좌를 충분히 학습한다면 여러분 만의 웹 서비스를 만들거나 취직에 도움이 될만한 포트폴리오를 만들 수 있을 겁니다. 중급이상 웹 개발 프레임워크 및 라이브러리 Java Spring Spring Boot Spring Data JPA Thymeleaf 온라인 강의
www.inflearn.com