-
Notifications
You must be signed in to change notification settings - Fork 1
Feature/75 관심기관 등록, 취소 기능 구현 #85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
6a8e7ae
ecbaaf5
82bf3f2
4bef513
3b13887
2a6358f
8b0ff08
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package com.somemore.global.exception; | ||
|
|
||
| public class DuplicateException extends RuntimeException{ | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BadReq로 모든 것을 처리하는 게 아니였나요...?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 중복 검사가 계속 생길거 같은데 전부 BadReq로 하면 예외가 명확하지 못한 느낌이 들어서 추가해주었습니다
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BadReq에서 구분된다면, Exception 메시지들도 따로 관리하는 것이 명확하다고 생각했고 그 반대도 같은 의미로 받아들이고 있었습니다. 저는 오히려 서진님의 구분된 Exception이 더 명확하다고 생각합니다.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exception 메시지들도 따로 관리하는 것이 혹시 도메인 별로 메시지들을 관리하는게 맞나요?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
이거 같은데 우선 진행해도 무방하다고 생각합니다. |
||
|
|
||
| public DuplicateException(final String message) { | ||
| super(message); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| package com.somemore.interestcenter.controller; | ||
|
|
||
| import com.somemore.global.common.response.ApiResponse; | ||
| import com.somemore.interestcenter.dto.request.RegisterInterestCenterRequestDto; | ||
| import com.somemore.interestcenter.dto.response.RegisterInterestCenterResponseDto; | ||
| import com.somemore.interestcenter.usecase.CancelInterestCenterUseCase; | ||
| import com.somemore.interestcenter.usecase.RegisterInterestCenterUseCase; | ||
| import io.swagger.v3.oas.annotations.Operation; | ||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||
| import jakarta.validation.Valid; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.web.bind.annotation.*; | ||
|
|
||
| @RequiredArgsConstructor | ||
| @RestController | ||
| @RequestMapping("/api/interest-center") | ||
|
||
| @Tag(name = "Interest Center Command API", description = "관심 기관의 등록과 취소 API를 제공합니다") | ||
| public class InterestCenterCommandApiController { | ||
|
|
||
| private final RegisterInterestCenterUseCase registerInterestCenterUseCase; | ||
| private final CancelInterestCenterUseCase cancelInterestCenterUseCase; | ||
|
|
||
| @Operation(summary = "관심기관 등록 API") | ||
| @PostMapping() | ||
| public ApiResponse<RegisterInterestCenterResponseDto> registerInterestCenter(@Valid @RequestBody RegisterInterestCenterRequestDto requestDto) { | ||
|
|
||
| RegisterInterestCenterResponseDto responseDto = registerInterestCenterUseCase.registerInterestCenter(requestDto); | ||
|
|
||
| return ApiResponse.ok(200, responseDto, "관심 기관 등록 성공"); | ||
| } | ||
|
|
||
| @Operation(summary = "관심기관 취소 API") | ||
| @DeleteMapping("/{interest-center-id}") | ||
| public ApiResponse<String> deleteInterestCenter(@PathVariable("interest-center-id") Long interestCenterId) { | ||
|
|
||
| cancelInterestCenterUseCase.cancelInterestCenter(interestCenterId); | ||
|
|
||
| return ApiResponse.okWithoutData("관심 기관 취소 성공"); | ||
| } | ||
|
|
||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package com.somemore.interestcenter.domain; | ||
|
|
||
| import com.somemore.global.common.BaseEntity; | ||
| import jakarta.persistence.*; | ||
| import lombok.*; | ||
|
|
||
| import java.util.UUID; | ||
|
|
||
| import static jakarta.persistence.GenerationType.IDENTITY; | ||
|
|
||
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
| @Getter | ||
| @Entity | ||
| @Table(name = "interest_center") | ||
| public class InterestCenter extends BaseEntity { | ||
|
|
||
| @Id | ||
| @GeneratedValue(strategy = IDENTITY) | ||
| private Long id; | ||
|
|
||
| @Column(name = "volunteer_id", nullable = false) | ||
| private UUID volunteerId; | ||
|
|
||
| @Column(name = "center_id", nullable = false) | ||
| private UUID centerId; | ||
|
|
||
| @Builder | ||
| private InterestCenter(UUID volunteerId, UUID centerId) { | ||
| this.volunteerId = volunteerId; | ||
| this.centerId = centerId; | ||
| } | ||
|
|
||
| public static InterestCenter create(UUID volunteerId, UUID centerId) { | ||
| return InterestCenter.builder() | ||
| .volunteerId(volunteerId) | ||
| .centerId(centerId) | ||
| .build(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package com.somemore.interestcenter.dto.request; | ||
|
|
||
| import com.fasterxml.jackson.databind.PropertyNamingStrategies; | ||
| import com.fasterxml.jackson.databind.annotation.JsonNaming; | ||
| import com.somemore.interestcenter.domain.InterestCenter; | ||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
| import jakarta.validation.constraints.NotNull; | ||
|
|
||
| import java.util.UUID; | ||
|
|
||
| @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) | ||
| public record RegisterInterestCenterRequestDto( | ||
|
|
||
| @Schema(description = "봉사자 ID", example = "123e4567-e89b-12d3-a456-426614174000") | ||
| @NotNull(message = "봉사자 ID는 필수값입니다.") | ||
| UUID volunteerId, | ||
|
|
||
| @Schema(description = "봉사자 ID", example = "123e4567-e89b-12d3-a456-426614174000") | ||
| @NotNull(message = "기관 ID는 필수값입니다.") | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 봉사자 ID -> 기관 ID 오타인것 같아요. 그리고 UUID volunteerId은 로그인시 시큐리티에서 컨트롤러로 가져오는 값이지 않나요??
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이런 실수를... 자세히 봐주셔서 감사합니다 |
||
| UUID centerId | ||
| ) { | ||
| public InterestCenter toEntity(){ | ||
| return InterestCenter.create(volunteerId, centerId); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| package com.somemore.interestcenter.dto.response; | ||
|
|
||
| import com.fasterxml.jackson.databind.PropertyNamingStrategies; | ||
| import com.fasterxml.jackson.databind.annotation.JsonNaming; | ||
| import com.somemore.interestcenter.domain.InterestCenter; | ||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
| import lombok.Builder; | ||
|
|
||
| import java.util.UUID; | ||
|
|
||
| @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) | ||
| @Builder | ||
| public record RegisterInterestCenterResponseDto( | ||
| @Schema(description = "관심 ID", example = "1111") | ||
| Long id, | ||
|
|
||
| @Schema(description = "봉사자 ID", example = "123e4567-e89b-12d3-a456-426614174000") | ||
| UUID volunteerId, | ||
|
|
||
| @Schema(description = "센터 ID", example = "123e4567-e89b-12d3-a456-426614174000") | ||
| UUID centerId | ||
| ) { | ||
| public static RegisterInterestCenterResponseDto from(InterestCenter interestCenter) { | ||
| return RegisterInterestCenterResponseDto.builder() | ||
| .id(interestCenter.getId()) | ||
| .volunteerId(interestCenter.getVolunteerId()) | ||
| .centerId(interestCenter.getCenterId()) | ||
| .build(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.somemore.interestcenter.repository; | ||
|
|
||
| import com.somemore.interestcenter.domain.InterestCenter; | ||
| import org.springframework.data.jpa.repository.JpaRepository; | ||
|
|
||
| public interface InterestCenterJpaRepository extends JpaRepository<InterestCenter, Long> { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package com.somemore.interestcenter.repository; | ||
|
|
||
| import com.somemore.interestcenter.domain.InterestCenter; | ||
| import com.somemore.interestcenter.dto.response.RegisterInterestCenterResponseDto; | ||
|
|
||
| import java.util.Optional; | ||
| import java.util.UUID; | ||
|
|
||
| public interface InterestCenterRepository { | ||
| InterestCenter save(InterestCenter interestCenter); | ||
| Optional<InterestCenter> findById(Long id); | ||
| Optional<RegisterInterestCenterResponseDto> findInterestCenterResponseById(Long id); | ||
| boolean existsByVolunteerIdAndCenterId(UUID volunteerId, UUID centerId); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| package com.somemore.interestcenter.repository; | ||
|
|
||
| import com.querydsl.core.types.Projections; | ||
| import com.querydsl.jpa.impl.JPAQueryFactory; | ||
| import com.somemore.interestcenter.domain.InterestCenter; | ||
| import com.somemore.interestcenter.domain.QInterestCenter; | ||
| import com.somemore.interestcenter.dto.response.RegisterInterestCenterResponseDto; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Repository; | ||
|
|
||
| import java.util.Optional; | ||
| import java.util.UUID; | ||
|
|
||
| @RequiredArgsConstructor | ||
| @Repository | ||
| public class InterestCenterRepositoryImpl implements InterestCenterRepository { | ||
|
|
||
| private final JPAQueryFactory queryFactory; | ||
| private final InterestCenterJpaRepository interestCenterJpaRepository; | ||
|
|
||
| @Override | ||
| public InterestCenter save(InterestCenter interestCenter) { | ||
| return interestCenterJpaRepository.save(interestCenter); | ||
| } | ||
|
|
||
| @Override | ||
| public Optional<InterestCenter> findById(Long id) { | ||
| QInterestCenter interestCenter = QInterestCenter.interestCenter; | ||
|
|
||
| InterestCenter result = queryFactory | ||
| .selectFrom(interestCenter) | ||
| .where( | ||
| interestCenter.id.eq(id) | ||
| .and(interestCenter.deleted.eq(false)) | ||
| ) | ||
| .fetchOne(); | ||
|
|
||
| return Optional.ofNullable(result); | ||
| } | ||
|
|
||
| @Override | ||
| public Optional<RegisterInterestCenterResponseDto> findInterestCenterResponseById(Long id) { | ||
| QInterestCenter interestCenter = QInterestCenter.interestCenter; | ||
|
|
||
| RegisterInterestCenterResponseDto result = queryFactory | ||
| .select( | ||
| Projections.constructor( | ||
| RegisterInterestCenterResponseDto.class, | ||
| interestCenter.id, | ||
| interestCenter.volunteerId, | ||
| interestCenter.centerId | ||
| ) | ||
| ) | ||
| .from(interestCenter) | ||
| .where( | ||
| interestCenter.id.eq(id) | ||
| .and(interestCenter.deleted.eq(false)) | ||
| ) | ||
| .fetchOne(); | ||
|
|
||
| return Optional.ofNullable(result); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean existsByVolunteerIdAndCenterId(UUID volunteerId, UUID centerId) { | ||
| QInterestCenter interestCenter = QInterestCenter.interestCenter; | ||
|
|
||
| Integer result = queryFactory | ||
| .selectOne() | ||
| .from(interestCenter) | ||
| .where( | ||
| interestCenter.volunteerId.eq(volunteerId) | ||
| .and(interestCenter.centerId.eq(centerId)) | ||
| .and(interestCenter.deleted.eq(false)) | ||
| ) | ||
| .fetchFirst(); | ||
|
|
||
| return result != null; | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package com.somemore.interestcenter.service; | ||
|
|
||
| import com.somemore.global.exception.BadRequestException; | ||
| import com.somemore.interestcenter.domain.InterestCenter; | ||
| import com.somemore.interestcenter.repository.InterestCenterRepository; | ||
| import com.somemore.interestcenter.usecase.CancelInterestCenterUseCase; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Service; | ||
|
|
||
| import static com.somemore.global.exception.ExceptionMessage.CANNOT_CANCEL_DELETED_INTEREST_CENTER; | ||
|
|
||
| @RequiredArgsConstructor | ||
| @Service | ||
| public class CancelInterestCenterService implements CancelInterestCenterUseCase { | ||
|
|
||
| private final InterestCenterRepository interestCenterRepository; | ||
|
|
||
| @Override | ||
| public void cancelInterestCenter(Long interestCenterId) { | ||
| InterestCenter interestCenter = interestCenterRepository.findById(interestCenterId) | ||
| .orElseThrow(() -> new BadRequestException(CANNOT_CANCEL_DELETED_INTEREST_CENTER.getMessage())); | ||
|
|
||
| interestCenter.markAsDeleted(); | ||
|
|
||
| interestCenterRepository.save(interestCenter); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package com.somemore.interestcenter.service; | ||
|
|
||
| import com.somemore.center.usecase.query.CenterQueryUseCase; | ||
| import com.somemore.global.exception.DuplicateException; | ||
| import com.somemore.interestcenter.domain.InterestCenter; | ||
| import com.somemore.interestcenter.dto.request.RegisterInterestCenterRequestDto; | ||
| import com.somemore.interestcenter.dto.response.RegisterInterestCenterResponseDto; | ||
| import com.somemore.interestcenter.repository.InterestCenterRepository; | ||
| import com.somemore.interestcenter.usecase.RegisterInterestCenterUseCase; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Service; | ||
|
|
||
| import static com.somemore.global.exception.ExceptionMessage.DUPLICATE_INTEREST_CENTER; | ||
|
|
||
| @RequiredArgsConstructor | ||
| @Service | ||
| public class RegisterInterestCenterService implements RegisterInterestCenterUseCase { | ||
|
|
||
| private final InterestCenterRepository repository; | ||
| private final CenterQueryUseCase centerQueryUseCase; | ||
|
|
||
| @Override | ||
| public RegisterInterestCenterResponseDto registerInterestCenter(RegisterInterestCenterRequestDto requestDto) { | ||
|
|
||
| centerQueryUseCase.validateCenterExists(requestDto.centerId()); | ||
|
|
||
| boolean isDuplicate = repository.existsByVolunteerIdAndCenterId(requestDto.volunteerId(), requestDto.centerId()); | ||
| if(isDuplicate){ | ||
| throw new DuplicateException(DUPLICATE_INTEREST_CENTER.getMessage()); | ||
| } | ||
|
|
||
| InterestCenter interestCenter = repository.save(requestDto.toEntity()); | ||
|
|
||
| return RegisterInterestCenterResponseDto.from(interestCenter); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| package com.somemore.interestcenter.usecase; | ||
|
|
||
| public interface CancelInterestCenterUseCase { | ||
| void cancelInterestCenter(Long interestCenterId); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저도 이거 만들려고 했는데 ㅎㅎ. 감사히 사용하겠습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
최신 커밋에서는 이름이 다시 ok로 변경되었습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
더 좋습니다.