-
Notifications
You must be signed in to change notification settings - Fork 1
[FEAT] 종소리 커스텀 기능 #193
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
[FEAT] 종소리 커스텀 기능 #193
Changes from all commits
614c8c5
83fce1c
9565128
b623ce8
d29a716
255ba33
e22c63f
8fb7c15
3b03178
3c33a87
56fc04f
fc8639a
e56455e
0626b31
96a2609
507e39f
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,7 @@ | ||
| package com.debatetimer.dto.customize.request; | ||
|
|
||
| public record BellRequest( | ||
| int time, | ||
| int count | ||
| ) { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.debatetimer.dto.customize.response; | ||
|
|
||
| public record BellResponse( | ||
| int time, | ||
| int count | ||
| ) { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,12 +3,14 @@ | |
| import com.debatetimer.domain.customize.CustomizeBoxType; | ||
| import com.debatetimer.domain.customize.Stance; | ||
| import com.debatetimer.entity.customize.CustomizeTimeBox; | ||
| import java.util.List; | ||
|
|
||
| public record CustomizeTimeBoxResponse( | ||
| Stance stance, | ||
| String speechType, | ||
| CustomizeBoxType boxType, | ||
| Integer time, | ||
| List<BellResponse> bell, | ||
| Integer timePerTeam, | ||
| Integer timePerSpeaking, | ||
| String speaker | ||
|
|
@@ -20,6 +22,20 @@ public CustomizeTimeBoxResponse(CustomizeTimeBox customizeTimeBox) { | |
| customizeTimeBox.getSpeechType(), | ||
| customizeTimeBox.getBoxType(), | ||
| convertTime(customizeTimeBox), | ||
| null, | ||
|
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. [추후 논의 부탁]
Request에서 Dto to Domain을 할때 빈 리스트를 반환하거나 초기화 방식으로 논리 일관성을 가져가면 어떨까요? 그럼 비토가 쓴 서비스 코드도 null을 의식하지 않고 분기문없이 초기화가능할 것 같아서요 (아... 코틀린 배우는 중이라 그런게 코틀린 마렵네 😄 ) |
||
| customizeTimeBox.getTimePerTeam(), | ||
| customizeTimeBox.getTimePerSpeaking(), | ||
| customizeTimeBox.getSpeaker() | ||
| ); | ||
| } | ||
|
|
||
| public CustomizeTimeBoxResponse(CustomizeTimeBox customizeTimeBox, List<BellResponse> bell) { | ||
| this( | ||
| customizeTimeBox.getStance(), | ||
| customizeTimeBox.getSpeechType(), | ||
| customizeTimeBox.getBoxType(), | ||
| convertTime(customizeTimeBox), | ||
| bell, | ||
| customizeTimeBox.getTimePerTeam(), | ||
| customizeTimeBox.getTimePerSpeaking(), | ||
| customizeTimeBox.getSpeaker() | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| package com.debatetimer.entity.customize; | ||
|
|
||
| import com.debatetimer.exception.custom.DTClientErrorException; | ||
| import com.debatetimer.exception.errorcode.ClientErrorCode; | ||
| import jakarta.persistence.Column; | ||
| import jakarta.persistence.Entity; | ||
| import jakarta.persistence.FetchType; | ||
| import jakarta.persistence.GeneratedValue; | ||
| import jakarta.persistence.GenerationType; | ||
| import jakarta.persistence.Id; | ||
| import jakarta.persistence.JoinColumn; | ||
| import jakarta.persistence.ManyToOne; | ||
| import jakarta.persistence.Table; | ||
| import jakarta.validation.constraints.NotNull; | ||
| import lombok.AccessLevel; | ||
| import lombok.Getter; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| @Table(name = "bell") | ||
| @Entity | ||
| @Getter | ||
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
| public class BellEntity { | ||
|
|
||
| public static final int MAX_BELL_COUNT = 3; | ||
|
|
||
| @Id | ||
| @GeneratedValue(strategy = GenerationType.IDENTITY) | ||
| private Long id; | ||
|
|
||
| @NotNull | ||
| @ManyToOne(fetch = FetchType.LAZY) | ||
| @JoinColumn(name = "customize_time_box_id") | ||
| private CustomizeTimeBox customizeTimeBox; | ||
|
|
||
| @Column(name = "bell_time") | ||
| private int time; | ||
| private int count; | ||
|
|
||
| public BellEntity(CustomizeTimeBox customizeTimeBox, int time, int count) { | ||
| validateTime(time); | ||
| validateCount(count); | ||
|
|
||
| this.customizeTimeBox = customizeTimeBox; | ||
| this.time = time; | ||
| this.count = count; | ||
| } | ||
|
|
||
| private void validateTime(int time) { | ||
| if (time < 0) { | ||
| throw new DTClientErrorException(ClientErrorCode.INVALID_BELL_TIME); | ||
| } | ||
| } | ||
|
|
||
| private void validateCount(int count) { | ||
| if (count <= 0 || count > MAX_BELL_COUNT) { | ||
| throw new DTClientErrorException(ClientErrorCode.INVALID_BELL_COUNT); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package com.debatetimer.repository.customize; | ||
|
|
||
| import com.debatetimer.entity.customize.BellEntity; | ||
| import com.debatetimer.entity.customize.CustomizeTimeBox; | ||
| import java.util.List; | ||
| import org.springframework.data.repository.Repository; | ||
|
|
||
| public interface BellRepository extends Repository<BellEntity, Long> { | ||
|
|
||
| BellEntity save(BellEntity bell); | ||
|
|
||
| List<BellEntity> findByCustomizeTimeBox(CustomizeTimeBox customizeTimeBox); | ||
|
|
||
| void deleteAllByCustomizeTimeBoxIn(List<CustomizeTimeBox> customizeTimeBoxes); | ||
|
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. [질문] 오... 이거 in절로 해서 한번에 찾아와지는 건가요? 신기..🤔
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. 처음 보는 쿼리 메소드라 궁금해서 그런데 이거
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. 전자입니다. 다만 그로인해 delete 쿼리가 customizeTimeBoxes의 수만큼 나가게 되긴하지만 악의적 유저의 요청을 제외하곤 잘 처리될 것 같습니다. 악의적 유저가 많은 것 같긴한데... 일단 좀 더 고민해보죠
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. 낙낙 패거리에 비토가 포함되어 있는 거잖아요? |
||
|
|
||
| List<BellEntity> findAllByCustomizeTimeBoxIn(List<CustomizeTimeBox> timeBoxes); | ||
| } | ||
leegwichan marked this conversation as resolved.
Show resolved
Hide resolved
|
|
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. 서비스 로직이 길어지는 건 어쩔 수 없는 것 같아요. 일단 넘어가고, 월요일에 더 이야기해보죠 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| package com.debatetimer.service.customize; | ||
|
|
||
| import com.debatetimer.domain.customize.CustomizeTable; | ||
| import com.debatetimer.domain.customize.CustomizeTimeBoxes; | ||
| import com.debatetimer.domain.member.Member; | ||
| import com.debatetimer.dto.customize.request.BellRequest; | ||
| import com.debatetimer.dto.customize.request.CustomizeTableCreateRequest; | ||
| import com.debatetimer.dto.customize.request.CustomizeTimeBoxCreateRequest; | ||
| import com.debatetimer.dto.customize.response.BellResponse; | ||
| import com.debatetimer.dto.customize.response.CustomizeTableResponse; | ||
| import com.debatetimer.dto.customize.response.CustomizeTimeBoxResponse; | ||
| import com.debatetimer.entity.customize.BellEntity; | ||
| import com.debatetimer.entity.customize.CustomizeTableEntity; | ||
| import com.debatetimer.entity.customize.CustomizeTimeBox; | ||
| import com.debatetimer.repository.customize.BellRepository; | ||
| import com.debatetimer.repository.customize.CustomizeTableRepository; | ||
| import com.debatetimer.repository.customize.CustomizeTimeBoxRepository; | ||
| import java.util.List; | ||
| import java.util.stream.IntStream; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Service; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
| @Service | ||
| @RequiredArgsConstructor | ||
| public class CustomizeServiceV2 { | ||
|
|
||
| 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); | ||
| CustomizeTableEntity savedTable = tableRepository.save(new CustomizeTableEntity(table)); | ||
|
|
||
| return saveTimeBoxesAndBells(tableCreateRequest, savedTable.toDomain()); | ||
| } | ||
|
|
||
| @Transactional(readOnly = true) | ||
| public CustomizeTableResponse findTable(long tableId, Member member) { | ||
| CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); | ||
| CustomizeTimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(tableEntity); | ||
| List<CustomizeTimeBoxResponse> timeBoxResponses = timeBoxes.getTimeBoxes() | ||
| .stream() | ||
| .map(this::getTimeBoxResponse) | ||
|
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. [추후 반영 제안]
=> 내외적으로 TimeBox와 Bell을 함께 다루는 객체를 커찬이 이야기꺼냈던 것 같은데 현재 타임박스 안에 bell이 완전 종속관계로 따라다니다보니까 두 객체를 함께 다루어야 하고 + 반환형은 하나이므로 도메인 객체나 엔티티가 아닌 DTO를 반환하는 private 메서드를 만들 수 밖에 없다는 게 아쉬운 것 같아요.
|
||
| .toList(); | ||
| return new CustomizeTableResponse(tableEntity.toDomain(), timeBoxResponses); | ||
| } | ||
|
|
||
| @Transactional | ||
| public CustomizeTableResponse updateTable( | ||
| CustomizeTableCreateRequest tableCreateRequest, | ||
| long tableId, | ||
| Member member | ||
| ) { | ||
| CustomizeTableEntity existingTable = tableRepository.getByIdAndMember(tableId, member); | ||
| CustomizeTable renewedTable = tableCreateRequest.toTable(member); | ||
| existingTable.updateTable(renewedTable); | ||
|
|
||
| deleteBell(timeBoxRepository.findTableTimeBoxes(existingTable)); | ||
| timeBoxRepository.deleteAllByTable(existingTable); | ||
| return saveTimeBoxesAndBells(tableCreateRequest, existingTable.toDomain()); | ||
| } | ||
|
|
||
| @Transactional | ||
| public CustomizeTableResponse updateUsedAt(long tableId, Member member) { | ||
| CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); | ||
| CustomizeTimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(tableEntity); | ||
| tableEntity.updateUsedAt(); | ||
| List<CustomizeTimeBoxResponse> timeBoxResponses = timeBoxes.getTimeBoxes() | ||
| .stream() | ||
| .map(this::getTimeBoxResponse) | ||
| .toList(); | ||
| return new CustomizeTableResponse(tableEntity.toDomain(), timeBoxResponses); | ||
| } | ||
|
|
||
| @Transactional | ||
| public void deleteTable(long tableId, Member member) { | ||
| CustomizeTableEntity table = tableRepository.getByIdAndMember(tableId, member); | ||
|
|
||
| deleteBell(timeBoxRepository.findTableTimeBoxes(table)); | ||
| timeBoxRepository.deleteAllByTable(table); | ||
| tableRepository.delete(table); | ||
| } | ||
|
|
||
| private CustomizeTableResponse saveTimeBoxesAndBells( | ||
| CustomizeTableCreateRequest tableCreateRequest, | ||
| CustomizeTable table | ||
| ) { | ||
| List<CustomizeTimeBoxCreateRequest> timeBoxCreateRequests = tableCreateRequest.table(); | ||
| List<CustomizeTimeBoxResponse> timeBoxResponses = IntStream.range(0, timeBoxCreateRequests.size()) | ||
|
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. 이 코드 마음에 안드는데 익숙해서 어디서 봤나 했더니 내가 쓴 코드였네 ㅋㅋ |
||
| .mapToObj(i -> createTimeBoxResponse(timeBoxCreateRequests.get(i), table, i + 1)) | ||
| .toList(); | ||
| return new CustomizeTableResponse(table, timeBoxResponses); | ||
| } | ||
|
|
||
| private CustomizeTimeBoxResponse createTimeBoxResponse( | ||
| CustomizeTimeBoxCreateRequest request, | ||
| CustomizeTable table, | ||
| int sequence | ||
| ) { | ||
| CustomizeTimeBox savedTimeBox = timeBoxRepository.save(request.toTimeBox(table, sequence)); | ||
| return createTimeBoxResponse(request.bell(), savedTimeBox); | ||
| } | ||
|
|
||
| private CustomizeTimeBoxResponse createTimeBoxResponse(List<BellRequest> bellRequests, CustomizeTimeBox timeBox) { | ||
| if (timeBox.getBoxType().isTimeBased()) { | ||
| return new CustomizeTimeBoxResponse(timeBox, null); | ||
| } | ||
|
|
||
| List<BellResponse> bellResponses = bellRequests | ||
| .stream() | ||
| .map(bellRequest -> new BellEntity(timeBox, bellRequest.time(), bellRequest.count())) | ||
| .map(bellRepository::save) | ||
| .map(bell -> new BellResponse(bell.getTime(), bell.getCount())) | ||
| .toList(); | ||
| return new CustomizeTimeBoxResponse(timeBox, bellResponses); | ||
| } | ||
|
|
||
| private CustomizeTimeBoxResponse getTimeBoxResponse(CustomizeTimeBox timeBox) { | ||
| if (timeBox.getBoxType().isTimeBased()) { | ||
| return new CustomizeTimeBoxResponse(timeBox, null); | ||
| } | ||
|
|
||
| List<BellResponse> bellResponses = bellRepository.findByCustomizeTimeBox(timeBox) | ||
| .stream() | ||
| .map(bell -> new BellResponse(bell.getTime(), bell.getCount())) | ||
| .toList(); | ||
| return new CustomizeTimeBoxResponse(timeBox, bellResponses); | ||
| } | ||
|
|
||
| private void deleteBell(CustomizeTimeBoxes savedCustomizeTimeBoxes) { | ||
| bellRepository.deleteAllByCustomizeTimeBoxIn(savedCustomizeTimeBoxes.getTimeBoxes()); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| create table bell | ||
| ( | ||
| id bigint auto_increment, | ||
| bell_time bigint not null, | ||
| count bigint not null, | ||
| customize_time_box_id bigint not null, | ||
| primary key (id) | ||
| ); | ||
|
|
||
| alter table bell | ||
| add constraint bell_to_customize_time_box | ||
| foreign key (customize_time_box_id) | ||
| references customize_time_box(id); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |
| import com.debatetimer.client.oauth.OAuthClient; | ||
| import com.debatetimer.domain.customize.CustomizeBoxType; | ||
| import com.debatetimer.domain.customize.Stance; | ||
| import com.debatetimer.dto.customize.request.BellRequest; | ||
| import com.debatetimer.dto.customize.request.CustomizeTableCreateRequest; | ||
| import com.debatetimer.dto.customize.request.CustomizeTableInfoCreateRequest; | ||
| import com.debatetimer.dto.customize.request.CustomizeTimeBoxCreateRequest; | ||
|
|
@@ -97,8 +98,15 @@ private ArbitraryBuilder<CustomizeTimeBoxCreateRequest> getCustomizeTimeBoxCreat | |
| .set("speechType", "입론1") | ||
| .set("boxType", CustomizeBoxType.NORMAL) | ||
| .set("time", 120) | ||
| .set("bell", getBellRequestBuilder().sampleList(2)) | ||
|
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. 어우 픽스쳐 몽키 사용하길 잘했다 ㅋㅋ |
||
| .set("timePerTeam", 60) | ||
| .set("timePerSpeaking", null) | ||
| .set("speaker", "발언자"); | ||
| } | ||
|
|
||
| private ArbitraryBuilder<BellRequest> getBellRequestBuilder() { | ||
| return fixtureMonkey.giveMeBuilder(BellRequest.class) | ||
| .set("time", 30) | ||
| .set("count", 1); | ||
| } | ||
| } | ||
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.
새로운 bell 필드가 추가되었지만 변환 로직에서 처리되지 않습니다.
bell필드가 요청 DTO에 추가되었지만,toTimeBox메서드에서 이 데이터를 처리하지 않고 있습니다. 이로 인해 클라이언트가 전송한 벨 설정 정보가 손실될 수 있습니다.🤖 Prompt for AI Agents