diff --git a/loan/src/main/java/com/example/loan/controller/ApplicationController.java b/loan/src/main/java/com/example/loan/controller/ApplicationController.java index 7325c2a0..7a751a74 100644 --- a/loan/src/main/java/com/example/loan/controller/ApplicationController.java +++ b/loan/src/main/java/com/example/loan/controller/ApplicationController.java @@ -42,4 +42,12 @@ public class ApplicationController extends AbstractController { applicationService.delete(applicationId); return ok(); } + + @PostMapping("/{applicationId}/terms") + public ResponseDto acceptTerms( + @PathVariable Long applicationId, + @RequestBody ApplicationDto.AcceptTerms request + ) { + return ok(applicationService.acceptTerms(applicationId, request)); + } } diff --git a/loan/src/main/java/com/example/loan/domain/AcceptTerms.java b/loan/src/main/java/com/example/loan/domain/AcceptTerms.java new file mode 100644 index 00000000..c2c87adb --- /dev/null +++ b/loan/src/main/java/com/example/loan/domain/AcceptTerms.java @@ -0,0 +1,31 @@ +package com.example.loan.domain; + +import lombok.*; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import org.hibernate.annotations.Where; + +import javax.persistence.*; + +@Entity +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@DynamicInsert +@DynamicUpdate +@Where(clause = "is_deleted=false") +public class AcceptTerms extends BaseEntity { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(nullable = false, updatable = false) + private Long acceptTermsId; + + @Column(columnDefinition = "bigint NOT NULL COMMENT '신청 ID'") + private Long applicationId; + + @Column(columnDefinition = "bigint NOT NULL COMMENT '약관 ID'") + private Long termsId; + +} diff --git a/loan/src/main/java/com/example/loan/dto/ApplicationDto.java b/loan/src/main/java/com/example/loan/dto/ApplicationDto.java index 72a4de43..f0509f70 100644 --- a/loan/src/main/java/com/example/loan/dto/ApplicationDto.java +++ b/loan/src/main/java/com/example/loan/dto/ApplicationDto.java @@ -5,6 +5,7 @@ import lombok.*; import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDateTime; +import java.util.List; public class ApplicationDto implements Serializable { @@ -37,4 +38,14 @@ public class ApplicationDto implements Serializable { private LocalDateTime updatedAt; } + @NoArgsConstructor + @AllArgsConstructor + @Getter + @Setter + @Builder + public static class AcceptTerms { + + List acceptTermsIds; + } + } diff --git a/loan/src/main/java/com/example/loan/repository/AcceptTermsRepository.java b/loan/src/main/java/com/example/loan/repository/AcceptTermsRepository.java new file mode 100644 index 00000000..3e73f6bc --- /dev/null +++ b/loan/src/main/java/com/example/loan/repository/AcceptTermsRepository.java @@ -0,0 +1,9 @@ +package com.example.loan.repository; + +import com.example.loan.domain.AcceptTerms; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface AcceptTermsRepository extends JpaRepository { +} diff --git a/loan/src/main/java/com/example/loan/service/ApplicationService.java b/loan/src/main/java/com/example/loan/service/ApplicationService.java index d13f9cca..b741ddb3 100644 --- a/loan/src/main/java/com/example/loan/service/ApplicationService.java +++ b/loan/src/main/java/com/example/loan/service/ApplicationService.java @@ -11,4 +11,6 @@ public interface ApplicationService { ApplicationDto.Response update(Long applicationId, ApplicationDto.Request request); void delete(Long applicationId); + + Boolean acceptTerms(Long applicationId, ApplicationDto.AcceptTerms request); } diff --git a/loan/src/main/java/com/example/loan/service/ApplicationServiceImpl.java b/loan/src/main/java/com/example/loan/service/ApplicationServiceImpl.java index 387426f7..bf2af980 100644 --- a/loan/src/main/java/com/example/loan/service/ApplicationServiceImpl.java +++ b/loan/src/main/java/com/example/loan/service/ApplicationServiceImpl.java @@ -1,21 +1,32 @@ package com.example.loan.service; +import com.example.loan.domain.AcceptTerms; import com.example.loan.domain.Application; +import com.example.loan.domain.Terms; import com.example.loan.dto.ApplicationDto; import com.example.loan.exception.BaseException; import com.example.loan.exception.ResultType; +import com.example.loan.repository.AcceptTermsRepository; import com.example.loan.repository.ApplicationRepository; +import com.example.loan.repository.TermsRepository; import lombok.RequiredArgsConstructor; import org.modelmapper.ModelMapper; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import java.time.LocalDateTime; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor public class ApplicationServiceImpl implements ApplicationService { private final ApplicationRepository applicationRepository; + private final TermsRepository termsRepository; + private final AcceptTermsRepository acceptTermsRepository; private final ModelMapper modelMapper; @Override @@ -60,4 +71,44 @@ public class ApplicationServiceImpl implements ApplicationService { applicationRepository.save(application); } + + @Override + public Boolean acceptTerms(Long applicationId, ApplicationDto.AcceptTerms request) { + applicationRepository.findById(applicationId) + .orElseThrow(() -> new BaseException(ResultType.SYSTEM_ERROR)); + + + List termsList = termsRepository.findAll(Sort.by(Sort.Direction.ASC, "termsId")); + + if (termsList.isEmpty()) { + throw new BaseException(ResultType.SYSTEM_ERROR); + } + + List acceptTermsIds = request.getAcceptTermsIds(); + + if (termsList.size() != acceptTermsIds.size()) { + throw new BaseException(ResultType.SYSTEM_ERROR); + } + + List termsIds = termsList.stream() + .map(Terms::getTermsId) + .collect(Collectors.toList()); + + Collections.sort(acceptTermsIds); + + if (!new HashSet<>(termsIds).containsAll(acceptTermsIds)) { + throw new BaseException(ResultType.SYSTEM_ERROR); + } + + for (Long termsId : acceptTermsIds) { + acceptTermsRepository.save( + AcceptTerms.builder() + .termsId(termsId) + .applicationId(applicationId) + .build() + ); + } + + return true; + } } diff --git a/loan/src/test/java/com/example/loan/service/ApplicationServiceTest.java b/loan/src/test/java/com/example/loan/service/ApplicationServiceTest.java index f768b228..8a251a21 100644 --- a/loan/src/test/java/com/example/loan/service/ApplicationServiceTest.java +++ b/loan/src/test/java/com/example/loan/service/ApplicationServiceTest.java @@ -1,8 +1,13 @@ package com.example.loan.service; +import com.example.loan.domain.AcceptTerms; import com.example.loan.domain.Application; +import com.example.loan.domain.Terms; import com.example.loan.dto.ApplicationDto; +import com.example.loan.exception.BaseException; +import com.example.loan.repository.AcceptTermsRepository; import com.example.loan.repository.ApplicationRepository; +import com.example.loan.repository.TermsRepository; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentMatchers; @@ -11,8 +16,11 @@ import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import org.modelmapper.ModelMapper; +import org.springframework.data.domain.Sort; import java.math.BigDecimal; +import java.util.Arrays; +import java.util.List; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; @@ -28,6 +36,12 @@ class ApplicationServiceTest { @Mock private ApplicationRepository applicationRepository; + @Mock + private TermsRepository termsRepository; + + @Mock + private AcceptTermsRepository acceptTermsRepository; + @Spy private ModelMapper modelMapper; @@ -109,4 +123,111 @@ class ApplicationServiceTest { assertThat(entity.getIsDeleted()).isTrue(); } + + @Test + void Should_AddAcceptTerms_When_RequestAcceptTermsOfApplication() { + + Terms entityA = Terms.builder() + .termsId(1L) + .name("약관 1") + .termsDetailUrl("https://terms.com") + .build(); + + Terms entityB = Terms.builder() + .termsId(2L) + .name("약관 2") + .termsDetailUrl("https://terms.com/2") + .build(); + + List acceptTerms = Arrays.asList(1L, 2L); + + ApplicationDto.AcceptTerms request = ApplicationDto.AcceptTerms.builder() + .acceptTermsIds(acceptTerms) + .build(); + + Long findId = 1L; + + when(applicationRepository.findById(findId)).thenReturn( + Optional.ofNullable(Application.builder().build()) + ); + + when(termsRepository.findAll(Sort.by(Sort.Direction.ASC, "termsId"))) + .thenReturn(Arrays.asList(entityA, entityB)); + + when(acceptTermsRepository.save(ArgumentMatchers.any(AcceptTerms.class))) + .thenReturn(AcceptTerms.builder().build()); + + + Boolean actual = applicationService.acceptTerms(findId, request); + + assertThat(actual).isTrue(); + } + + @Test + void Should_ThrownException_When_RequestNotAllAcceptTermsOfApplication() { + + Terms entityA = Terms.builder() + .termsId(1L) + .name("약관 1") + .termsDetailUrl("https://terms.com") + .build(); + + Terms entityB = Terms.builder() + .termsId(2L) + .name("약관 2") + .termsDetailUrl("https://terms.com/2") + .build(); + + List acceptTerms = Arrays.asList(1L); + + ApplicationDto.AcceptTerms request = ApplicationDto.AcceptTerms.builder() + .acceptTermsIds(acceptTerms) + .build(); + + Long findId = 1L; + + when(applicationRepository.findById(findId)).thenReturn( + Optional.ofNullable(Application.builder().build()) + ); + + when(termsRepository.findAll(Sort.by(Sort.Direction.ASC, "termsId"))) + .thenReturn(Arrays.asList(entityA, entityB)); + + assertThatThrownBy(() -> applicationService.acceptTerms(findId, request)) + .isInstanceOf(BaseException.class); + } + + @Test + void Should_ThrownException_When_RequestNotExistAcceptTermsOfApplication() { + + Terms entityA = Terms.builder() + .termsId(1L) + .name("약관 1") + .termsDetailUrl("https://terms.com") + .build(); + + Terms entityB = Terms.builder() + .termsId(2L) + .name("약관 2") + .termsDetailUrl("https://terms.com/2") + .build(); + + List acceptTerms = Arrays.asList(1L, 3L); + + ApplicationDto.AcceptTerms request = ApplicationDto.AcceptTerms.builder() + .acceptTermsIds(acceptTerms) + .build(); + + Long findId = 1L; + + when(applicationRepository.findById(findId)).thenReturn( + Optional.ofNullable(Application.builder().build()) + ); + + when(termsRepository.findAll(Sort.by(Sort.Direction.ASC, "termsId"))) + .thenReturn(Arrays.asList(entityA, entityB)); + + assertThatThrownBy(() -> applicationService.acceptTerms(findId, request)) + .isInstanceOf(BaseException.class); + } } \ No newline at end of file