-
Notifications
You must be signed in to change notification settings - Fork 0
[feat] 투표 생성 api 개발 #46
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 all commits
499787a
55c2160
43fada5
f384961
3d2f151
02a72a1
d53fdb4
6084ef8
d3aa352
48716f2
1f807d6
06d2200
6705b6d
1a01f13
69ff3ff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,28 @@ | ||
| package konkuk.thip.room.adapter.out.persistence; | ||
|
|
||
| import konkuk.thip.common.exception.EntityNotFoundException; | ||
| import konkuk.thip.room.adapter.out.jpa.RoomJpaEntity; | ||
| import konkuk.thip.room.adapter.out.mapper.RoomMapper; | ||
| import konkuk.thip.room.application.port.out.RoomCommandPort; | ||
| import konkuk.thip.room.domain.Room; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Repository; | ||
|
|
||
| import static konkuk.thip.common.exception.code.ErrorCode.ROOM_NOT_FOUND; | ||
|
|
||
| @Repository | ||
| @RequiredArgsConstructor | ||
| public class RoomCommandPersistenceAdapter implements RoomCommandPort { | ||
|
|
||
| private final RoomJpaRepository jpaRepository; | ||
| private final RoomMapper userMapper; | ||
| private final RoomJpaRepository roomJpaRepository; | ||
| private final RoomMapper roomMapper; | ||
|
|
||
| @Override | ||
| public Room findById(Long id) { | ||
| RoomJpaEntity roomJpaEntity = roomJpaRepository.findById(id).orElseThrow( | ||
| () -> new EntityNotFoundException(ROOM_NOT_FOUND) | ||
| ); | ||
|
|
||
| return roomMapper.toDomainEntity(roomJpaEntity); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,8 @@ | ||
| package konkuk.thip.room.application.port.out; | ||
|
|
||
| import konkuk.thip.room.domain.Room; | ||
|
|
||
| public interface RoomCommandPort { | ||
|
|
||
| Room findById(Long id); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| package konkuk.thip.user.adapter.in.web; | ||
|
|
||
| import jakarta.servlet.http.HttpServletResponse; | ||
| import jakarta.validation.Valid; | ||
| import konkuk.thip.common.dto.BaseResponse; | ||
| import konkuk.thip.common.security.annotation.Oauth2Id; | ||
| import konkuk.thip.common.security.util.JwtUtil; | ||
|
|
@@ -11,7 +12,6 @@ | |
| import konkuk.thip.user.application.port.in.UserSignupUseCase; | ||
| import konkuk.thip.user.application.port.in.UserVerifyNicknameUseCase; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.validation.annotation.Validated; | ||
| import org.springframework.web.bind.annotation.PostMapping; | ||
| import org.springframework.web.bind.annotation.RequestBody; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
|
|
@@ -28,7 +28,7 @@ public class UserCommandController { | |
| private final JwtUtil jwtUtil; | ||
|
|
||
| @PostMapping("/users/signup") | ||
| public BaseResponse<UserSignupResponse> signup(@Validated @RequestBody UserSignupRequest request, | ||
| public BaseResponse<UserSignupResponse> signup(@Valid @RequestBody UserSignupRequest request, | ||
|
Contributor
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. 👍🏻 👍🏻
Member
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. 붐붐따리붐붐따 붐업 |
||
| @Oauth2Id String oauth2Id, | ||
| HttpServletResponse response) { | ||
| Long userId = userSignupUseCase.signup(request.toCommand(oauth2Id)); | ||
|
|
@@ -38,7 +38,7 @@ public BaseResponse<UserSignupResponse> signup(@Validated @RequestBody UserSignu | |
| } | ||
|
|
||
| @PostMapping("/users/nickname") | ||
| public BaseResponse<UserVerifyNicknameResponse> verifyNickname(@Validated @RequestBody UserVerifyNicknameRequest request) { | ||
| public BaseResponse<UserVerifyNicknameResponse> verifyNickname(@Valid @RequestBody UserVerifyNicknameRequest request) { | ||
| return BaseResponse.ok(UserVerifyNicknameResponse.of( | ||
| userVerifyNicknameUseCase.isNicknameUnique(request.nickname())) | ||
| ); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,31 @@ | ||
| package konkuk.thip.vote.adapter.in.web; | ||
|
|
||
| import jakarta.validation.Valid; | ||
| import konkuk.thip.common.dto.BaseResponse; | ||
| import konkuk.thip.common.security.annotation.UserId; | ||
| import konkuk.thip.vote.adapter.in.web.request.VoteCreateRequest; | ||
| import konkuk.thip.vote.adapter.in.web.response.VoteCreateResponse; | ||
| import konkuk.thip.vote.application.port.in.VoteCreateUseCase; | ||
| import lombok.RequiredArgsConstructor; | ||
| 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.RestController; | ||
|
|
||
| @RestController | ||
| @RequiredArgsConstructor | ||
| public class VoteCommandController { | ||
|
|
||
| private final VoteCreateUseCase voteCreateUseCase; | ||
|
|
||
| @PostMapping("/rooms/{roomId}/vote") | ||
| public BaseResponse<VoteCreateResponse> createVote( | ||
| @UserId Long userId, | ||
| @PathVariable Long roomId, | ||
| @Valid @RequestBody VoteCreateRequest request) { | ||
|
|
||
| return BaseResponse.ok(VoteCreateResponse.of( | ||
| voteCreateUseCase.createVote(request.toCommand(userId, roomId)) | ||
| )); | ||
| } | ||
| } |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| package konkuk.thip.vote.adapter.in.web.request; | ||
|
|
||
| import jakarta.validation.Valid; | ||
| import jakarta.validation.constraints.NotBlank; | ||
| import jakarta.validation.constraints.NotNull; | ||
| import jakarta.validation.constraints.Size; | ||
| import konkuk.thip.vote.application.port.in.dto.VoteCreateCommand; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public record VoteCreateRequest( | ||
| @NotNull(message = "page는 필수입니다.") | ||
| Integer page, | ||
|
|
||
| @NotNull(message = "isOverview(= 총평 여부)는 필수입니다.") | ||
| Boolean isOverview, | ||
|
|
||
| @NotBlank(message = "투표 내용은 필수입니다.") | ||
| @Size(max = 20, message = "투표 내용은 최대 20자 입니다.") | ||
| String content, | ||
|
|
||
| @NotNull(message = "투표 항목은 필수입니다.") | ||
| @Size(min = 1, max = 5, message = "투표 항목은 1개 이상, 최대 5개까지입니다.") | ||
|
Contributor
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. 현재 "투표 항목은 1개 이상, 최대 5개까지이다"라는 요구사항은 도메인 규칙에 해당하는 것 같긴한데, 어떻게 생각하시나요..? 변동될 수 있는 규칙이라 도메인에서 검증하는 것이 더 유지보수하는게 좋을 것 같긴한데 따지고보면 다른 파라미터들도 다 비슷한 거 같아서.. 애매하네요 😅
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. 저도 현준님이 리뷰 남겨주신 것처럼 "투표 항목은 1개 이상, 5개 이하만 가능하다" 라는 요구사항을 투표 생성 api 구현시에 지켜야 하는 도메인 규칙이라고 생각합니다!
Contributor
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. 넵 확인했습니다! 뭐 투표를 생성하는 로직이 다른 api에 사용하는 것은 아니니 상관없을 것 같네요! 만약 추후에 투표를 생성하는 로직이 이 api가 아닌 다른 api에서도 필요해진다면 중복 코드를 방지하기 위해 도메인 계층으로 옮기는 것도 좋을 것 같아요~
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. 넵 동의합니다! |
||
| @Valid | ||
| List<VoteItemCreateRequest> voteItemList | ||
| ) { | ||
| public record VoteItemCreateRequest( | ||
| @NotBlank(message = "투표 항목 이름은 필수입니다.") | ||
| @Size(max = 20, message = "투표 항목 이름은 최대 20자입니다.") | ||
| String itemName | ||
| ) {} | ||
|
|
||
| public VoteCreateCommand toCommand(Long userId, Long roomId) { | ||
| List<VoteCreateCommand.VoteItemCreateCommand> mappedItems = voteItemList.stream() | ||
| .map(voteItem -> new VoteCreateCommand.VoteItemCreateCommand(voteItem.itemName)) | ||
| .toList(); | ||
|
|
||
| return new VoteCreateCommand( | ||
| userId, | ||
| roomId, | ||
| page, | ||
| isOverview, | ||
| content, | ||
| mappedItems | ||
| ); | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package konkuk.thip.vote.adapter.in.web.response; | ||
|
|
||
| public record VoteCreateResponse( | ||
| Long voteId | ||
| ) { | ||
| public static VoteCreateResponse of(Long voteId) { | ||
| return new VoteCreateResponse(voteId); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,62 @@ | ||
| package konkuk.thip.vote.adapter.out.persistence; | ||
|
|
||
| import konkuk.thip.common.exception.EntityNotFoundException; | ||
| import konkuk.thip.room.adapter.out.jpa.RoomJpaEntity; | ||
| import konkuk.thip.room.adapter.out.persistence.RoomJpaRepository; | ||
| import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; | ||
| import konkuk.thip.user.adapter.out.persistence.UserJpaRepository; | ||
| import konkuk.thip.vote.adapter.out.jpa.VoteItemJpaEntity; | ||
| import konkuk.thip.vote.adapter.out.jpa.VoteJpaEntity; | ||
| import konkuk.thip.vote.adapter.out.mapper.VoteItemMapper; | ||
| import konkuk.thip.vote.adapter.out.mapper.VoteMapper; | ||
| import konkuk.thip.vote.application.port.out.VoteCommandPort; | ||
| import konkuk.thip.vote.domain.Vote; | ||
| import konkuk.thip.vote.domain.VoteItem; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Repository; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import static konkuk.thip.common.exception.code.ErrorCode.*; | ||
|
|
||
| @Repository | ||
| @RequiredArgsConstructor | ||
| public class VoteCommandPersistenceAdapter implements VoteCommandPort { | ||
|
|
||
| private final VoteJpaRepository voteJpaRepository; | ||
| private final VoteItemJpaRepository voteItemJpaRepository; | ||
| private final UserJpaRepository userJpaRepository; | ||
| private final RoomJpaRepository roomJpaRepository; | ||
|
|
||
| private final VoteMapper voteMapper; | ||
| private final VoteItemMapper voteItemMapper; | ||
|
|
||
| @Override | ||
| public Long saveVote(Vote vote) { | ||
| UserJpaEntity userJpaEntity = userJpaRepository.findById(vote.getCreatorId()).orElseThrow( | ||
| () -> new EntityNotFoundException(USER_NOT_FOUND) | ||
| ); | ||
|
|
||
| RoomJpaEntity roomJpaEntity = roomJpaRepository.findById(vote.getRoomId()).orElseThrow( | ||
| () -> new EntityNotFoundException(ROOM_NOT_FOUND) | ||
| ); | ||
|
|
||
| return voteJpaRepository.save(voteMapper.toJpaEntity(vote, userJpaEntity, roomJpaEntity)).getPostId(); | ||
| } | ||
|
|
||
| @Override | ||
| public void saveAllVoteItems(List<VoteItem> voteItems) { | ||
| if (voteItems.isEmpty()) return; | ||
|
|
||
| Long voteId = voteItems.get(0).getVoteId(); | ||
| VoteJpaEntity voteJpaEntity = voteJpaRepository.findById(voteId).orElseThrow( | ||
| () -> new EntityNotFoundException(VOTE_NOT_FOUND) | ||
| ); | ||
|
|
||
| List<VoteItemJpaEntity> voteItemJpaEntities = voteItems.stream() | ||
| .map(voteItem -> voteItemMapper.toJpaEntity(voteItem, voteJpaEntity)) | ||
| .toList(); | ||
|
|
||
| voteItemJpaRepository.saveAll(voteItemJpaEntities); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package konkuk.thip.vote.adapter.out.persistence; | ||
|
|
||
| import konkuk.thip.vote.adapter.out.jpa.VoteItemJpaEntity; | ||
| import org.springframework.data.jpa.repository.JpaRepository; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public interface VoteItemJpaRepository extends JpaRepository<VoteItemJpaEntity, Long> { | ||
|
|
||
| List<VoteItemJpaEntity> findAllByVoteJpaEntity_PostId(Long voteId); | ||
| } |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package konkuk.thip.vote.application.port.in; | ||
|
|
||
| import konkuk.thip.vote.application.port.in.dto.VoteCreateCommand; | ||
|
|
||
| public interface VoteCreateUseCase { | ||
|
|
||
| Long createVote(VoteCreateCommand command); | ||
| } |
This file was deleted.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,19 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package konkuk.thip.vote.application.port.in.dto; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.List; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public record VoteCreateCommand( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Long userId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Long roomId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int page, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| boolean isOverview, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String content, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List<VoteItemCreateCommand> voteItemCreateCommands | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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. 💡 Verification agent 🧩 Analysis chainnull safety 고려 필요
🏁 Script executed: #!/bin/bash
# voteItemCreateCommands null 체크가 서비스 레이어에서 이루어지는지 확인
ast-grep --pattern 'command.voteItemCreateCommands()' -A 5Length of output: 741 #!/bin/bash
# VoteCreateCommand 클래스 내용 확인
cat src/main/java/konkuk/thip/vote/application/port/in/dto/VoteCreateCommand.java다음으로 🏁 Script executed: #!/bin/bash
# VoteCreateCommand 클래스 내용 확인
cat src/main/java/konkuk/thip/vote/application/port/in/dto/VoteCreateCommand.javaLength of output: 433 null 안전성 강화 필요
위 두 군데에 null 체크를 추가해 NPE를 방지하세요. 📝 Committable suggestion
Suggested change
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public record VoteItemCreateCommand(String itemName) {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,13 @@ | ||
| package konkuk.thip.vote.application.port.out; | ||
|
|
||
| import konkuk.thip.vote.domain.Vote; | ||
| import konkuk.thip.vote.domain.VoteItem; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public interface VoteCommandPort { | ||
|
|
||
| Long saveVote(Vote vote); | ||
|
|
||
| void saveAllVoteItems(List<VoteItem> voteItems); | ||
| } |
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.
🤩