diff --git a/src/main/java/umc/th/juinjang/api/termsAgreement/controller/TermsAgreementController.java b/src/main/java/umc/th/juinjang/api/termsAgreement/controller/TermsAgreementController.java new file mode 100644 index 00000000..abefc5d1 --- /dev/null +++ b/src/main/java/umc/th/juinjang/api/termsAgreement/controller/TermsAgreementController.java @@ -0,0 +1,51 @@ +package umc.th.juinjang.api.termsAgreement.controller; + +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import lombok.RequiredArgsConstructor; +import umc.th.juinjang.api.dto.ApiResponse; +import umc.th.juinjang.api.termsAgreement.controller.request.AgreeRequest; +import umc.th.juinjang.api.termsAgreement.controller.response.AgreeResponse; +import umc.th.juinjang.api.termsAgreement.controller.response.StatusResponse; +import umc.th.juinjang.api.termsAgreement.service.TermsAgreementService; +import umc.th.juinjang.domain.member.model.Member; +import umc.th.juinjang.domain.termsAgreement.repository.TermsType; + +@RestController +@RequestMapping("/api/v2/terms-agreement") +@RequiredArgsConstructor +public class TermsAgreementController { + + private final TermsAgreementService termsAgreementService; + + @Operation(summary = "특정 약관 동의 여부 확인", + description = "로그인한 멤버가 특정 약관에 동의했는지 확인합니다.") + @GetMapping("/{termsType}") + public ApiResponse checkStatus( + @AuthenticationPrincipal Member member, + @Parameter(description = "확인할 약관 타입") + @PathVariable TermsType termsType) { + boolean isAgreed = termsAgreementService.checkStatus(member.getMemberId(), termsType); + return ApiResponse.onSuccess(StatusResponse.of(isAgreed)); + } + + @Operation(summary = "특정 약관에 동의 하기", + description = "해당 멤버가 특정 약관에 동의합니다.") + @PostMapping + public ApiResponse agreeToSpecificTerms( + @AuthenticationPrincipal Member member, + @RequestBody AgreeRequest request + ) { + termsAgreementService.agreeToSpecificTerms(member.getMemberId(), request.termsType()); + return ApiResponse.onSuccess(AgreeResponse.from(request.termsType(), true)); + } + +} diff --git a/src/main/java/umc/th/juinjang/api/termsAgreement/controller/request/AgreeRequest.java b/src/main/java/umc/th/juinjang/api/termsAgreement/controller/request/AgreeRequest.java new file mode 100644 index 00000000..07401c80 --- /dev/null +++ b/src/main/java/umc/th/juinjang/api/termsAgreement/controller/request/AgreeRequest.java @@ -0,0 +1,8 @@ +package umc.th.juinjang.api.termsAgreement.controller.request; + +import umc.th.juinjang.domain.termsAgreement.repository.TermsType; + +public record AgreeRequest( + TermsType termsType +) { +} diff --git a/src/main/java/umc/th/juinjang/api/termsAgreement/controller/response/AgreeResponse.java b/src/main/java/umc/th/juinjang/api/termsAgreement/controller/response/AgreeResponse.java new file mode 100644 index 00000000..bf1bcd66 --- /dev/null +++ b/src/main/java/umc/th/juinjang/api/termsAgreement/controller/response/AgreeResponse.java @@ -0,0 +1,15 @@ +package umc.th.juinjang.api.termsAgreement.controller.response; + +import umc.th.juinjang.domain.termsAgreement.repository.TermsType; + +public record AgreeResponse( + TermsType termsType, + boolean isAgreed +) { + public static AgreeResponse from(TermsType termsType, boolean isAgreed) { + return new AgreeResponse( + termsType, + isAgreed + ); + } +} diff --git a/src/main/java/umc/th/juinjang/api/termsAgreement/controller/response/StatusResponse.java b/src/main/java/umc/th/juinjang/api/termsAgreement/controller/response/StatusResponse.java new file mode 100644 index 00000000..b259a486 --- /dev/null +++ b/src/main/java/umc/th/juinjang/api/termsAgreement/controller/response/StatusResponse.java @@ -0,0 +1,9 @@ +package umc.th.juinjang.api.termsAgreement.controller.response; + +public record StatusResponse( + boolean status +) { + public static StatusResponse of(boolean isAgreed) { + return new StatusResponse(isAgreed); + } +} diff --git a/src/main/java/umc/th/juinjang/api/termsAgreement/service/TermsAgreementService.java b/src/main/java/umc/th/juinjang/api/termsAgreement/service/TermsAgreementService.java new file mode 100644 index 00000000..642b40e9 --- /dev/null +++ b/src/main/java/umc/th/juinjang/api/termsAgreement/service/TermsAgreementService.java @@ -0,0 +1,37 @@ +package umc.th.juinjang.api.termsAgreement.service; + +import static umc.th.juinjang.common.code.status.ErrorStatus.*; + +import java.util.Optional; + +import org.springframework.stereotype.Service; + +import lombok.RequiredArgsConstructor; +import umc.th.juinjang.common.exception.handler.MemberHandler; +import umc.th.juinjang.domain.termsAgreement.repository.TermsAgreement; +import umc.th.juinjang.domain.termsAgreement.repository.TermsAgreementRepository; +import umc.th.juinjang.domain.termsAgreement.repository.TermsType; + +@Service +@RequiredArgsConstructor +public class TermsAgreementService { + + private final TermsAgreementRepository termsAgreementRepository; + + public boolean checkStatus(Long memberId, TermsType termsType) { + return termsAgreementRepository.findByMemberIdAndTermsType(memberId, termsType) + .isPresent(); + } + + public void agreeToSpecificTerms(Long memberId, TermsType termsType) { + Optional existingAgreement = + termsAgreementRepository.findByMemberIdAndTermsType(memberId, termsType); + + if (existingAgreement.isPresent()) { + throw new MemberHandler(TERMS_AGREEMENT_DUPLICATED); + } + + TermsAgreement agreement = TermsAgreement.create(memberId, termsType); + termsAgreementRepository.save(agreement); + } +} diff --git a/src/main/java/umc/th/juinjang/common/code/status/ErrorStatus.java b/src/main/java/umc/th/juinjang/common/code/status/ErrorStatus.java index 1f105800..bccdc6fa 100644 --- a/src/main/java/umc/th/juinjang/common/code/status/ErrorStatus.java +++ b/src/main/java/umc/th/juinjang/common/code/status/ErrorStatus.java @@ -119,8 +119,11 @@ public enum ErrorStatus implements BaseErrorCode { // LikedNote LIKEDNOTE_CONFLICT(HttpStatus.CONFLICT, "LIKEDNOTE4000", "이미 좋아요한 노트입니다"), - LIKEDNOTE_NOT_FOUND(HttpStatus.NOT_FOUND, "LIKEDNOTE4001", "이미 취소했거나 좋아요한 적이 없습니다."); + LIKEDNOTE_NOT_FOUND(HttpStatus.NOT_FOUND, "LIKEDNOTE4001", "이미 취소했거나 좋아요한 적이 없습니다."), + // Terms_Agreement + TERMS_AGREEMENT_DUPLICATED(HttpStatus.BAD_REQUEST, "TERMS_AGREEMENT_4000", "이미 약관에 동의하였습니다."); + private final HttpStatus httpStatus; private final String code; private final String message; diff --git a/src/main/java/umc/th/juinjang/domain/termsAgreement/repository/TermsAgreement.java b/src/main/java/umc/th/juinjang/domain/termsAgreement/repository/TermsAgreement.java new file mode 100644 index 00000000..283440a5 --- /dev/null +++ b/src/main/java/umc/th/juinjang/domain/termsAgreement/repository/TermsAgreement.java @@ -0,0 +1,44 @@ +package umc.th.juinjang.domain.termsAgreement.repository; + +import java.time.LocalDateTime; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class TermsAgreement { + + @Id + @Column(name = "terms_agreement_id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private Long memberId; + + @Enumerated(EnumType.STRING) + private TermsType termsType; + + private LocalDateTime agreedAt; + + public static TermsAgreement create(Long memberId, TermsType termsType) { + return TermsAgreement.builder() + .memberId(memberId) + .termsType(termsType) + .agreedAt(LocalDateTime.now()) + .build(); + } +} diff --git a/src/main/java/umc/th/juinjang/domain/termsAgreement/repository/TermsAgreementRepository.java b/src/main/java/umc/th/juinjang/domain/termsAgreement/repository/TermsAgreementRepository.java new file mode 100644 index 00000000..9367564c --- /dev/null +++ b/src/main/java/umc/th/juinjang/domain/termsAgreement/repository/TermsAgreementRepository.java @@ -0,0 +1,12 @@ +package umc.th.juinjang.domain.termsAgreement.repository; + +import java.util.Optional; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface TermsAgreementRepository extends JpaRepository { + // 멤버가 특정 약관에 동의 했는 여부를 체크하는 메서드 + Optional findByMemberIdAndTermsType(Long memberId, TermsType termsType); +} diff --git a/src/main/java/umc/th/juinjang/domain/termsAgreement/repository/TermsType.java b/src/main/java/umc/th/juinjang/domain/termsAgreement/repository/TermsType.java new file mode 100644 index 00000000..0fc768be --- /dev/null +++ b/src/main/java/umc/th/juinjang/domain/termsAgreement/repository/TermsType.java @@ -0,0 +1,10 @@ +package umc.th.juinjang.domain.termsAgreement.repository; + +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public enum TermsType { + PENCIL_SHOP_SERVICE("연필상점 이용약관"); + + private final String text; +}