본문 바로가기
스프링과 JPA 기반 웹 어플리케이션 개발/5부 모임

68. 모임 참가 신청 수락 및 출석 체크

by Backchus 2020. 5. 6.

참가 신청 수락 및 취소 출석 체크

 

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

 

'스프링과 JPA 기반 웹 어플리케이션 개발 > 5부 모임' 카테고리의 다른 글

67. 모임 참가 신청 및 취소  (0) 2020.05.06
66. 모임 취소  (0) 2020.05.04
65. 모임 수정  (0) 2020.05.04
64. 모임 목록 조회  (0) 2020.05.04
63. 모임 조회  (0) 2020.05.04