-
Notifications
You must be signed in to change notification settings - Fork 2
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] 주제, 선택지 조회 기능 구현 #36
Changes from 15 commits
009f49d
5d970fb
c4ae03e
c1867d3
81b9d14
62a14a9
0a4501b
da4ed13
f57c30c
8566753
935ff1e
458cd54
de80d87
31e3543
06b3dba
9e061d6
246538a
b042859
3d5b963
112e91b
39691e1
b7ba2f4
2cc2472
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,22 @@ | ||
package ddangkong.controller.content; | ||
|
||
import ddangkong.controller.content.dto.BalanceContentResponse; | ||
import ddangkong.service.content.BalanceContentService; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@RestController | ||
@RequestMapping("/api") | ||
@RequiredArgsConstructor | ||
public class BalanceContentController { | ||
|
||
private final BalanceContentService balanceContentService; | ||
|
||
@GetMapping("/balances/rooms/{roomId}/question") | ||
public BalanceContentResponse getBalanceContent(@PathVariable Long roomId) { | ||
return balanceContentService.findRecentBalanceContent(roomId); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package ddangkong.controller.content.dto; | ||
|
||
import ddangkong.controller.option.dto.BalanceOptionResponse; | ||
import ddangkong.domain.content.BalanceContent; | ||
import ddangkong.domain.content.Category; | ||
import ddangkong.domain.option.BalanceOption; | ||
import lombok.Builder; | ||
|
||
public record BalanceContentResponse( | ||
Long questionId, | ||
Category category, | ||
String title, | ||
BalanceOptionResponse firstOption, | ||
BalanceOptionResponse secondOption | ||
PgmJun marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) { | ||
|
||
@Builder | ||
private BalanceContentResponse(BalanceContent balanceContent, | ||
BalanceOption firstOption, | ||
BalanceOption secondOption) { | ||
this(balanceContent.getId(), | ||
balanceContent.getCategory(), | ||
balanceContent.getName(), | ||
BalanceOptionResponse.from(firstOption), | ||
BalanceOptionResponse.from(secondOption)); | ||
} | ||
leegwichan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} |
PgmJun marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
package ddangkong.controller.exception; | ||
|
||
import ddangkong.service.excpetion.BusinessLogicException; | ||
import ddangkong.service.excpetion.ViolateDataException; | ||
import jakarta.validation.ConstraintViolationException; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.http.HttpStatus; | ||
|
@@ -12,6 +14,8 @@ | |
@Slf4j | ||
public class GlobalExceptionHandler { | ||
|
||
private static final String SERVER_ERROR_MESSAGE = "서버 오류가 발생했습니다. 관리자에게 문의하세요."; | ||
|
||
@ExceptionHandler | ||
public ErrorResponse handleBindingException(BindException e) { | ||
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. 의견) 여기 ResponseStatus가 안되어 있는 것 같네요! 확인 부탁드려요! |
||
log.warn(e.getMessage()); | ||
|
@@ -26,11 +30,27 @@ public ErrorResponse handleConstraintViolationException(ConstraintViolationExcep | |
return new ErrorResponse(e.getConstraintViolations()); | ||
} | ||
|
||
@ExceptionHandler | ||
@ResponseStatus(HttpStatus.BAD_REQUEST) | ||
public ErrorResponse handleBusinessLogicException(BusinessLogicException e) { | ||
log.warn(e.getMessage()); | ||
|
||
return new ErrorResponse(e.getMessage()); | ||
} | ||
|
||
@ExceptionHandler | ||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) | ||
public ErrorResponse handleViolateDataException(ViolateDataException e) { | ||
log.error(e.getMessage()); | ||
|
||
return new ErrorResponse(SERVER_ERROR_MESSAGE); | ||
} | ||
|
||
@ExceptionHandler | ||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) | ||
public ErrorResponse handleException(Exception e) { | ||
log.error(e.getMessage()); | ||
|
||
return new ErrorResponse("서버 오류가 발생했습니다. 관리자에게 문의하세요."); | ||
return new ErrorResponse(SERVER_ERROR_MESSAGE); | ||
} | ||
} |
PgmJun marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package ddangkong.controller.option.dto; | ||
|
||
import ddangkong.domain.option.BalanceOption; | ||
|
||
public record BalanceOptionResponse( | ||
Long optionId, | ||
String content | ||
) { | ||
public static BalanceOptionResponse from(BalanceOption balanceOption) { | ||
return new BalanceOptionResponse(balanceOption.getId(), balanceOption.getName()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package ddangkong.domain; | ||
|
||
import jakarta.persistence.Column; | ||
import jakarta.persistence.EntityListeners; | ||
import jakarta.persistence.MappedSuperclass; | ||
import java.time.LocalDateTime; | ||
import lombok.Getter; | ||
import org.springframework.data.annotation.CreatedDate; | ||
import org.springframework.data.jpa.domain.support.AuditingEntityListener; | ||
|
||
@MappedSuperclass | ||
@EntityListeners(AuditingEntityListener.class) | ||
@Getter | ||
public class BaseEntity { | ||
PgmJun marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
@CreatedDate | ||
@Column(updatable = false, nullable = false) | ||
private LocalDateTime createdAt; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
package ddangkong.domain.question; | ||
package ddangkong.domain.content; | ||
|
||
public enum Category { | ||
EXAMPLE, | ||
|
PgmJun marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package ddangkong.domain.content; | ||
|
||
import ddangkong.domain.room.RoomContent; | ||
import java.util.Optional; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
public interface RoomContentRepository extends JpaRepository<RoomContent, Long> { | ||
|
||
Optional<RoomContent> findTopByRoomIdOrderByCreatedAtDesc(Long roomId); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package ddangkong.domain.option; | ||
|
||
import ddangkong.domain.content.BalanceContent; | ||
import java.util.List; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
public interface BalanceOptionRepository extends JpaRepository<BalanceOption, Long> { | ||
|
||
List<BalanceOption> findByBalanceContent(BalanceContent balanceContent); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
package ddangkong.domain.room; | ||
|
||
import ddangkong.domain.question.BalanceQuestion; | ||
import ddangkong.domain.BaseEntity; | ||
import ddangkong.domain.content.BalanceContent; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.FetchType; | ||
import jakarta.persistence.GeneratedValue; | ||
|
@@ -9,17 +10,13 @@ | |
import jakarta.persistence.JoinColumn; | ||
import jakarta.persistence.ManyToOne; | ||
import lombok.AccessLevel; | ||
import lombok.EqualsAndHashCode; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import lombok.ToString; | ||
|
||
@Entity | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
@Getter | ||
@EqualsAndHashCode | ||
@ToString | ||
public class RoomQuestion { | ||
public class RoomContent extends BaseEntity { | ||
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. 질문) BaseEntity가 RoomContent에 필요한가에 대해 조금 생각해봤는데요! 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의 역할은 해당 Row(객체)에 고유값을 부여하는 것이라고 생각합니다. 실 구현이 시간과 관련있다고 해서 이를 이용하여 구현한다면, 나중에 사이드 이펙트가 생길 수 있다고 생각합니다. 지금은 Auto increment 정책을 사용하고 있어서 가능하겠지만, 추후 이 정책이 바뀐다면 관련 구현이 바뀌어야 할 것 같아요, |
||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
|
@@ -30,6 +27,6 @@ public class RoomQuestion { | |
private Room room; | ||
|
||
@ManyToOne(optional = false, fetch = FetchType.LAZY) | ||
@JoinColumn(name = "balance_question_id") | ||
private BalanceQuestion balanceQuestion; | ||
@JoinColumn(name = "balance_content_id") | ||
private BalanceContent balanceContent; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package ddangkong.service.content; | ||
|
||
import ddangkong.controller.content.dto.BalanceContentResponse; | ||
import ddangkong.domain.option.BalanceOption; | ||
import ddangkong.domain.option.BalanceOptionRepository; | ||
import ddangkong.domain.content.BalanceContent; | ||
import ddangkong.domain.content.RoomContentRepository; | ||
import ddangkong.service.excpetion.BusinessLogicException; | ||
import ddangkong.service.excpetion.ViolateDataException; | ||
import java.util.List; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class BalanceContentService { | ||
|
||
private static final int BALANCE_OPTION_SIZE = 2; | ||
|
||
private final RoomContentRepository roomContentRepository; | ||
|
||
private final BalanceOptionRepository balanceOptionRepository; | ||
|
||
@Transactional(readOnly = true) | ||
public BalanceContentResponse findRecentBalanceContent(Long roomId) { | ||
BalanceContent balanceContent = findRecentContent(roomId); | ||
List<BalanceOption> balanceOptions = findBalanceOptions(balanceContent); | ||
|
||
return BalanceContentResponse.builder() | ||
.balanceContent(balanceContent) | ||
.firstOption(balanceOptions.get(0)) | ||
.secondOption(balanceOptions.get(1)) | ||
.build(); | ||
} | ||
|
||
private BalanceContent findRecentContent(Long roomId) { | ||
return roomContentRepository.findTopByRoomIdOrderByCreatedAtDesc(roomId) | ||
.orElseThrow(() -> new BusinessLogicException("해당 방의 질문이 존재하지 않습니다.")) | ||
.getBalanceContent(); | ||
} | ||
|
||
private List<BalanceOption> findBalanceOptions(BalanceContent balanceContent) { | ||
List<BalanceOption> balanceOptions = balanceOptionRepository.findByBalanceContent(balanceContent); | ||
validateBalanceOptions(balanceOptions); | ||
return balanceOptions; | ||
} | ||
|
||
private void validateBalanceOptions(List<BalanceOption> balanceOptions) { | ||
if (balanceOptions.size() != BALANCE_OPTION_SIZE) { | ||
throw new ViolateDataException("밸런스 게임의 선택지가 %d개입니다".formatted(balanceOptions.size())); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package ddangkong.service.excpetion; | ||
|
||
public class BusinessLogicException extends RuntimeException { | ||
PgmJun marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
public BusinessLogicException(String message) { | ||
super(message); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package ddangkong.service.excpetion; | ||
|
||
public class ViolateDataException extends RuntimeException { | ||
|
||
public ViolateDataException(String message) { | ||
super(message); | ||
} | ||
} |
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.
의견) BalanceOption, BalanceContent 라는 도메인 네이밍이지만 패키지는 그냥 content 인 것이 괜찮을 지 고민이 되네요
저희가 밸런스 게임 제공 서비스가 아니라 대화주제 제공 서비스이며, 밸런스 게임은 그 중 하나의 도메인일 뿐이니 이런식으로 두는 건 어떨까에 대해 같이 고민해보면 좋을 것 같아요!
depth가 깊어지지만 알아보기는 더 쉬울 것 같기도 합니다:)
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.
7/22 회의 결과, balance package 추가하도록 하겠습니다~