Skip to content

Commit

Permalink
merge: exception 메세지 구체화 #118
Browse files Browse the repository at this point in the history
[FEAT] exception 메세지 구체화 #118
  • Loading branch information
sjk4618 authored Jan 21, 2025
2 parents daa4aea + a73713b commit 8f609ee
Show file tree
Hide file tree
Showing 21 changed files with 173 additions and 92 deletions.
3 changes: 2 additions & 1 deletion cakey-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ dependencies {
implementation group: 'org.postgresql', name: 'postgresql', version: '42.7.3'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-security'
// implementation 'org.springframework.boot:spring-boot-starter-data-redis:3.2.1' //redis
implementation 'org.springframework.boot:spring-boot-starter-validation'

implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:4.1.0' //feign

implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.5' //jwt 의존성
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@
import com.cakey.common.response.BaseResponse;
import com.cakey.rescode.SuccessCode;
import com.cakey.store.domain.Station;
import jakarta.validation.constraints.Min;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/v1/cake")
@RequiredArgsConstructor
@Validated
public class CakeController {
private final CakeService cakeService;

Expand Down Expand Up @@ -56,7 +59,7 @@ public ResponseEntity<BaseResponse<?>> getRankCakesByStationStore(@UserId final
@GetMapping("/select/{cakeId}")
public ResponseEntity<BaseResponse<?>> getCakeSelect(
@UserId final Long userId,
@PathVariable(value = "cakeId") final long cakeId,
@PathVariable(value = "cakeId") @Min(1) final long cakeId,
@RequestParam(value = "dayCategory") final DayCategory dayCategory,
@RequestParam(value = "themeName") final ThemeName themeName
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,6 @@ public enum CakeErrorCode implements ErrorCode {








;


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,13 @@ public CakesPopularListRes getPopularCakesByStationStore(final Long userId,
}

public CakeListByPopularityRes getCakeByRank(final Long userId) {
final List<CakeByPopularityDto> cakeByPopularityDtos;
try {
final List<CakeByPopularityDto> cakeByPopularityDtos = cakeFacade.findCakeByRank(userId);
return new CakeListByPopularityRes(cakeByPopularityDtos);
cakeByPopularityDtos = cakeFacade.findCakeByRank(userId);
} catch (NotFoundBaseException e) {
throw new CakeNotFoundException(CakeErrorCode.CAKE_NOT_FOUND_ENTITY);
}
return new CakeListByPopularityRes(cakeByPopularityDtos);
}


Expand Down Expand Up @@ -176,7 +177,6 @@ public CakesLatestListRes findCakesByCategoryAndTheme(final DayCategory dayCateg
final boolean isLastData = cakeInfoDtos.get(lastCakeInfoDtosIndex).isLastData();
final Long nextCakeIdCursor = cakeInfoDtos.get(lastCakeInfoDtosIndex).getCakeIdCursor();


final List<CakeInfo> cakes = cakeInfoDtos.stream()
.map(cakeInfoDto -> CakeInfo.of(
cakeInfoDto.getCakeId(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
import com.cakey.cake.domain.Cake;
import com.cakey.cake.dto.CakeInfo;
import com.cakey.cake.dto.CakeInfoDto;
import com.cakey.cake.exception.CakeErrorCode;
import com.cakey.cake.exception.CakeNotFoundException;
import com.cakey.cake.facade.CakeFacade;
import com.cakey.cakelike.domain.CakeLikes;
import com.cakey.cakelike.facade.CakeLikesFacade;
import com.cakey.cakelike.facade.CakeLikesRemover;
import com.cakey.cakelikes.dto.CakeLikedLatestRes;
import com.cakey.cakelikes.dto.CakeLikedPopularRes;
import com.cakey.common.exception.NotFoundBaseException;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
Expand All @@ -28,10 +31,13 @@ public class CakeLikesService {
public CakeLikedLatestRes getLatestCakeLikedByUser(final long userId,
final Long cakeIdCursor,
final Integer size) {

final List<CakeInfoDto> cakeInfoDtos;
// 페이지네이션 조회
final List<CakeInfoDto> cakeInfoDtos = cakeFacade.findLatestLikedCakesByUser(userId, cakeIdCursor, size);

try {
cakeInfoDtos = cakeFacade.findLatestLikedCakesByUser(userId, cakeIdCursor, size);
} catch (NotFoundBaseException e) {
throw new CakeNotFoundException(CakeErrorCode.CAKE_NOT_FOUND_ENTITY);
}
//마지막 데이터인지
final int lastCakeInfoDtosIndex = cakeInfoDtos.size() - 1;
final boolean isLastData = cakeInfoDtos.get(lastCakeInfoDtosIndex).isLastData();
Expand Down Expand Up @@ -63,10 +69,14 @@ public CakeLikedPopularRes getPopularLikedCakesByUser(final long userId,
final Long cakeIdCursor,
final Integer cakeLikesCursor,
final int size) {
final List<CakeInfoDto> cakeInfoDtos;

// 페이지네이션 조회
final List<CakeInfoDto> cakeInfoDtos = cakeFacade.findPopularLikedCakesByUser(userId, cakeIdCursor,cakeLikesCursor, size);

try {
cakeInfoDtos = cakeFacade.findPopularLikedCakesByUser(userId, cakeIdCursor, cakeLikesCursor, size);
} catch (NotFoundBaseException e) {
throw new CakeNotFoundException(CakeErrorCode.CAKE_NOT_FOUND_ENTITY);
}
//마지막 데이터인지
final int lastCakeInfoDtosIndex = cakeInfoDtos.size() - 1;
final int nextLikesCursor = cakeInfoDtos.get(lastCakeInfoDtosIndex).getCakeLikeCount();
Expand Down Expand Up @@ -96,21 +106,29 @@ public CakeLikedPopularRes getPopularLikedCakesByUser(final long userId,

@Transactional
public void postCakeLike(final long cakeId, final long userId) {
Cake cake = cakeFacade.findById(cakeId);
try {
final Cake cake = cakeFacade.findById(cakeId);
} catch (NotFoundBaseException e) {
throw new CakeNotFoundException(CakeErrorCode.CAKE_NOT_FOUND_ENTITY);
}

if (!cakeLikesFacade.existsCakeLikesByCakeIdAndUserId(cakeId, userId)) {
CakeLikes cakeLikes = CakeLikes.createCakeLikes(cakeId, userId);
final CakeLikes cakeLikes = CakeLikes.createCakeLikes(cakeId, userId);
cakeLikesFacade.saveCakeLikes(cakeLikes);
} else {
//todo: 추후 구체적인 예외 처리
throw new RuntimeException("Cake like already exists");
throw new CakeNotFoundException(CakeErrorCode.CAKE_LIKES_CONFLICT);
}
}

//케이크 좋아요 취소
@Transactional
public void deleteCakeLikes(final long cakeId, final long userId) {
CakeLikes cakeLikes = cakeLikesFacade.getCakeLikesByCakeIdAndUserId(cakeId, userId);
final CakeLikes cakeLikes;
try {
cakeLikes = cakeLikesFacade.getCakeLikesByCakeIdAndUserId(cakeId, userId);
} catch (NotFoundBaseException e) {
throw new CakeNotFoundException(CakeErrorCode.CAKE_LIKES_NOT_FOUND_ENTITY);
}
cakeLikesFacade.removeCakeLikes(cakeLikes);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ public class OptionalAuthenticationFilter extends OncePerRequestFilter { //로
"/api/v1/store/likes/latest/*",
"/api/v1/store/likes/popularity/*",
"/api/v1/store/likes/coordinate",
"/api/v1/store/likes/cake/latest/*",
"/api/v1/store/likes/cake/popularity/*",
"/api/v1/cake/store/likes/cake/latest/*",
"/api/v1/cake/store/likes/cake/popularity/*",
"/api/v1/store/likes/*",
"/api/v1/cake/likes/*",
"/api/v1/cake/likes/latest/*",
Expand Down
4 changes: 2 additions & 2 deletions cakey-api/src/main/java/com/cakey/config/FilterConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ public FilterRegistrationBean<RequiredAuthenticationFilter> requiredAuthenticati
"/api/v1/store/likes/latest/*",
"/api/v1/store/likes/popularity/*",
"/api/v1/store/likes/coordinate",
"/api/v1/store/likes/cake/latest/*",
"/api/v1/store/likes/cake/popularity/*",
"/api/v1/cake/store/likes/cake/latest/*",
"/api/v1/cake/store/likes/cake/popularity/*",
"/api/v1/store/likes/*",
"/api/v1/cake/likes/*",
"/api/v1/cake/likes/latest/*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import com.cakey.user.exception.UserApiBaseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import jakarta.persistence.EntityNotFoundException;
import jakarta.validation.ConstraintViolationException;
import lombok.val;
import org.hibernate.exception.ConstraintViolationException;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.dao.DataIntegrityViolationException;
Expand Down Expand Up @@ -61,6 +61,21 @@ public ResponseEntity<BaseResponse<?>> handleMethodArgumentNotValidException(fin
return ApiResponseUtil.failure(ErrorBaseCode.BAD_REQUEST_REQUEST_BODY_VALID, errorMessage);
}

/**
* 400 - ConstraintViolationException
* 발생 이유 : @Validated 검증 실패
*/
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<BaseResponse<?>> handleConstraintViolationException(final ConstraintViolationException e) {
String errorMessage = e.getConstraintViolations().stream()
.map(violation -> String.format("Field '%s': %s",
violation.getPropertyPath(),
violation.getMessage()
))
.collect(Collectors.joining(", "));
return ApiResponseUtil.failure(ErrorBaseCode.BAD_REQUEST_REQUEST_BODY_VALID, errorMessage);
}

/**
* 400 - HandlerMethodValidationException
* 발생 이유 : @Valid 검증 실패 (@Request Param, @ModelAttribute)
Expand Down Expand Up @@ -168,7 +183,7 @@ public ResponseEntity<BaseResponse<?>> handleDataIntegrityViolationException(fin
if (e.getCause() instanceof ConstraintViolationException constraintViolationException) {

// 제약 조건 이름 추출
String constraintName = constraintViolationException.getConstraintName().toString();
String constraintName = constraintViolationException.getConstraintViolations().toString();
String errorMessage = String.format("제약 조건 '%s' 위반이 발생했습니다.", constraintName);
// log.info(errorMessage);
return ApiResponseUtil.failure(ErrorBaseCode.INTEGRITY_CONFLICT, errorMessage);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import org.springframework.http.HttpStatus;

public class StoreConflictBaseException extends StoreApiBaseException {
protected StoreConflictBaseException(ErrorCode errorCode) {
public StoreConflictBaseException(ErrorCode errorCode) {
super(errorCode);
}

Expand Down
80 changes: 44 additions & 36 deletions cakey-api/src/main/java/com/cakey/store/service/StoreService.java
Original file line number Diff line number Diff line change
Expand Up @@ -149,17 +149,6 @@ public List<AllStationRes.StationInfo> getAllStation() {
.collect(Collectors.toList());
}

private int calculateNextCursor(final List<StoreInfoDto> storeInfoDtos) {
return storeInfoDtos.isEmpty() ? -1 : storeInfoDtos.get(storeInfoDtos.size() - 1).getStoreLikesCount();
}

//스토어 id들 조회
private List<Long> getStoreIds(final List<StoreInfoDto> storeInfoDtos) {
return storeInfoDtos.stream()
.map(StoreInfoDto::getStoreId)
.toList();
}

//storeInfo 생성
private List<StoreInfo> getStoreInfo(final List<StoreInfoDto> storeInfoDtos, final Map<Long, List<CakeMainImageDto>> imageMap) {
return storeInfoDtos.stream()
Expand Down Expand Up @@ -200,7 +189,7 @@ public StoreDetailAllDesignRes getStoreAllDesign(final long storeId, final Long
// 케이크 조회
// 스토어 ID로 케이크 리스트 조회
try {
cakes = cakeFacade.findAllByStoreId(storeId);
cakes = cakeFacade.findAllByStoreId(storeId);
} catch (NotFoundBaseException e) {
throw new CakeNotFoundException(CakeErrorCode.CAKE_NOT_FOUND_ENTITY);
}
Expand All @@ -227,7 +216,7 @@ public StoreDetailAllDesignRes getStoreAllDesign(final long storeId, final Long
public StoreAllSizeAndTasteRes getStoreSizeAndTaste(final long storeId) {
final List<SizeDto> sizeList;
try {
sizeList = sizeFacade.findSizeAllByStoreIdAndOrderByPriceAsc(storeId);
sizeList = sizeFacade.findSizeAllByStoreIdAndOrderByPriceAsc(storeId);
} catch (NotFoundBaseException e) {
throw new StoreNotfoundException(StoreErrorCode.STORE_NOT_FOUND_ENTITY);
}
Expand All @@ -239,45 +228,64 @@ public StoreDetailInfoRes getStoreDetailInfo(final long storeId) {
StoreOperationTimeDto storeOperationTimeDto;
try {
storeDetailInfoDto = storeFacade.findStoreDetailInfo(storeId);
} catch (NotFoundBaseException e) {
throw new StoreNotfoundException(StoreErrorCode.STORE_NOT_FOUND_ENTITY);
}

try {
storeOperationTimeDto = storeOperationTimeFacade.findStoreOperationTimeByStoreId(storeId);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm");
return StoreDetailInfoRes.of(
storeOperationTimeDto.monOpen().format(formatter),
storeOperationTimeDto.monClose().format(formatter),
storeOperationTimeDto.tueOpen().format(formatter),
storeOperationTimeDto.tueClose().format(formatter),
storeOperationTimeDto.wedOpen().format(formatter),
storeOperationTimeDto.wedClose().format(formatter),
storeOperationTimeDto.thuOpen().format(formatter),
storeOperationTimeDto.thuClose().format(formatter),
storeOperationTimeDto.friOpen().format(formatter),
storeOperationTimeDto.friClose().format(formatter),
storeOperationTimeDto.satOpen().format(formatter),
storeOperationTimeDto.satClose().format(formatter),
storeOperationTimeDto.sunOpen().format(formatter),
storeOperationTimeDto.sunClose().format(formatter),
storeDetailInfoDto.address(),
storeDetailInfoDto.phone()
);
} catch (NotFoundBaseException e) {
throw new StoreNotfoundException(STORE_OPERATION_TIME_NOT_FOUND);
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm");
return StoreDetailInfoRes.of(
storeOperationTimeDto.monOpen().format(formatter),
storeOperationTimeDto.monClose().format(formatter),
storeOperationTimeDto.tueOpen().format(formatter),
storeOperationTimeDto.tueClose().format(formatter),
storeOperationTimeDto.wedOpen().format(formatter),
storeOperationTimeDto.wedClose().format(formatter),
storeOperationTimeDto.thuOpen().format(formatter),
storeOperationTimeDto.thuClose().format(formatter),
storeOperationTimeDto.friOpen().format(formatter),
storeOperationTimeDto.friClose().format(formatter),
storeOperationTimeDto.satOpen().format(formatter),
storeOperationTimeDto.satClose().format(formatter),
storeOperationTimeDto.sunOpen().format(formatter),
storeOperationTimeDto.sunClose().format(formatter),
storeDetailInfoDto.address(),
storeDetailInfoDto.phone()
);
}

public StoreListByPopularityRes getStoreByRank() {
final List<StoreByPopularityDto> storeByPopularityDtoList = storeFacade.findStoreListByLank();
final List<StoreByPopularityDto> storeByPopularityDtoList;
try {
storeByPopularityDtoList = storeFacade.findStoreListByLank();
} catch (NotFoundBaseException e) {
throw new StoreNotfoundException(StoreErrorCode.STORE_NOT_FOUND_ENTITY);
}
return new StoreListByPopularityRes(storeByPopularityDtoList);
}

public StoreSelectedCoordinateRes getStoreSelectedCoordinate(final long storeId) {
Store store = storeFacade.findStoreById(storeId);
final Store store;
try {
store = storeFacade.findStoreById(storeId);
} catch (NotFoundBaseException e) {
throw new StoreNotfoundException(StoreErrorCode.STORE_NOT_FOUND_ENTITY);
}
return StoreSelectedCoordinateRes.of(storeId, store.getLatitude(), store.getLongitude());
}

//선택된 스토어 조회
public StoreSelectedRes getStoreSelected(final long storeId, final Long userId) {
final StoreSelectedDto storeSelectedDto = storeFacade.findStoreInfoById(storeId, userId);

final StoreSelectedDto storeSelectedDto;
try {
storeSelectedDto = storeFacade.findStoreInfoById(storeId, userId);
} catch (NotFoundBaseException e) {
throw new StoreNotfoundException(StoreErrorCode.STORE_NOT_FOUND_ENTITY);
}
return StoreSelectedRes.of(
storeSelectedDto.storeId(),
storeSelectedDto.storeName(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
import com.cakey.rescode.SuccessCode;
import com.cakey.store.dto.StoreLikedCoordinateRes;
import com.cakey.storelikes.service.StoreLikesService;
import jakarta.validation.constraints.Min;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

@RestController
@Validated
@RequestMapping("/api/v1/store/likes")
@RequiredArgsConstructor
public class StoreLikesController {
Expand Down Expand Up @@ -44,7 +47,7 @@ public ResponseEntity<BaseResponse<?>> getPopularityStoreByUserLikes(
//찜한 스토어 좌표 조회
@GetMapping("/coordinate")
public ResponseEntity<BaseResponse<?>> getStoreLikedCoordinate(
@UserId final long userId
@UserId @Min(1) final long userId
) {
return ApiResponseUtil.success(
SuccessCode.OK,
Expand Down
Loading

0 comments on commit 8f609ee

Please sign in to comment.