-
Notifications
You must be signed in to change notification settings - Fork 1
[FEAT] 종소리 관련 API V2 제작 #206
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
cb8b655
275b54a
530b26b
b3ad675
6b27490
adcfaf5
7a8c985
c6bcebc
644987d
5d46dab
37508a5
14040c2
e96fecf
582aa5b
79759e2
981cd09
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 |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| package com.debatetimer.config; | ||
|
|
||
| import com.debatetimer.exception.decoder.H2ErrorDecoder; | ||
| import com.debatetimer.exception.decoder.MySqlErrorDecoder; | ||
| import com.debatetimer.exception.decoder.RepositoryErrorDecoder; | ||
| import lombok.NoArgsConstructor; | ||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.context.annotation.Configuration; | ||
| import org.springframework.context.annotation.Profile; | ||
|
|
||
| @NoArgsConstructor(access = lombok.AccessLevel.PRIVATE) | ||
| public class ErrorDecoderConfig { | ||
|
|
||
| @Profile({"dev", "prod"}) | ||
| @Configuration | ||
| public static class MySqlErrorDecoderConfig { | ||
|
|
||
| @Bean | ||
| public RepositoryErrorDecoder mySqlErrorDecoder() { | ||
| return new MySqlErrorDecoder(); | ||
| } | ||
| } | ||
|
|
||
| @Profile({"test", "local"}) | ||
| @Configuration | ||
| public static class H2ErrorDecoderConfig { | ||
|
|
||
| @Bean | ||
| public RepositoryErrorDecoder h2ErrorDecoder() { | ||
| return new H2ErrorDecoder(); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package com.debatetimer.controller.poll; | ||
|
|
||
| import com.debatetimer.dto.poll.request.VoteRequest; | ||
| import com.debatetimer.dto.poll.response.VoteCreateResponse; | ||
| import com.debatetimer.dto.poll.response.VoterPollInfoResponse; | ||
| import com.debatetimer.service.poll.VoteService; | ||
| import jakarta.validation.Valid; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.http.HttpStatus; | ||
| 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.ResponseStatus; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
|
|
||
| @RestController | ||
| @RequiredArgsConstructor | ||
| public class VoteController { | ||
|
|
||
| private final VoteService voteService; | ||
|
|
||
| @GetMapping("/api/polls/{pollId}/votes") | ||
| @ResponseStatus(HttpStatus.OK) | ||
| public VoterPollInfoResponse getVotersPollInfo(@PathVariable long pollId) { | ||
| return voteService.getVoterPollInfo(pollId); | ||
| } | ||
|
|
||
| @PostMapping("/api/polls/{pollId}/votes") | ||
| @ResponseStatus(HttpStatus.CREATED) | ||
| public VoteCreateResponse votePoll( | ||
| @PathVariable long pollId, | ||
| @RequestBody @Valid VoteRequest voteRequest | ||
| ) { | ||
| return voteService.vote(pollId, voteRequest); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package com.debatetimer.domain.customize; | ||
|
|
||
| import com.debatetimer.exception.custom.DTClientErrorException; | ||
| import com.debatetimer.exception.errorcode.ClientErrorCode; | ||
| import java.util.function.IntPredicate; | ||
|
|
||
| public enum BellType { | ||
|
|
||
| AFTER_START(time -> time >= 0), | ||
| BEFORE_END(time -> time >= 0), | ||
| AFTER_END(time -> time <= 0), | ||
| ; | ||
|
|
||
| private final IntPredicate timeValidator; | ||
|
|
||
| BellType(IntPredicate timeValidator) { | ||
| this.timeValidator = timeValidator; | ||
| } | ||
|
|
||
| public void validateTime(int time) { | ||
| if (!timeValidator.test(time)) { | ||
| throw new DTClientErrorException(ClientErrorCode.INVALID_BELL_TIME); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,4 +5,8 @@ public enum PollStatus { | |
| PROGRESS, | ||
| DONE, | ||
| ; | ||
|
|
||
| public boolean isProgress() { | ||
| return this == PROGRESS; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,8 @@ | ||
| package com.debatetimer.service.customize; | ||
| package com.debatetimer.domainrepository.customize; | ||
|
|
||
| import com.debatetimer.domain.customize.CustomizeTable; | ||
| import com.debatetimer.domain.customize.CustomizeTimeBox; | ||
| import com.debatetimer.domain.member.Member; | ||
| import com.debatetimer.dto.customize.request.CustomizeTableCreateRequest; | ||
| import com.debatetimer.dto.customize.response.CustomizeTableResponse; | ||
| import com.debatetimer.entity.customize.BellEntity; | ||
| import com.debatetimer.entity.customize.CustomizeTableEntity; | ||
| import com.debatetimer.entity.customize.CustomizeTimeBoxEntities; | ||
|
|
@@ -15,84 +13,77 @@ | |
| import java.util.List; | ||
| import java.util.stream.IntStream; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Service; | ||
| import org.springframework.stereotype.Repository; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
| @Service | ||
| @Repository | ||
| @RequiredArgsConstructor | ||
| public class CustomizeServiceV2 { | ||
| public class CustomizeTableDomainRepository { | ||
|
|
||
| private final CustomizeTableRepository tableRepository; | ||
| private final CustomizeTimeBoxRepository timeBoxRepository; | ||
| private final BellRepository bellRepository; | ||
|
|
||
| @Transactional | ||
| public CustomizeTableResponse save(CustomizeTableCreateRequest tableCreateRequest, Member member) { | ||
| CustomizeTable table = tableCreateRequest.toTable(member); | ||
| List<CustomizeTimeBox> timeBoxes = tableCreateRequest.toTimeBoxList(); | ||
|
|
||
| public CustomizeTable save(CustomizeTable table, List<CustomizeTimeBox> timeBoxes) { | ||
| CustomizeTableEntity savedTableEntity = tableRepository.save(new CustomizeTableEntity(table)); | ||
| saveTimeBoxes(savedTableEntity, timeBoxes); | ||
| return new CustomizeTableResponse(savedTableEntity.toDomain(), timeBoxes); | ||
|
|
||
| return savedTableEntity.toDomain(); | ||
| } | ||
|
|
||
| private void saveTimeBoxes(CustomizeTableEntity tableEntity, List<CustomizeTimeBox> timeBoxes) { | ||
|
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. [질문] 아마 비토가 작성한 코드는 아닌 것 같은데, 이거 sequence mapping을 왜 domain repository에서 해주나요? 도메인이 sequence 자체를 들고 있으면 더 좋을 것 같아요.
Contributor
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. CustomizeTimeBoxes를 고민 안 해봤던 건 아닌데 문제는 그 IntStream을 사용하는 로직을 CustomizeTimeBoxes안에 만드는 방법이 안보였습니다. 그래서 콜리도 이전에 해당 로직을 dto에 뒀던 거로 기억해요.
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.
또한, 상위 계층에서 dto -> domain을 할 때에 순차적으로 매핑하여 보내주어야 함이라는 것이 도메인 레포지토리 안의 private method를 보아야지만 알 수 있는 것이 바람직하지 않다고 생각합니다. 따라서 백엔드-프론트 간의 API 규약에 대해서 매핑하는 것은 dto가 처리하는게 맞지 않는가? 가 제 입장입니다.
Contributor
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.
제 답변의 2번에서 언급했든이 순차대로 입력받는다는 도메인 규칙을 도메인 레포지토리가 모르는 상태라고 생각하고 있습니다. 그래서 비일관성이라는 점은 제 기준에서는 아니라는 점을 먼저 언급하고 싶네요.
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. 제가 이것을 보고도 그냥 넘어갔던 이유는... List가 순서가 매겨져 있기 때문이라고 생각해요. 각 타임박스의 순서가 정해져있기 때문에 'List'라는 도메인을 저장한다면 순서에 맞춰 저장할 것이라고 충분히 생각할 수 있다 생각했어요. 제 관점에서 본다면 List 보다 CustomizeTimeBoxes 와 같은 Wrapper 도메인 객체를 만들어 저장했다면, 비토의 현재 로직이 합리적이지 않았을까 생각합니다.
자료 구조가 Set 도 아니고 List라면... 이걸 매핑하는 개발자가 역순이나 섞어서 매핑하려 하지는 않을 것 같긴 해요. "순차적으로 매핑하여 보내주어야 함"을 얼마나 강조하고 싶은지의 문제일 수는 있겠네요. 제 최종 의견은... 자료 구조가 List 라는 것만으로도 충분히 '순서를 보장해야 한다'라는 이미지를 줄 수 있다 생각해요. (만약에 그렇지 않은 경우가 있을 때, 순서대로 매핑하는 것을 방지할 수 있는 장치가 있어야 한다고 생각합니다.) |
||
| IntStream.range(0, timeBoxes.size()) | ||
| .forEach(i -> saveTimeBox(tableEntity, timeBoxes.get(i), i + 1)); | ||
| } | ||
|
|
||
| private void saveTimeBox(CustomizeTableEntity tableEntity, CustomizeTimeBox timeBox, int sequence) { | ||
| CustomizeTimeBoxEntity timeBoxEntity = timeBoxRepository.save( | ||
| new CustomizeTimeBoxEntity(tableEntity, timeBox, sequence)); | ||
| timeBox.getBells() | ||
| .forEach(bell -> bellRepository.save(new BellEntity(timeBoxEntity, bell))); | ||
| } | ||
|
|
||
| @Transactional(readOnly = true) | ||
| public CustomizeTableResponse findTable(long tableId, Member member) { | ||
| public CustomizeTable getByIdAndMember(long tableId, Member member) { | ||
| return tableRepository.getByIdAndMember(tableId, member) | ||
| .toDomain(); | ||
| } | ||
|
|
||
| @Transactional(readOnly = true) | ||
| public List<CustomizeTimeBox> getCustomizeTimeBoxes(long tableId, Member member) { | ||
| CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); | ||
| List<CustomizeTimeBoxEntity> timeBoxEntityList = timeBoxRepository.findAllByCustomizeTable(tableEntity); | ||
| List<BellEntity> bellEntityList = bellRepository.findAllByCustomizeTimeBoxIn(timeBoxEntityList); | ||
| CustomizeTimeBoxEntities timeBoxEntities = new CustomizeTimeBoxEntities(timeBoxEntityList, bellEntityList); | ||
|
|
||
| return new CustomizeTableResponse(tableEntity.toDomain(), timeBoxEntities.toDomain()); | ||
| return timeBoxEntities.toDomain(); | ||
| } | ||
|
|
||
| @Transactional | ||
| public CustomizeTableResponse updateTable( | ||
| CustomizeTableCreateRequest tableCreateRequest, | ||
| long tableId, | ||
| Member member | ||
| ) { | ||
| public CustomizeTable update(CustomizeTable table, long tableId, Member member, List<CustomizeTimeBox> timeBoxes) { | ||
| CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); | ||
| tableEntity.updateTable(tableCreateRequest.toTable(member)); | ||
| tableEntity.updateTable(table); | ||
|
|
||
| bellRepository.deleteAllByTable(tableEntity.getId()); | ||
| timeBoxRepository.deleteAllByTable(tableEntity.getId()); | ||
| List<CustomizeTimeBox> timeBoxes = tableCreateRequest.toTimeBoxList(); | ||
|
|
||
| saveTimeBoxes(tableEntity, timeBoxes); | ||
| return new CustomizeTableResponse(tableEntity.toDomain(), timeBoxes); | ||
| return tableEntity.toDomain(); | ||
| } | ||
|
|
||
| @Transactional | ||
| public CustomizeTableResponse updateUsedAt(long tableId, Member member) { | ||
| public CustomizeTable updateUsedAt(long tableId, Member member) { | ||
| CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); | ||
| List<CustomizeTimeBoxEntity> timeBoxEntityList = timeBoxRepository.findAllByCustomizeTable(tableEntity); | ||
| List<BellEntity> bellEntityList = bellRepository.findAllByCustomizeTimeBoxIn(timeBoxEntityList); | ||
| CustomizeTimeBoxEntities timeBoxEntities = new CustomizeTimeBoxEntities(timeBoxEntityList, bellEntityList); | ||
|
|
||
| tableEntity.updateUsedAt(); | ||
| CustomizeTable table = tableEntity.toDomain(); | ||
| List<CustomizeTimeBox> timeBoxes = timeBoxEntities.toDomain(); | ||
| return new CustomizeTableResponse(table, timeBoxes); | ||
| return tableEntity.toDomain(); | ||
| } | ||
|
|
||
| @Transactional | ||
| public void deleteTable(long tableId, Member member) { | ||
| public void delete(long tableId, Member member) { | ||
| CustomizeTableEntity table = tableRepository.getByIdAndMember(tableId, member); | ||
|
|
||
| bellRepository.deleteAllByTable(table.getId()); | ||
| timeBoxRepository.deleteAllByTable(table.getId()); | ||
| tableRepository.delete(table); | ||
| } | ||
|
|
||
| private void saveTimeBoxes(CustomizeTableEntity tableEntity, List<CustomizeTimeBox> timeBoxes) { | ||
| IntStream.range(0, timeBoxes.size()) | ||
| .forEach(i -> saveTimeBox(tableEntity, timeBoxes.get(i), i + 1)); | ||
| } | ||
|
|
||
| private void saveTimeBox(CustomizeTableEntity tableEntity, CustomizeTimeBox timeBox, int sequence) { | ||
| CustomizeTimeBoxEntity timeBoxEntity = timeBoxRepository.save( | ||
| new CustomizeTimeBoxEntity(tableEntity, timeBox, sequence)); | ||
| timeBox.getBells() | ||
| .forEach(bell -> bellRepository.save(new BellEntity(timeBoxEntity, bell))); | ||
| } | ||
| } | ||
This file was deleted.
Uh oh!
There was an error while loading. Please reload this page.
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.
[질문]
이거 타입 + 양수 시간으로 나타내자고 하지 않았나요? 왜 AFTER_END에서는 음수죠? 치코랑 협의가 된 부분인지 궁금해요.
AFTER_END 30 이면 끝난 후 30초 등등으로 해석하기로 했던 것 같은데요?
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.
이건 콜리의 실수였던거로...