Divide MemberService separately

This commit is contained in:
MangKyu
2023-02-18 18:50:01 +09:00
parent 99a3ace09e
commit f05bbfef9c
10 changed files with 301 additions and 78 deletions

View File

@@ -0,0 +1,126 @@
package com.mangkyu.employment.interview.app.mail.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mangkyu.employment.interview.app.member.entity.Member;
import com.mangkyu.employment.interview.app.quiz.dto.AddQuizRequestHolder;
import com.mangkyu.employment.interview.app.quiz.entity.Quiz;
import com.mangkyu.employment.interview.app.quiz.repository.QuizRepository;
import com.mangkyu.employment.interview.app.quiz.service.QuizService;
import com.mangkyu.employment.interview.app.solvedquiz.repository.SolvedQuizRepository;
import com.mangkyu.employment.interview.app.member.repository.MemberRepository;
import com.mangkyu.employment.interview.enums.value.QuizCategory;
import com.mangkyu.employment.interview.enums.value.QuizDay;
import com.mangkyu.employment.interview.enums.value.QuizLevel;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.util.*;
@RequiredArgsConstructor
public class MailController {
private final MailService mailService;
private final MemberRepository memberRepository;
private final QuizService quizService;
private final QuizRepository quizRepository;
private final SolvedQuizRepository solvedQuizRepository;
private final RequestMappingHandlerMapping requestMappingHandlerMapping;
@PostConstruct
public void init() {
final Set<QuizDay> quizDaySet = new HashSet<>();
quizDaySet.add(QuizDay.MONDAY);
quizDaySet.add(QuizDay.WEDNESDAY);
quizDaySet.add(QuizDay.SATURDAY);
final Set<QuizCategory> quizCategorySet = new HashSet<>();
quizCategorySet.add(QuizCategory.JAVA);
quizCategorySet.add(QuizCategory.SPRING);
quizCategorySet.add(QuizCategory.SERVER);
quizCategorySet.add(QuizCategory.NETWORK);
quizCategorySet.add(QuizCategory.OPERATING_SYSTEM);
quizCategorySet.add(QuizCategory.DATABASE);
quizCategorySet.add(QuizCategory.PROGRAMMING);
quizCategorySet.add(QuizCategory.DATA_STRUCTURE);
quizCategorySet.add(QuizCategory.ALGORITHM);
quizCategorySet.add(QuizCategory.PROBLEM_SOLVING);
quizCategorySet.add(QuizCategory.CULTURE);
quizCategorySet.add(QuizCategory.EXPERIENCE);
quizCategorySet.add(QuizCategory.PERSONALITY);
final Member member1 = Member.builder()
.resourceId(UUID.randomUUID().toString())
.email("whalsrb1226@naver.com")
.quizLevel(QuizLevel.JUNIOR)
.quizDaySet(quizDaySet)
// .quizCategorySet(quizCategorySet)
.solvedQuizList(Collections.emptyList())
.build();
final Member member2 = Member.builder()
.resourceId(UUID.randomUUID().toString())
.email("whalsrb1226@naver.com")
.quizLevel(QuizLevel.JUNIOR)
.solvedQuizList(Collections.emptyList())
.build();
memberRepository.save(member1);
// userRepository.save(user2);
System.out.println("http://localhost:8080/quiz/editView/" + quizRepository.save(quiz(QuizCategory.JAVA, Collections.singletonList(QuizLevel.JUNIOR), "Junit4 vs Junit5 차이는 무엇인가?")).getResourceId());
System.out.println("http://localhost:8080/quiz/editView/" + quizRepository.save(quiz(QuizCategory.JAVA, Collections.singletonList(QuizLevel.JUNIOR), "Junit4 vs Junit5 차이는 무엇인가?")).getResourceId());
System.out.println("http://localhost:8080/quiz/editView/" + quizRepository.save(quiz(QuizCategory.JAVA, Collections.singletonList(QuizLevel.JUNIOR), "Junit4 vs Junit5 차이는 무엇인가?")).getResourceId());
System.out.println("http://localhost:8080/quiz/editView/" + quizRepository.save(quiz(QuizCategory.JAVA, Collections.singletonList(QuizLevel.JUNIOR), "Junit4 vs Junit5 차이는 무엇인가?")).getResourceId());
System.out.println("http://localhost:8080/quiz/editView/" + quizRepository.save(quiz(QuizCategory.JAVA, Collections.singletonList(QuizLevel.JUNIOR), "Junit4 vs Junit5 차이는 무엇인가?")).getResourceId());
System.out.println("http://localhost:8080/quiz/editView/" + quizRepository.save(quiz(QuizCategory.PROGRAMMING, Arrays.asList(QuizLevel.JUNIOR, QuizLevel.SENIOR), "DDD의 Layered Architecture에서 Presentation, Application, Domain, InfraStructure layer의 역할에 대해 설명해 주세요.")).getResourceId());
System.out.println("http://localhost:8080/quiz/editView/" + quizRepository.save(quiz(QuizCategory.SPRING, Collections.singletonList(QuizLevel.JUNIOR), "Spring Framework에서 사용되는 대표적인 디자인 패턴과 적용된 곳을 설명해주세요.")).getResourceId());
System.out.println("http://localhost:8080/quiz/editView/" + quizRepository.save(quiz(QuizCategory.DATABASE, Arrays.asList(QuizLevel.JUNIOR, QuizLevel.SENIOR), "MSA의 장점과 단점에 대해 설명해주세요")).getResourceId());
// quizRepository.save(quiz(QuizCategory.JAVA, Collections.singletonList(QuizLevel.JUNIOR), "당신은 ㅜ구인가"));
// quizRepository.save(quiz(QuizCategory.DATABASE, Arrays.asList(QuizLevel.JUNIOR, QuizLevel.SENIOR), "퇴근합시다"));
}
@Transactional
@GetMapping("/temp")
public ResponseEntity<AddQuizRequestHolder> sendMail1() throws IOException {
final File file = ResourceUtils.getFile("classpath:quiz/quiz-211220.json");
final ObjectMapper objectMapper = new ObjectMapper();
final AddQuizRequestHolder addQuizRequestHolder = objectMapper.readValue(file, AddQuizRequestHolder.class);
return ResponseEntity.ok(addQuizRequestHolder);
}
@GetMapping("/temp2")
public ResponseEntity<Void> sendMail2() {
mailService.sendMail("whalsrb1226@naver.com", quizList2(), true);
return ResponseEntity.noContent().build();
}
private List<Quiz> quizList1() {
return Arrays.asList(
quiz(QuizCategory.JAVA, Collections.singletonList(QuizLevel.JUNIOR), "Junit4 vs Junit5 차이는 무엇인가?"),
quiz(QuizCategory.DATABASE, Arrays.asList(QuizLevel.JUNIOR, QuizLevel.SENIOR), "Layered Architecture에서 Presentation, Application, Domain, InfraStructure layer의 역할에 대해 설명해 주세요."),
quiz(QuizCategory.SPRING, Arrays.asList(QuizLevel.JUNIOR, QuizLevel.SENIOR, QuizLevel.NEW), "Spring에서 템플릿 메소드 패턴이 사용된 곳은(디스패처 서블릿)")
);
}
private List<Quiz> quizList2() {
return Arrays.asList(
quiz(QuizCategory.DATABASE, Arrays.asList(QuizLevel.JUNIOR, QuizLevel.SENIOR), "Layered Architecture에서 Presentation, Application, Domain, InfraStructure layer의 역할에 대해 설명해 주세요."),
quiz(QuizCategory.SPRING, Arrays.asList(QuizLevel.JUNIOR, QuizLevel.SENIOR, QuizLevel.NEW), "Spring에서 템플릿 메소드 패턴이 사용된 곳은(디스패처 서블릿)")
);
}
private Quiz quiz(final QuizCategory quizCategory, final List<QuizLevel> quizLevelList, final String title) {
return Quiz.builder()
.resourceId(UUID.randomUUID().toString())
.quizCategory(quizCategory)
.quizLevel(quizLevelList)
.title(title).build();
}
}

View File

@@ -1,6 +1,6 @@
package com.mangkyu.employment.interview.app.member.controller;
import com.mangkyu.employment.interview.app.member.service.MemberService;
import com.mangkyu.employment.interview.app.member.service.AddMemberService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@@ -14,11 +14,11 @@ import javax.validation.Valid;
@RequiredArgsConstructor
class AddMemberController {
private final MemberService memberService;
private final AddMemberService memberService;
@PostMapping("/members")
public ResponseEntity<Void> add(@RequestBody @Valid final AddMemberRequest request) {
memberService.addUser(request);
memberService.add(request);
return ResponseEntity.status(HttpStatus.CREATED)
.build();

View File

@@ -3,35 +3,25 @@ package com.mangkyu.employment.interview.app.member.service;
import com.mangkyu.employment.interview.app.member.controller.AddMemberRequest;
import com.mangkyu.employment.interview.app.member.entity.Member;
import com.mangkyu.employment.interview.app.member.repository.MemberRepository;
import com.mangkyu.employment.interview.enums.value.QuizDay;
import lombok.RequiredArgsConstructor;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MemberService {
@Transactional
public class AddMemberService {
private final MemberRepository memberRepository;
@Deprecated // TODO: remove modelmapper
private final ModelMapper modelMapper;
@Transactional
public void addUser(final AddMemberRequest addMemberRequest) {
public void add(final AddMemberRequest addMemberRequest) {
final Member member = modelMapper.map(addMemberRequest, Member.class);
memberRepository.save(member);
}
@Transactional
public void disableUser(final Member member) {
member.setIsEnable(false);
memberRepository.save(member);
}
public List<Member> getEnabledUserList(final QuizDay QuizDay) {
return memberRepository.findAllByIsEnableTrueAndQuizDaySetIs(QuizDay);
}
}

View File

@@ -0,0 +1,22 @@
package com.mangkyu.employment.interview.app.member.service;
import com.mangkyu.employment.interview.app.member.entity.Member;
import com.mangkyu.employment.interview.app.member.repository.MemberRepository;
import com.mangkyu.employment.interview.enums.value.QuizDay;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class GetMemberService {
private final MemberRepository memberRepository;
public List<Member> getEnabledUserList(final QuizDay QuizDay) {
return memberRepository.findAllByIsEnableTrueAndQuizDaySetIs(QuizDay);
}
}

View File

@@ -0,0 +1,21 @@
package com.mangkyu.employment.interview.app.member.service;
import com.mangkyu.employment.interview.app.member.entity.Member;
import com.mangkyu.employment.interview.app.member.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
@Transactional
public class UpdateMemberService {
private final MemberRepository memberRepository;
public void disableUser(final Member member) {
member.setIsEnable(false);
memberRepository.save(member);
}
}

View File

@@ -2,10 +2,11 @@ package com.mangkyu.employment.interview.cron;
import com.mangkyu.employment.interview.app.mail.service.MailService;
import com.mangkyu.employment.interview.app.member.entity.Member;
import com.mangkyu.employment.interview.app.member.service.UpdateMemberService;
import com.mangkyu.employment.interview.app.quiz.entity.Quiz;
import com.mangkyu.employment.interview.app.quiz.service.QuizService;
import com.mangkyu.employment.interview.app.solvedquiz.service.SolvedQuizService;
import com.mangkyu.employment.interview.app.member.service.MemberService;
import com.mangkyu.employment.interview.app.member.service.GetMemberService;
import com.mangkyu.employment.interview.enums.value.QuizDay;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -22,7 +23,8 @@ import java.util.List;
@Slf4j
public class SendQuizCronJob {
private final MemberService memberService;
private final GetMemberService memberService;
private final UpdateMemberService updateMemberService;
private final QuizService quizService;
private final MailService mailService;
private final SolvedQuizService solvedQuizService;
@@ -33,12 +35,12 @@ public class SendQuizCronJob {
*/
// @Scheduled(cron = "*/30 * * * * *") // every 30 seconds
@Scheduled(cron = "0 1 0 * * ?")
@Scheduled(cron = "0 15 0 * * ?")
@Transactional
public void sendQuizMail() {
log.info("========== sendQuizMail ============");
final DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
final List<Member> memberList = memberService.getEnabledUserList(QuizDay.findQuizDay(dayOfWeek));
log.info("========== sendQuizMail member={} ============", memberList.size());
for (final Member member : memberList) {
sendUnsolvedQuizForUser(member);
}
@@ -50,7 +52,7 @@ public class SendQuizCronJob {
final List<Quiz> randomQuizList = quizService.getRandomQuizListUnderLimit(unsolvedQuizList, member.getQuizSize());
if (isLastMail) {
memberService.disableUser(member);
updateMemberService.disableUser(member);
}
mailService.sendMail(member.getEmail(), randomQuizList, isLastMail);

View File

@@ -14,85 +14,43 @@ import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import org.modelmapper.ModelMapper;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class MemberServiceTest {
class AddMemberServiceTest {
@InjectMocks
private MemberService target;
private AddMemberService target;
@Mock
private MemberRepository memberRepository;
@Spy
private ModelMapper modelMapper = new ModelMapperConfig().modelMapper();
private final QuizDay quizDay = QuizDay.MONDAY;
@Test
public void addUserSuccess() {
void addUser_Success() {
// given
final Set<QuizDay> quizDaySet = new HashSet<>();
quizDaySet.add(QuizDay.MONDAY);
quizDaySet.add(QuizDay.WEDNESDAY);
quizDaySet.add(QuizDay.SATURDAY);
final AddMemberRequest addMemberRequest = AddMemberRequest.builder()
final AddMemberRequest request = AddMemberRequest.builder()
.email("whalsrb1226@gmail.com")
.quizLevel(QuizLevel.JUNIOR)
.quizDaySet(quizDaySet)
.build();
// when
target.addUser(addMemberRequest);
// then
}
@Test
public void disableUserSuccess() {
// given
final Member member = user(true);
// when
target.disableUser(member);
target.add(request);
// then
// verify
verify(memberRepository, times(1)).save(member);
verify(memberRepository, times(1)).save(any(Member.class));
}
@Test
public void getEnabledUserListSuccess() {
// given
final List<Member> enabledMemberList = Arrays.asList(user(true), user(true));
doReturn(enabledMemberList).when(memberRepository).findAllByIsEnableTrueAndQuizDaySetIs(quizDay);
// when
final List<Member> result = target.getEnabledUserList(quizDay);
// then
assertThat(result.size()).isEqualTo(enabledMemberList.size());
// verify
verify(memberRepository, times(1)).findAllByIsEnableTrueAndQuizDaySetIs(quizDay);
}
private Member user(final boolean isEnable) {
final Member member = Member.builder()
.email("minkyu@test.com")
.quizLevel(QuizLevel.JUNIOR)
.build();
member.setIsEnable(isEnable);
return member;
}
}

View File

@@ -0,0 +1,53 @@
package com.mangkyu.employment.interview.app.member.service;
import com.mangkyu.employment.interview.app.member.entity.Member;
import com.mangkyu.employment.interview.app.member.repository.MemberRepository;
import com.mangkyu.employment.interview.enums.value.QuizDay;
import com.mangkyu.employment.interview.enums.value.QuizLevel;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doReturn;
@ExtendWith(MockitoExtension.class)
class GetMemberServiceTest {
@InjectMocks
private GetMemberService target;
@Mock
private MemberRepository memberRepository;
@Test
void getEnabledUserList_Success() {
// given
final List<Member> enabledMemberList = List.of(user(), user());
doReturn(enabledMemberList)
.when(memberRepository)
.findAllByIsEnableTrueAndQuizDaySetIs(QuizDay.MONDAY);
// when
final List<Member> result = target.getEnabledUserList(QuizDay.MONDAY);
// then
assertThat(result.size()).isEqualTo(enabledMemberList.size());
}
private Member user() {
final Member member = Member.builder()
.email("minkyu@test.com")
.quizLevel(QuizLevel.JUNIOR)
.build();
member.setIsEnable(true);
return member;
}
}

View File

@@ -0,0 +1,47 @@
package com.mangkyu.employment.interview.app.member.service;
import com.mangkyu.employment.interview.app.member.entity.Member;
import com.mangkyu.employment.interview.app.member.repository.MemberRepository;
import com.mangkyu.employment.interview.enums.value.QuizLevel;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
class UpdateMemberServiceTest {
@InjectMocks
private UpdateMemberService target;
@Mock
private MemberRepository memberRepository;
@Test
void disableUserSuccess() {
// given
final Member member = user();
// when
target.disableUser(member);
// then
// verify
verify(memberRepository, times(1)).save(member);
}
private Member user() {
final Member member = Member.builder()
.email("minkyu@test.com")
.quizLevel(QuizLevel.JUNIOR)
.build();
member.setIsEnable(true);
return member;
}
}

View File

@@ -2,10 +2,11 @@ package com.mangkyu.employment.interview.cron;
import com.mangkyu.employment.interview.app.mail.service.MailService;
import com.mangkyu.employment.interview.app.member.entity.Member;
import com.mangkyu.employment.interview.app.member.service.UpdateMemberService;
import com.mangkyu.employment.interview.app.quiz.entity.Quiz;
import com.mangkyu.employment.interview.app.quiz.service.QuizService;
import com.mangkyu.employment.interview.app.solvedquiz.service.SolvedQuizService;
import com.mangkyu.employment.interview.app.member.service.MemberService;
import com.mangkyu.employment.interview.app.member.service.GetMemberService;
import com.mangkyu.employment.interview.enums.value.QuizDay;
import com.mangkyu.employment.interview.enums.value.QuizLevel;
import org.junit.jupiter.api.BeforeEach;
@@ -29,7 +30,10 @@ class SendQuizCronJobTest {
private SendQuizCronJob target;
@Mock
private MemberService memberService;
private GetMemberService memberService;
@Mock
private UpdateMemberService updateMemberService;
@Mock
private QuizService quizService;
@Mock
@@ -68,7 +72,7 @@ class SendQuizCronJobTest {
// verify
verify(quizService, times(0)).getUnsolvedQuizList(member.getId(), member.getQuizLevel(), member.getQuizCategorySet());
verify(quizService, times(0)).getRandomQuizListUnderLimit(anyList(), anyInt());
verify(memberService, times(0)).disableUser(member);
verify(updateMemberService, times(0)).disableUser(member);
verify(mailService, times(0)).sendMail(anyString(), anyList(), anyBoolean());
verify(solvedQuizService, times(0)).addSolvedQuizList(any(Member.class), anyList());
}
@@ -97,7 +101,7 @@ class SendQuizCronJobTest {
// verify
verify(quizService, times(1)).getUnsolvedQuizList(member.getId(), member.getQuizLevel(), member.getQuizCategorySet());
verify(quizService, times(1)).getRandomQuizListUnderLimit(unsolvedQuizList, member.getQuizSize());
verify(memberService, times(0)).disableUser(member);
verify(updateMemberService, times(0)).disableUser(member);
verify(mailService, times(1)).sendMail(member.getEmail(), randomQuizList, false);
verify(solvedQuizService, times(1)).addSolvedQuizList(member, randomQuizList);
}
@@ -126,7 +130,7 @@ class SendQuizCronJobTest {
// verify
verify(quizService, times(1)).getUnsolvedQuizList(member.getId(), member.getQuizLevel(), member.getQuizCategorySet());
verify(quizService, times(1)).getRandomQuizListUnderLimit(unsolvedQuizList, member.getQuizSize());
verify(memberService, times(1)).disableUser(member);
verify(updateMemberService, times(1)).disableUser(member);
verify(mailService, times(1)).sendMail(member.getEmail(), randomQuizList, true);
verify(solvedQuizService, times(1)).addSolvedQuizList(member, randomQuizList);
}