From 658a43aea718f14a2a8aa445eaf92a2cea017f33 Mon Sep 17 00:00:00 2001 From: dldmsql Date: Sun, 8 Oct 2023 22:12:48 +0900 Subject: [PATCH 1/6] =?UTF-8?q?chore:=20#44=20API=20=EC=88=98=ED=96=89?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=ED=99=95=EC=9D=B8=EC=9D=84=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20AOP=20=EB=8F=84=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit service와 repository 내부 함수 실행 시점부터 종료시점까지의 소요 시간을 측정해서 콘솔창에 남깁니다. --- .../server/global/aop/log/LogAspect.java | 43 +++++++++ .../server/global/aop/log/LogTrace.java | 89 +++++++++++++++++++ .../server/global/aop/log/Pointcuts.java | 17 ++++ .../server/global/aop/log/TraceInfo.java | 3 + 4 files changed, 152 insertions(+) create mode 100644 src/main/java/everymeal/server/global/aop/log/LogAspect.java create mode 100644 src/main/java/everymeal/server/global/aop/log/LogTrace.java create mode 100644 src/main/java/everymeal/server/global/aop/log/Pointcuts.java create mode 100644 src/main/java/everymeal/server/global/aop/log/TraceInfo.java diff --git a/src/main/java/everymeal/server/global/aop/log/LogAspect.java b/src/main/java/everymeal/server/global/aop/log/LogAspect.java new file mode 100644 index 0000000..9407c38 --- /dev/null +++ b/src/main/java/everymeal/server/global/aop/log/LogAspect.java @@ -0,0 +1,43 @@ +package everymeal.server.global.aop.log; + + +import everymeal.server.global.exception.ApplicationException; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Aspect +@Component +public class LogAspect { + + private LogTrace logTrace; + + @Autowired + public LogAspect(LogTrace logTrace) { + this.logTrace = logTrace; + } + + @Around( + "everymeal.server.global.aop.log.Pointcuts.allQuery() || everymeal.server.global.aop.log.Pointcuts.allService()") + public Object executingTimeLog(ProceedingJoinPoint joinPoint) throws Throwable { + TraceInfo traceInfo = null; + try { + traceInfo = logTrace.start(joinPoint.getSignature().toShortString()); + Object result = joinPoint.proceed(); + logTrace.end(traceInfo); + return result; + } catch (ApplicationException e) { + if (traceInfo != null) { + logTrace.apiException(e, traceInfo); + } + throw e; + } catch (Exception e) { + if (traceInfo != null) { + logTrace.exception(e, traceInfo); + } + throw e; + } + } +} diff --git a/src/main/java/everymeal/server/global/aop/log/LogTrace.java b/src/main/java/everymeal/server/global/aop/log/LogTrace.java new file mode 100644 index 0000000..abcdf95 --- /dev/null +++ b/src/main/java/everymeal/server/global/aop/log/LogTrace.java @@ -0,0 +1,89 @@ +package everymeal.server.global.aop.log; + + +import everymeal.server.global.exception.ApplicationException; +import java.util.UUID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +@Component +public class LogTrace { + + private ThreadLocal threadId = ThreadLocal.withInitial(this::createThreadId); + + public TraceInfo start(String method) { + syncTrace(); + String id = threadId.get(); + long startTime = System.currentTimeMillis(); + logger().info("[" + id + "] " + method + " ==== start"); + return new TraceInfo(id, method, startTime); + } + + public void end(TraceInfo traceInfo) { + long endTime = System.currentTimeMillis(); + long resultTime = endTime - traceInfo.startTime(); + if (resultTime >= 1000) + logger().warn( + "[" + + traceInfo.theadId() + + "] " + + traceInfo.method() + + " ==== execute time = " + + resultTime + + "ms"); + else + logger().info( + "[" + + traceInfo.theadId() + + "] " + + traceInfo.method() + + " ==== execute time = " + + resultTime + + "ms"); + removeThreadLocal(); + } + + public void apiException(ApplicationException e, TraceInfo traceInfo) { + logger().error( + "[" + + traceInfo.theadId() + + "] " + + traceInfo.method() + + " ==== API EXCEPTION! [" + + e.getErrorCode() + + "] " + + e.getMessage()); + removeThreadLocal(); + } + + public void exception(Exception e, TraceInfo traceInfo) { + logger().error( + "[" + + traceInfo.theadId() + + "] " + + traceInfo.method() + + " ==== INTERNAL ERROR! " + + e.getMessage()); + removeThreadLocal(); + } + + private void syncTrace() { + String id = threadId.get(); + if (id == null) { + threadId.set(createThreadId()); + } + } + + private String createThreadId() { + return UUID.randomUUID().toString().substring(0, 8); + } + + private void removeThreadLocal() { + threadId.remove(); + } + + private Logger logger() { + return LoggerFactory.getLogger(LogTrace.class); + } +} diff --git a/src/main/java/everymeal/server/global/aop/log/Pointcuts.java b/src/main/java/everymeal/server/global/aop/log/Pointcuts.java new file mode 100644 index 0000000..48f378c --- /dev/null +++ b/src/main/java/everymeal/server/global/aop/log/Pointcuts.java @@ -0,0 +1,17 @@ +package everymeal.server.global.aop.log; + + +import org.aspectj.lang.annotation.Pointcut; + +public class Pointcuts { + + @Pointcut("execution(* everymeal.server.meal.*.*(..))") + public void all() {} + + @Pointcut("execution(* everymeal.server..*Service.*(..))") + public void allService() {} + + @Pointcut( + "execution(* everymeal.server..*Repository.*(..)) || execution(* everymeal.server..*RepositoryImpl.*(..))") + public void allQuery() {} +} diff --git a/src/main/java/everymeal/server/global/aop/log/TraceInfo.java b/src/main/java/everymeal/server/global/aop/log/TraceInfo.java new file mode 100644 index 0000000..87a4f3e --- /dev/null +++ b/src/main/java/everymeal/server/global/aop/log/TraceInfo.java @@ -0,0 +1,3 @@ +package everymeal.server.global.aop.log; + +public record TraceInfo(String theadId, String method, Long startTime) {} From 2b933edc89799d9c3a19c4b7d361f4606e0442cd Mon Sep 17 00:00:00 2001 From: dldmsql Date: Sun, 8 Oct 2023 22:15:28 +0900 Subject: [PATCH 2/6] =?UTF-8?q?fix:=20#44=20=EC=8B=9D=EC=82=AC=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20API=20DTO=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20?= =?UTF-8?q?=EC=84=B1=EB=8A=A5=20=EA=B3=A0=EB=8F=84=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 피그마 상에 노출되어야 하는 데이터에 맞추어 DTO를 수정했습니다. - 등록 시, 동일한 제공일자와 식사구분 데이테를 갖고 있는지 검증 로직으로 변경했습니다. - 학식 하루/주간 조회 성능 고도화를 위해 비동기 처리/인덱스/Instant 타입 변경을 수행하였습니다. --- .../global/exception/ExceptionList.java | 2 +- .../dto/request/MealRegisterReq.java | 23 +- .../dto/response/DayMealListGetRes.java | 25 +- .../dto/response/WeekMealListGetRes.java | 30 +- .../everymeal/server/meal/entity/Meal.java | 17 +- .../server/meal/entity/MealCategory.java | 21 ++ .../server/meal/entity/MealStatus.java | 6 +- .../server/meal/entity/MealType.java | 12 +- .../meal/repository/MealRepositoryCustom.java | 9 +- .../meal/repository/MealRepositoryImpl.java | 63 ++-- .../server/meal/service/MealServiceImpl.java | 288 +++++++++++++++--- .../meal/controller/MealControllerTest.java | 8 +- .../meal/service/MealServiceImplTest.java | 68 +++-- 13 files changed, 428 insertions(+), 144 deletions(-) create mode 100644 src/main/java/everymeal/server/meal/entity/MealCategory.java diff --git a/src/main/java/everymeal/server/global/exception/ExceptionList.java b/src/main/java/everymeal/server/global/exception/ExceptionList.java index 86e5f18..2d6abb8 100644 --- a/src/main/java/everymeal/server/global/exception/ExceptionList.java +++ b/src/main/java/everymeal/server/global/exception/ExceptionList.java @@ -12,7 +12,7 @@ public enum ExceptionList { RESTAURANT_NOT_FOUND("M0002", HttpStatus.NOT_FOUND, "등록된 식당이 아닙니다."), UNIVERSITY_NOT_FOUND("M0003", HttpStatus.NOT_FOUND, "등록된 학교가 아닙니다."), INVALID_MEAL_OFFEREDAT_REQUEST( - "M0004", HttpStatus.BAD_REQUEST, "등록되어 있는 식단 데이터 보다 과거의 날짜로 등록할 수 없습니다."), + "M0004", HttpStatus.BAD_REQUEST, "동일한 데이터를 갖는 식단 데이터가 이미 존재합니다."), INVALID_REQUEST("R0001", HttpStatus.BAD_REQUEST, "Request의 Data Type이 올바르지 않습니다."), USER_NOT_FOUND("U0001", HttpStatus.NOT_FOUND, "등록된 유저가 아닙니다."), diff --git a/src/main/java/everymeal/server/meal/controller/dto/request/MealRegisterReq.java b/src/main/java/everymeal/server/meal/controller/dto/request/MealRegisterReq.java index 58f06e6..d655a53 100644 --- a/src/main/java/everymeal/server/meal/controller/dto/request/MealRegisterReq.java +++ b/src/main/java/everymeal/server/meal/controller/dto/request/MealRegisterReq.java @@ -2,17 +2,28 @@ import io.swagger.v3.oas.annotations.media.Schema; -import java.time.LocalDate; +import jakarta.validation.constraints.NotBlank; +import java.time.Instant; public record MealRegisterReq( @Schema( description = "메뉴를 ',' 구분자를 기준으로 묶어서 하나의 문자열로 보내주세요.", defaultValue = "갈비탕, 깍두기, 흰쌀밥") + @NotBlank String menu, - @Schema(description = "식사 분류 ( 조식 | 중식 | 석식 | 특식 ) ENUM으로 관리합니다.", defaultValue = "LUNCH") + @Schema( + description = "식사 분류 ( BREAKFAST | LUNCH | DINNER ) ENUM으로 관리합니다.", + defaultValue = "LUNCH") + @NotBlank String mealType, - @Schema(description = "식사 운영 상태 ( 운영 | 미운영 | 단축운영 )", defaultValue = "OPEN") + @Schema(description = "식사 운영 상태 ( OPEN | CLOSED | SHORT_OPEN )", defaultValue = "OPEN") String mealStatus, - @Schema(description = "식사 제공 날짜 ( yyyy-MM-dd ) ", defaultValue = "2023-10-01") - LocalDate offeredAt, - @Schema(description = "가격을 Double 형태로 관리합니다.", defaultValue = "10000.0") Double price) {} + @Schema(description = "식사 제공 날짜 ( yyyy-MM-dd ) ", defaultValue = "2023-10-01") @NotBlank + Instant offeredAt, + @Schema(description = "가격을 Double 형태로 관리합니다.", defaultValue = "0.0") Double price, + @Schema( + description = + "음식의 카테고리 ( DEFAULT | KOREAN | JAPANESE | CHINESE | SNACKBAR | WESTERN ) 중 단일 메뉴라면 DEFAULT를 입력해주세요.", + defaultValue = "DEFAULT") + @NotBlank + String category) {} diff --git a/src/main/java/everymeal/server/meal/controller/dto/response/DayMealListGetRes.java b/src/main/java/everymeal/server/meal/controller/dto/response/DayMealListGetRes.java index 1038239..52fb419 100644 --- a/src/main/java/everymeal/server/meal/controller/dto/response/DayMealListGetRes.java +++ b/src/main/java/everymeal/server/meal/controller/dto/response/DayMealListGetRes.java @@ -3,27 +3,32 @@ import com.fasterxml.jackson.annotation.JsonFormat; import everymeal.server.meal.entity.Meal; -import everymeal.server.meal.entity.MealStatus; -import everymeal.server.meal.entity.MealType; -import java.time.LocalDate; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.List; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; +@NoArgsConstructor +@AllArgsConstructor @Getter @Setter @Builder public class DayMealListGetRes { private String menu; - private MealType mealType; - private MealStatus mealStatus; + private String mealType; + private String mealStatus; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul") - private LocalDate offeredAt; + private Instant offeredAt; private Double price; + private String category; + private String restaurantName; public static List of(List mealList) { return mealList.stream() @@ -31,10 +36,12 @@ public static List of(List mealList) { meal -> DayMealListGetRes.builder() .menu(meal.getMenu()) - .mealType(meal.getMealType()) - .mealStatus(meal.getMealStatus()) - .offeredAt(meal.getOfferedAt()) + .mealType(meal.getMealType().getValue()) + .mealStatus(meal.getMealStatus().getValue()) + .offeredAt(meal.getOfferedAt().truncatedTo(ChronoUnit.DAYS)) .price(meal.getPrice()) + .category(meal.getCategory().name()) + .restaurantName(meal.getRestaurant().getName()) .build()) .toList(); } diff --git a/src/main/java/everymeal/server/meal/controller/dto/response/WeekMealListGetRes.java b/src/main/java/everymeal/server/meal/controller/dto/response/WeekMealListGetRes.java index 3ec0a37..351d561 100644 --- a/src/main/java/everymeal/server/meal/controller/dto/response/WeekMealListGetRes.java +++ b/src/main/java/everymeal/server/meal/controller/dto/response/WeekMealListGetRes.java @@ -2,9 +2,7 @@ import com.fasterxml.jackson.annotation.JsonFormat; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Comparator; +import java.time.Instant; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -17,26 +15,26 @@ @Builder public class WeekMealListGetRes { @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul") - private LocalDate offeredAt; + private Instant offeredAt; private List dayMealListGetResList; public static List of(List mealListGetRes) { // offeredAt을 기준으로 DayMealList 그룹핑 - Map> groupedData = + Map> groupedData = mealListGetRes.stream() .collect(Collectors.groupingBy(DayMealListGetRes::getOfferedAt)); - List weekMealList = new ArrayList<>(); - for (Map.Entry> entry : groupedData.entrySet()) { - WeekMealListGetRes weekMeal = - WeekMealListGetRes.builder() - .offeredAt(entry.getKey()) - .dayMealListGetResList(entry.getValue()) - .build(); - weekMealList.add(weekMeal); - } - // offeredAt을 기준으로 오름차순 정렬 - weekMealList.sort(Comparator.comparing(WeekMealListGetRes::getOfferedAt)); + List weekMealList = + groupedData.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .map( + entry -> + WeekMealListGetRes.builder() + .offeredAt(entry.getKey()) + .dayMealListGetResList(entry.getValue()) + .build()) + .collect(Collectors.toList()); + return weekMealList; } } diff --git a/src/main/java/everymeal/server/meal/entity/Meal.java b/src/main/java/everymeal/server/meal/entity/Meal.java index a0802d7..3d4b0ef 100644 --- a/src/main/java/everymeal/server/meal/entity/Meal.java +++ b/src/main/java/everymeal/server/meal/entity/Meal.java @@ -7,15 +7,18 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.Index; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; -import java.time.LocalDate; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import java.time.Instant; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @Getter -@Table +@Table(indexes = {@Index(name = "idx__mealType__offeredAt", columnList = "mealType, offeredAt")}) @Entity @NoArgsConstructor(access = lombok.AccessLevel.PROTECTED) public class Meal { @@ -32,10 +35,14 @@ public class Meal { @Enumerated(EnumType.STRING) private MealStatus mealStatus; - private LocalDate offeredAt; + @Temporal(TemporalType.TIMESTAMP) + private Instant offeredAt; private Double price; + @Enumerated(EnumType.STRING) + private MealCategory category; + @ManyToOne private Restaurant restaurant; @Builder @@ -43,14 +50,16 @@ public Meal( String menu, MealType mealType, MealStatus mealStatus, - LocalDate offeredAt, + Instant offeredAt, Double price, + MealCategory category, Restaurant restaurant) { this.menu = menu; this.mealType = mealType; this.mealStatus = mealStatus; this.offeredAt = offeredAt; this.price = price; + this.category = category; this.restaurant = restaurant; } } diff --git a/src/main/java/everymeal/server/meal/entity/MealCategory.java b/src/main/java/everymeal/server/meal/entity/MealCategory.java new file mode 100644 index 0000000..9324b6a --- /dev/null +++ b/src/main/java/everymeal/server/meal/entity/MealCategory.java @@ -0,0 +1,21 @@ +package everymeal.server.meal.entity; + + +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public enum MealCategory { + DEFAULT("단일메뉴"), + KOREAN("한식"), + JAPANESE("일식"), + CHINESE("중식"), + WESTERN("양식"), + SNACKBAR("분식"); + private String value; + + MealCategory(String value) { + this.value = value; + } +} diff --git a/src/main/java/everymeal/server/meal/entity/MealStatus.java b/src/main/java/everymeal/server/meal/entity/MealStatus.java index 3c5bf45..7c4754f 100644 --- a/src/main/java/everymeal/server/meal/entity/MealStatus.java +++ b/src/main/java/everymeal/server/meal/entity/MealStatus.java @@ -12,9 +12,9 @@ public enum MealStatus { SHORT_OPEN("단축운영"), ; - private String name; + private String value; - MealStatus(String name) { - this.name = name; + MealStatus(String value) { + this.value = value; } } diff --git a/src/main/java/everymeal/server/meal/entity/MealType.java b/src/main/java/everymeal/server/meal/entity/MealType.java index cef97d5..1383972 100644 --- a/src/main/java/everymeal/server/meal/entity/MealType.java +++ b/src/main/java/everymeal/server/meal/entity/MealType.java @@ -7,15 +7,15 @@ @Getter @RequiredArgsConstructor public enum MealType { - BREAKFAST("조식"), - LUNCH("중식"), - DINNER("석식"), + BREAKFAST("아침"), + LUNCH("점심"), + DINNER("저녁"), SPECIAL("특식"), ; - private String name; + private String value; - MealType(String name) { - this.name = name; + MealType(String value) { + this.value = value; } } diff --git a/src/main/java/everymeal/server/meal/repository/MealRepositoryCustom.java b/src/main/java/everymeal/server/meal/repository/MealRepositoryCustom.java index cddef4e..e5630db 100644 --- a/src/main/java/everymeal/server/meal/repository/MealRepositoryCustom.java +++ b/src/main/java/everymeal/server/meal/repository/MealRepositoryCustom.java @@ -3,7 +3,8 @@ import everymeal.server.meal.controller.dto.request.MealRegisterReq; import everymeal.server.meal.entity.Meal; -import java.time.LocalDate; +import everymeal.server.meal.entity.MealType; +import java.time.Instant; import java.util.List; import org.springframework.stereotype.Repository; @@ -11,8 +12,6 @@ public interface MealRepositoryCustom { List findAllByAfterOfferedAt(MealRegisterReq mealRegisterReq, Long restaurantIdx); - List findAllByOfferedAt(LocalDate offeredAt, Long restaurantIdx); - - List findAllByBetweenOfferedAtAndEndedAt( - LocalDate startedAt, LocalDate endedAt, Long restaurantIdx); + List findAllByOfferedAtOnDateAndMealType( + Instant offeredAt, MealType mealType, Long restaurantIdx); } diff --git a/src/main/java/everymeal/server/meal/repository/MealRepositoryImpl.java b/src/main/java/everymeal/server/meal/repository/MealRepositoryImpl.java index 7f87eae..bc89051 100644 --- a/src/main/java/everymeal/server/meal/repository/MealRepositoryImpl.java +++ b/src/main/java/everymeal/server/meal/repository/MealRepositoryImpl.java @@ -5,16 +5,28 @@ import com.querydsl.jpa.impl.JPAQueryFactory; import everymeal.server.meal.controller.dto.request.MealRegisterReq; import everymeal.server.meal.entity.Meal; +import everymeal.server.meal.entity.MealCategory; +import everymeal.server.meal.entity.MealType; import everymeal.server.meal.entity.QMeal; import everymeal.server.meal.entity.QRestaurant; -import java.time.LocalDate; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.List; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor public class MealRepositoryImpl implements MealRepositoryCustom { private final JPAQueryFactory jpaQueryFactory; - + /** + * ============================================================================================ + * 현재 사용하지 않는 함수입니다. -- 추후 사용 가능성을 고려해서 유지하고 있습니다.
+ * 요청 DTO 내의 제공일자 이후의 데이터가 존재하는 지를 확인합니다. + * + * @param restaurantIdx 식당 아이디 + * @param mealRegisterReq 요청 dto + * @return List + * ========================================================================================= + */ @Override public List findAllByAfterOfferedAt(MealRegisterReq mealRegisterReq, Long restaurantIdx) { var queryResult = @@ -27,44 +39,47 @@ public List findAllByAfterOfferedAt(MealRegisterReq mealRegisterReq, Long .fetch(); return queryResult; } - + /** + * ============================================================================================ + * 일별 식사구분에 따른 식사 데이터 조회
+ * + * @param restaurantIdx 식당 아이디 + * @param offeredAt 제공일자 + * @param mealType 식사구분 ( 아침/점심/저녁 ) + * @return List + * ========================================================================================= + */ @Override - public List findAllByOfferedAt(LocalDate offeredAt, Long restaurantIdx) { + public List findAllByOfferedAtOnDateAndMealType( + Instant offeredAt, MealType mealType, Long restaurantIdx) { var queryResult = jpaQueryFactory .selectFrom(QMeal.meal) .leftJoin(QMeal.meal.restaurant, QRestaurant.restaurant) .on(QRestaurant.restaurant.idx.eq(restaurantIdx)) - .where(isEqOfferedAt(offeredAt)) + .where(isEqOfferedAt(offeredAt), isEqMealType(mealType)) .fetch(); return queryResult; } - @Override - public List findAllByBetweenOfferedAtAndEndedAt( - LocalDate startedAt, LocalDate endedAt, Long restaurantIdx) { - var queryResult = - jpaQueryFactory - .selectFrom(QMeal.meal) - .leftJoin(QMeal.meal.restaurant, QRestaurant.restaurant) - .on(QRestaurant.restaurant.idx.eq(restaurantIdx)) - .where(isBetweenOfferedAt(startedAt, endedAt)) - .orderBy(QMeal.meal.offeredAt.desc(), QMeal.meal.mealType.desc()) - .fetch(); - return queryResult; + /** 단일 메뉴, 복합 메뉴를 판별 */ + private BooleanExpression isSingleMenu(boolean isSingle) { + return isSingle ? QMeal.meal.category.eq(MealCategory.DEFAULT) : null; } - private BooleanExpression isBetweenOfferedAt(LocalDate startedAt, LocalDate endedAt) { - return QMeal.meal.offeredAt.between(startedAt, endedAt); + /** offeredAt 과 동일한 경우 */ + private BooleanExpression isEqOfferedAt(Instant offeredAt) { + Instant startOfDay = offeredAt.truncatedTo(ChronoUnit.DAYS); + Instant endOfDay = startOfDay.plus(1, ChronoUnit.DAYS).minus(1, ChronoUnit.MILLIS); + return QMeal.meal.offeredAt.between(startOfDay, endOfDay); } - - /** offeredAt 과 동일 한 경우 */ - private BooleanExpression isEqOfferedAt(LocalDate offeredAt) { - return QMeal.meal.offeredAt.eq(offeredAt); + /** mealType 과 동일한 경우 */ + private BooleanExpression isEqMealType(MealType mealType) { + return QMeal.meal.mealType.eq(mealType); } /** offeredAt 이후인 경우 */ - private BooleanExpression isAfterOfferedAt(LocalDate offeredAt) { + private BooleanExpression isAfterOfferedAt(Instant offeredAt) { return QMeal.meal.offeredAt.after(offeredAt); } } diff --git a/src/main/java/everymeal/server/meal/service/MealServiceImpl.java b/src/main/java/everymeal/server/meal/service/MealServiceImpl.java index c5d9d36..1dcf7cf 100644 --- a/src/main/java/everymeal/server/meal/service/MealServiceImpl.java +++ b/src/main/java/everymeal/server/meal/service/MealServiceImpl.java @@ -10,6 +10,7 @@ import everymeal.server.meal.controller.dto.response.RestaurantListGetRes; import everymeal.server.meal.controller.dto.response.WeekMealListGetRes; import everymeal.server.meal.entity.Meal; +import everymeal.server.meal.entity.MealCategory; import everymeal.server.meal.entity.MealStatus; import everymeal.server.meal.entity.MealType; import everymeal.server.meal.entity.Restaurant; @@ -18,11 +19,14 @@ import everymeal.server.meal.repository.RestaurantRepository; import everymeal.server.university.entity.University; import everymeal.server.university.repository.UniversityRepository; -import java.time.LocalDate; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.concurrent.CompletableFuture; import lombok.RequiredArgsConstructor; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -35,14 +39,30 @@ public class MealServiceImpl implements MealService { private final MealRepositoryCustom mealRepositoryCustom; private final UniversityRepository universityRepository; private final RestaurantRepository restaurantRepository; + /** + * ============================================================================================ + * GLOBAL STATIC CONSTANTS + * ============================================================================================= + */ + private final String UN_REGISTERED_MEAL = "등록된 식단이 없습니다."; + private final String TIME_PARSING_INFO = "T00:00:00Z"; + + /** + * ============================================================================================ + * 학생 식당 등록
+ * 관리자에 의한 학교 등록이 선행되어야 합니다. University_name, University_campusName 을 구분자로 학교를 식별합니다. + * + * @param restaurantRegisterReq 식당 등록 요청 DTO + * @return true + *

등록된 학교가 없는 경우, + * @throws ApplicationException 404 등록된 학교가 아닙니다.
+ * ========================================================================================= + */ @Override @Transactional public Boolean createRestaurant(RestaurantRegisterReq restaurantRegisterReq) { - /** - * 시나리오 1. 관리자에 의한 학교 등록 ( University ) - 학교 구분키 name & campusName 2. 관리자에 의한 학교별 학생식당 등록 ( - * Restaurant ) - */ + // 학교 조회 University university = universityRepository .findByNameAndCampusNameAndIsDeletedFalse( @@ -52,6 +72,7 @@ public Boolean createRestaurant(RestaurantRegisterReq restaurantRegisterReq) { .findFirst() .orElseThrow( () -> new ApplicationException(ExceptionList.UNIVERSITY_NOT_FOUND)); + // 식당 등록 Restaurant restaurant = Restaurant.builder() .name(restaurantRegisterReq.restaurantName()) @@ -61,6 +82,18 @@ public Boolean createRestaurant(RestaurantRegisterReq restaurantRegisterReq) { return restaurantRepository.save(restaurant).getIdx() != null; } + /** + * ============================================================================================ + * 학식 식단 등록 ( 주간, 하루 모두 등록 가능 ) + * + * @param weekMealRegisterReq 식단 등록 요청 DTO + * @return true + *

식당이 없는 경우, + * @throws ApplicationException 404 존재하지 않는 식당입니다.
+ * REQ 데이터 중 offeredAt, Restaurant, MealType 이 동일한 데이터가 존재한다면, + * @throws ApplicationException 400 등록되어 있는 식단 데이터 보다 과거의 날짜로 등록할 수 없습니다.
+ * ========================================================================================= + */ @Override @Transactional public Boolean createWeekMeal(WeekMealRegisterReq weekMealRegisterReq) { @@ -74,34 +107,49 @@ public Boolean createWeekMeal(WeekMealRegisterReq weekMealRegisterReq) { weekMealRegisterReq .registerReqList() .sort(Comparator.comparing(MealRegisterReq::offeredAt)); - /** - * 조건) 가장 늦은 offeredAt를 기준으로 날짜가 이후인 경우에만 추가할 수 있어야 한다. ( 데이터 간 충돌 방지를 위해서 ) 가정) REQ로 들어온 - * offeredAt(식사제공날짜)가 이미 테이블 내에 포함되어 있다. 행동) REQ 중 가장 빠른 offeredAt을 기준으로 테이블 내에 데이터가 존재하는지 - * 조회 list.size() > 0 존재한다면, 오류 처리 존재하지 않는다면, 테이블에 삽입 - */ - List meals = - mealRepositoryCustom.findAllByAfterOfferedAt( - weekMealRegisterReq.registerReqList().get(0), restaurant.getIdx()); - if (meals.size() > 0) - throw new ApplicationException(ExceptionList.INVALID_MEAL_OFFEREDAT_REQUEST); - // 주간 단위 식단 생성 + // 식단 등록 List mealList = new ArrayList<>(); for (MealRegisterReq req : weekMealRegisterReq.registerReqList()) { - Meal meal = - Meal.builder() - .mealStatus(MealStatus.valueOf(req.mealStatus())) - .mealType(MealType.valueOf(req.mealType())) - .menu(req.menu()) - .restaurant(restaurant) - .price(req.price()) - .offeredAt(req.offeredAt()) - .build(); - mealList.add(meal); + // 제공날짜, 학생식당, 식사분류가 동일한 데이터가 이미 존재하면, 덮어쓰기 불가능 오류 + if (!mealRepositoryCustom + .findAllByOfferedAtOnDateAndMealType( + req.offeredAt(), MealType.valueOf(req.mealType()), restaurant.getIdx()) + .isEmpty()) { + throw new ApplicationException(ExceptionList.INVALID_MEAL_OFFEREDAT_REQUEST); + } else { + Instant iOfferedAt = Instant.from(req.offeredAt()); + MealStatus mealStatus = + req.mealStatus() == null + ? MealStatus.OPEN + : MealStatus.valueOf(req.mealStatus()); + Double price = req.price() == null ? 0.0 : req.price(); + Meal meal = + Meal.builder() + .mealStatus(mealStatus) + .mealType(MealType.valueOf(req.mealType())) + .menu(req.menu()) + .restaurant(restaurant) + .price(price) + .offeredAt(iOfferedAt) + .category(MealCategory.valueOf(req.category())) + .build(); + mealList.add(meal); + } } mealRepository.saveAll(mealList); return true; } - + /** + * ============================================================================================ + * 학생식당 리스트 조회 + * + * @param universityName 학교 한글명 + * @param campusName 캠퍼스 이름 + * @return List + *

학교가 없는 경우, + * @throws ApplicationException 404 존재하지 않는 학교입니다.
+ * ========================================================================================= + */ @Override public List getRestaurantList(String universityName, String campusName) { // 학교 등록 여부 판단 @@ -117,9 +165,21 @@ public List getRestaurantList(String universityName, Strin restaurantRepository.findAllByUniversityAndUseYnTrue(university); return RestaurantListGetRes.of(restaurants); } - + /** + * ============================================================================================ + * 학식 식단 Day 조회
+ * 등록되지 않은 식단 데이터는 아침/점심/저녁 포맷팅에 맞게 응답 데이터를 생성해서 반환합니다. + * + * @param restaurantIdx 식당 아이디 + * @param offeredAt 제공 일자 --- yyyy-MM-dd + * @return List + *

식당이 없는 경우, + * @throws ApplicationException 404 존재하지 않는 식당입니다.
+ * ========================================================================================= + */ @Override public List getDayMealList(Long restaurantIdx, String offeredAt) { + Instant ldOfferedAt = Instant.parse(offeredAt + TIME_PARSING_INFO); // 학생 식당 등록 여부 판단 Restaurant restaurant = restaurantRepository @@ -127,12 +187,34 @@ public List getDayMealList(Long restaurantIdx, String offered .orElseThrow( () -> new ApplicationException(ExceptionList.RESTAURANT_NOT_FOUND)); // REQ offeredAt에 해당하는 식단 조회 - List meals = - mealRepositoryCustom.findAllByOfferedAt( - LocalDate.parse(offeredAt), restaurant.getIdx()); - return DayMealListGetRes.of(meals); + List breakfastMeals = + mealRepositoryCustom.findAllByOfferedAtOnDateAndMealType( + ldOfferedAt, MealType.BREAKFAST, restaurant.getIdx()); + List lunchMeals = + mealRepositoryCustom.findAllByOfferedAtOnDateAndMealType( + ldOfferedAt, MealType.LUNCH, restaurant.getIdx()); + List dinnerMeals = + mealRepositoryCustom.findAllByOfferedAtOnDateAndMealType( + ldOfferedAt, MealType.DINNER, restaurant.getIdx()); + // 등록되지 않은 식단 처리 + List res = + chkEmptyDayMeal( + ldOfferedAt, restaurant.getName(), breakfastMeals, lunchMeals, dinnerMeals); + return res; } + /** + * ============================================================================================ + * 학식 식단 Week 조회
+ * 등록되지 않은 식단 데이터는 아침/점심/저녁 포맷팅에 맞게 응답 데이터를 생성해서 반환합니다. ( 7일 데이터 ) + * + * @param restaurantIdx 식당 아이디 + * @param offeredAt 제공일자 --- yyyy-MM-dd + * @return List + *

식당이 없는 경우, + * @throws ApplicationException 404 존재하지 않는 식당입니다.
+ * ========================================================================================= + */ @Override public List getWeekMealList(Long restaurantIdx, String offeredAt) { // 학생 식당 등록 여부 판단 @@ -141,13 +223,141 @@ public List getWeekMealList(Long restaurantIdx, String offer .findById(restaurantIdx) .orElseThrow( () -> new ApplicationException(ExceptionList.RESTAURANT_NOT_FOUND)); - // REQ offeredAt에 해당하는 식단 조회 - LocalDate startedAt = LocalDate.parse(offeredAt); - LocalDate endedAt = startedAt.plusDays(7); + // REQ offeredAt을 시작 일자로 주간 단위 식단 조회 + Instant startedAt = Instant.parse(offeredAt + TIME_PARSING_INFO); + Instant endedAt = startedAt.plus(6, ChronoUnit.DAYS); + List dateList = new ArrayList<>(); + Instant current = startedAt; + while (!current.isAfter(endedAt)) { + dateList.add(current); + current = current.plus(1, ChronoUnit.DAYS); + } + // 비동기로 아침/점심/저녁 조회 쿼리 수행 + List>> mealFutures = + dateList.stream() + .map( + ldOfferedAt -> { + CompletableFuture> breakfastFuture = + getMealsByDateAndTypeAsync( + ldOfferedAt.truncatedTo(ChronoUnit.DAYS), + MealType.BREAKFAST, + restaurant.getIdx()); + CompletableFuture> lunchFuture = + getMealsByDateAndTypeAsync( + ldOfferedAt.truncatedTo(ChronoUnit.DAYS), + MealType.LUNCH, + restaurant.getIdx()); + CompletableFuture> dinnerFuture = + getMealsByDateAndTypeAsync( + ldOfferedAt.truncatedTo(ChronoUnit.DAYS), + MealType.DINNER, + restaurant.getIdx()); + + return CompletableFuture.allOf( + breakfastFuture, lunchFuture, dinnerFuture) + .thenApply( + ignored -> { + List breakfastMeals = + breakfastFuture.join(); + List lunchMeals = lunchFuture.join(); + List dinnerMeals = + dinnerFuture.join(); + + // 등록되지 않은 식단 처리 + return chkEmptyDayMeal( + ldOfferedAt.truncatedTo( + ChronoUnit.DAYS), + restaurant.getName(), + breakfastMeals, + lunchMeals, + dinnerMeals); + }); + }) + .toList(); + // CompletableFuture를 모두 조합하고 결과를 가져옴 + List res = + mealFutures.stream().map(CompletableFuture::join).flatMap(List::stream).toList(); + return WeekMealListGetRes.of(res); + } + /** + * ============================================================================================ + * 비동기 식사 조회
+ * 일별 아침/점심/저녁에 따른 식사 조회 쿼리를 수행합니다. + * + * @param restaurantIdx 식당 아이디 + * @param date 제공일자 --- yyyy-MM-dd + * @param mealType 식사 구분 ( 아침/점심/저녁 ) + * @return CompletableFuture> + * ========================================================================================= + */ + @Async + public CompletableFuture> getMealsByDateAndTypeAsync( + Instant date, MealType mealType, Long restaurantIdx) { + // 비동기로 아침/점심/저녁 조회 쿼리 수행 List meals = - mealRepositoryCustom.findAllByBetweenOfferedAtAndEndedAt( - startedAt, endedAt, restaurant.getIdx()); - List dayMealListGetResList = DayMealListGetRes.of(meals); - return WeekMealListGetRes.of(dayMealListGetResList); + mealRepositoryCustom.findAllByOfferedAtOnDateAndMealType( + date, mealType, restaurantIdx); + return CompletableFuture.completedFuture(meals); + } + /** + * ============================================================================================ + * 등록되지 않은 식단 존재 여부 확인
+ * 등록되지 않은 식단이 존재하는 경우, 응답을 위한 더미 데이터를 생성합니다. + * + * @param offeredAt 제공일자 + * @param restaurantName 식당명 + * @param breakfastMeals 아침 식사 + * @param lunchMeals 점심 식사 + * @param dinnerMeals 저녁 식사 + * @return List + * ========================================================================================= + */ + private List chkEmptyDayMeal( + Instant offeredAt, + String restaurantName, + List breakfastMeals, + List lunchMeals, + List dinnerMeals) { + List res = new ArrayList<>(); + if (breakfastMeals.isEmpty()) { + res.add( + new DayMealListGetRes( + UN_REGISTERED_MEAL, + MealType.BREAKFAST.getValue(), + MealStatus.OPEN.getValue(), + offeredAt, + 0.0, + MealCategory.DEFAULT.getValue(), + restaurantName)); + } else { + res.addAll(DayMealListGetRes.of(breakfastMeals)); + } + if (lunchMeals.isEmpty()) { + res.add( + new DayMealListGetRes( + UN_REGISTERED_MEAL, + MealType.LUNCH.getValue(), + MealStatus.OPEN.getValue(), + offeredAt, + 0.0, + MealCategory.DEFAULT.getValue(), + restaurantName)); + } else { + res.addAll(DayMealListGetRes.of(lunchMeals)); + } + if (dinnerMeals.isEmpty()) { + res.add( + new DayMealListGetRes( + UN_REGISTERED_MEAL, + MealType.DINNER.getValue(), + MealStatus.OPEN.getValue(), + offeredAt, + 0.0, + MealCategory.DEFAULT.getValue(), + restaurantName)); + } else { + res.addAll(DayMealListGetRes.of(dinnerMeals)); + } + return res; } } diff --git a/src/test/java/everymeal/server/meal/controller/MealControllerTest.java b/src/test/java/everymeal/server/meal/controller/MealControllerTest.java index f3e4a8f..2b02238 100644 --- a/src/test/java/everymeal/server/meal/controller/MealControllerTest.java +++ b/src/test/java/everymeal/server/meal/controller/MealControllerTest.java @@ -9,9 +9,10 @@ import everymeal.server.meal.controller.dto.request.MealRegisterReq; import everymeal.server.meal.controller.dto.request.RestaurantRegisterReq; import everymeal.server.meal.controller.dto.request.WeekMealRegisterReq; +import everymeal.server.meal.entity.MealCategory; import everymeal.server.meal.entity.MealStatus; import everymeal.server.meal.entity.MealType; -import java.time.LocalDate; +import java.time.Instant; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.DisplayName; @@ -32,8 +33,9 @@ void createWeekMeal() throws Exception { "갈비탕, 깍두기, 흰쌀밥", MealType.BREAKFAST.name(), MealStatus.OPEN.name(), - LocalDate.now(), - 10000.0); + Instant.now(), + 10000.0, + MealCategory.DEFAULT.name()); list.add(mealReq); } WeekMealRegisterReq req = new WeekMealRegisterReq(list, 1L); diff --git a/src/test/java/everymeal/server/meal/service/MealServiceImplTest.java b/src/test/java/everymeal/server/meal/service/MealServiceImplTest.java index 58d84cc..c1a2f8f 100644 --- a/src/test/java/everymeal/server/meal/service/MealServiceImplTest.java +++ b/src/test/java/everymeal/server/meal/service/MealServiceImplTest.java @@ -14,6 +14,7 @@ import everymeal.server.meal.controller.dto.response.RestaurantListGetRes; import everymeal.server.meal.controller.dto.response.WeekMealListGetRes; import everymeal.server.meal.entity.Meal; +import everymeal.server.meal.entity.MealCategory; import everymeal.server.meal.entity.MealStatus; import everymeal.server.meal.entity.MealType; import everymeal.server.meal.entity.Restaurant; @@ -22,7 +23,9 @@ import everymeal.server.meal.repository.RestaurantRepository; import everymeal.server.university.entity.University; import everymeal.server.university.repository.UniversityRepository; +import java.time.Instant; import java.time.LocalDate; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.AfterEach; @@ -87,15 +90,16 @@ void createWeekMeal() throws Exception { restaurantRegisterReq.restaurantName())); List list = new ArrayList<>(); - LocalDate today = LocalDate.now(); + Instant today = Instant.now(); for (int i = 0; i < 7; i++) { MealRegisterReq mealReq = new MealRegisterReq( "갈비탕, 깍두기, 흰쌀밥", MealType.BREAKFAST.name(), MealStatus.OPEN.name(), - today.plusDays(i), - 10000.0); + today.plus(i, ChronoUnit.DAYS), + 10000.0, + MealCategory.DEFAULT.name()); list.add(mealReq); } WeekMealRegisterReq req = new WeekMealRegisterReq(list, restaurant.getIdx()); @@ -125,23 +129,26 @@ void getWeekMealList() throws Exception { restaurantRegisterReq.restaurantName())); List list = new ArrayList<>(); - LocalDate today = LocalDate.now(); + Instant today = Instant.now(); for (int i = 0; i < 7; i++) { + Instant offeredAt = today.plus(i, ChronoUnit.DAYS); MealRegisterReq mealReq = new MealRegisterReq( "갈비탕, 깍두기, 흰쌀밥", MealType.BREAKFAST.name(), MealStatus.OPEN.name(), - today.plusDays(i), - 10000.0); + offeredAt, + 10000.0, + MealCategory.DEFAULT.name()); list.add(mealReq); } WeekMealRegisterReq req = new WeekMealRegisterReq(list, restaurant.getIdx()); mealService.createWeekMeal(req); // when + String offeredAt = today.toString().split("T")[0]; List response = - mealService.getWeekMealList(restaurant.getIdx(), today.toString()); + mealService.getWeekMealList(restaurant.getIdx(), offeredAt); // then assertEquals(response.size(), req.registerReqList().size()); @@ -164,25 +171,27 @@ void getDayMealList() throws Exception { restaurantRegisterReq.address(), restaurantRegisterReq.restaurantName())); List list = new ArrayList<>(); - for (int i = 0; i < 7; i++) { - MealRegisterReq mealReq = - new MealRegisterReq( - "갈비탕, 깍두기, 흰쌀밥", - MealType.BREAKFAST.name(), - MealStatus.OPEN.name(), - LocalDate.now(), - 10000.0); - list.add(mealReq); - } + Instant today = Instant.now(); + MealRegisterReq mealReq = + new MealRegisterReq( + "갈비탕, 깍두기, 흰쌀밥", + MealType.BREAKFAST.name(), + MealStatus.OPEN.name(), + today, + 10000.0, + MealCategory.DEFAULT.name()); + list.add(mealReq); WeekMealRegisterReq req = new WeekMealRegisterReq(list, restaurant.getIdx()); mealService.createWeekMeal(req); // when + String offeredAt = LocalDate.now().toString().split("T")[0]; List response = - mealService.getDayMealList(restaurant.getIdx(), LocalDate.now().toString()); + mealService.getDayMealList(restaurant.getIdx(), offeredAt); // then - assertEquals(response.size(), req.registerReqList().size()); + assertEquals(response.size(), 3); + assertEquals(response.get(1).getMenu(), "등록된 식단이 없습니다."); } @DisplayName("학교별 학생 식당 조회") @@ -233,15 +242,16 @@ void createRestaurantWhenUniversityIsNotFound() throws Exception { void createWeekMealWhenRestaurantIsNotFound() throws Exception { // given List list = new ArrayList<>(); - LocalDate today = LocalDate.now(); + Instant today = Instant.now(); for (int i = 0; i < 7; i++) { MealRegisterReq mealReq = new MealRegisterReq( "갈비탕, 깍두기, 흰쌀밥", MealType.BREAKFAST.name(), MealStatus.OPEN.name(), - today.plusDays(i), - 10000.0); + today.plus(i, ChronoUnit.DAYS), + 10000.0, + MealCategory.DEFAULT.name()); list.add(mealReq); } WeekMealRegisterReq invalidReq = new WeekMealRegisterReq(list, 9999L); @@ -255,7 +265,7 @@ void createWeekMealWhenRestaurantIsNotFound() throws Exception { } @Test - @DisplayName("등록되어 있는 식단 데이터 보다 과거의 날짜로 식단을 등록하려는 경 - 덮어쓰기") + @DisplayName("등록되어 있는 식단 데이터 덮어 쓰기") void createWeekMealBeforeLastMealOfferedAt() throws Exception { // given RestaurantRegisterReq restaurantRegisterReq = getRestaurantRegisterReq(); @@ -277,21 +287,23 @@ void createWeekMealBeforeLastMealOfferedAt() throws Exception { .menu("떡볶이, 어묵탕, 튀김") .mealType(MealType.LUNCH) .mealStatus(MealStatus.OPEN) - .offeredAt(LocalDate.now()) + .offeredAt(Instant.now()) .price(5000.0) + .category(MealCategory.DEFAULT) .restaurant(restaurant) .build()); List list = new ArrayList<>(); - LocalDate today = LocalDate.now(); + Instant today = Instant.now(); for (int i = 0; i < 7; i++) { MealRegisterReq mealReq = new MealRegisterReq( "갈비탕, 깍두기, 흰쌀밥", - MealType.BREAKFAST.name(), + MealType.LUNCH.name(), MealStatus.OPEN.name(), - today.minusDays(i), - 10000.0); + today.plus(i, ChronoUnit.DAYS), + 10000.0, + MealCategory.DEFAULT.name()); list.add(mealReq); } WeekMealRegisterReq invalidReq = new WeekMealRegisterReq(list, restaurant.getIdx()); From 2d0960c59778fbf44b3b2c212deaee2703a08d00 Mon Sep 17 00:00:00 2001 From: kyubeom Date: Sun, 15 Oct 2023 16:18:39 +0900 Subject: [PATCH 3/6] =?UTF-8?q?refactor=20:=20meal=20=EC=A3=BC=EA=B0=84=20?= =?UTF-8?q?=EC=8B=9D=EB=8B=A8=20=EC=A1=B0=ED=9A=8C=20=EC=BF=BC=EB=A6=AC?= =?UTF-8?q?=ED=8A=9C=EB=8B=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/global/config/QueryDslConfig.java | 3 +- .../meal/controller/MealController.java | 6 +- .../dto/response/DayMealListGetResTest.java | 33 +++++ .../dto/response/WeekMealListGetResTest.java | 29 +++++ .../meal/repository/MealRepositoryCustom.java | 4 + .../meal/repository/MealRepositoryImpl.java | 38 ++++++ .../server/meal/service/MealService.java | 3 + .../server/meal/service/MealServiceImpl.java | 120 ++++++++++++------ 8 files changed, 194 insertions(+), 42 deletions(-) create mode 100644 src/main/java/everymeal/server/meal/controller/dto/response/DayMealListGetResTest.java create mode 100644 src/main/java/everymeal/server/meal/controller/dto/response/WeekMealListGetResTest.java diff --git a/src/main/java/everymeal/server/global/config/QueryDslConfig.java b/src/main/java/everymeal/server/global/config/QueryDslConfig.java index b2fbe09..fe42287 100644 --- a/src/main/java/everymeal/server/global/config/QueryDslConfig.java +++ b/src/main/java/everymeal/server/global/config/QueryDslConfig.java @@ -1,6 +1,7 @@ package everymeal.server.global.config; +import com.querydsl.jpa.JPQLTemplates; import com.querydsl.jpa.impl.JPAQueryFactory; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; @@ -14,6 +15,6 @@ public class QueryDslConfig { @Bean public JPAQueryFactory jpaQueryFactory(EntityManager em) { - return new JPAQueryFactory(em); + return new JPAQueryFactory(JPQLTemplates.DEFAULT, em); } } diff --git a/src/main/java/everymeal/server/meal/controller/MealController.java b/src/main/java/everymeal/server/meal/controller/MealController.java index 5d67925..15ed12a 100644 --- a/src/main/java/everymeal/server/meal/controller/MealController.java +++ b/src/main/java/everymeal/server/meal/controller/MealController.java @@ -7,6 +7,7 @@ import everymeal.server.meal.controller.dto.response.DayMealListGetRes; import everymeal.server.meal.controller.dto.response.RestaurantListGetRes; import everymeal.server.meal.controller.dto.response.WeekMealListGetRes; +import everymeal.server.meal.controller.dto.response.WeekMealListGetResTest; import everymeal.server.meal.service.MealService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Schema; @@ -101,13 +102,14 @@ public ApplicationResponse> getDayMeal( */ @GetMapping("/week") @Operation(summary = "주간 식단 조회") - public ApplicationResponse> getWeekMeal( + public ApplicationResponse> getWeekMeal( @RequestParam @Schema(description = "학생식당 PK", defaultValue = "1") Long restaurantIdx, @RequestParam @Schema( description = "조회하고자 하는 시작 날짜 ( yyyy-MM-dd )", defaultValue = "2023-10-01") String offeredAt) { - return ApplicationResponse.ok(mealService.getWeekMealList(restaurantIdx, offeredAt)); +// return ApplicationResponse.ok(mealService.getWeekMealList(restaurantIdx, offeredAt)); + return ApplicationResponse.ok(mealService.getWeekMealListTest(restaurantIdx, offeredAt)); } } diff --git a/src/main/java/everymeal/server/meal/controller/dto/response/DayMealListGetResTest.java b/src/main/java/everymeal/server/meal/controller/dto/response/DayMealListGetResTest.java new file mode 100644 index 0000000..b6ccb1a --- /dev/null +++ b/src/main/java/everymeal/server/meal/controller/dto/response/DayMealListGetResTest.java @@ -0,0 +1,33 @@ +package everymeal.server.meal.controller.dto.response; + +import com.fasterxml.jackson.annotation.JsonFormat; +import everymeal.server.meal.entity.MealCategory; +import everymeal.server.meal.entity.MealStatus; +import everymeal.server.meal.entity.MealType; +import java.time.Instant; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Builder +public class DayMealListGetResTest { + + private String menu; + private MealType mealType; + private MealStatus mealStatus; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul") + private Instant offeredAt; + + private Double price; + private MealCategory category; + private String restaurantName; + + +} diff --git a/src/main/java/everymeal/server/meal/controller/dto/response/WeekMealListGetResTest.java b/src/main/java/everymeal/server/meal/controller/dto/response/WeekMealListGetResTest.java new file mode 100644 index 0000000..76e1eb7 --- /dev/null +++ b/src/main/java/everymeal/server/meal/controller/dto/response/WeekMealListGetResTest.java @@ -0,0 +1,29 @@ +package everymeal.server.meal.controller.dto.response; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.querydsl.core.annotations.QueryProjection; +import java.time.Instant; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@Builder +public class WeekMealListGetResTest { + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul") + private Instant offeredAt; + + private List dayMealListGetResListTest; + + @QueryProjection + public WeekMealListGetResTest(Instant offeredAt, + List dayMealListGetResListTest) { + this.offeredAt = offeredAt; + this.dayMealListGetResListTest = dayMealListGetResListTest; + } +} diff --git a/src/main/java/everymeal/server/meal/repository/MealRepositoryCustom.java b/src/main/java/everymeal/server/meal/repository/MealRepositoryCustom.java index e5630db..218a67c 100644 --- a/src/main/java/everymeal/server/meal/repository/MealRepositoryCustom.java +++ b/src/main/java/everymeal/server/meal/repository/MealRepositoryCustom.java @@ -2,8 +2,10 @@ import everymeal.server.meal.controller.dto.request.MealRegisterReq; +import everymeal.server.meal.controller.dto.response.WeekMealListGetResTest; import everymeal.server.meal.entity.Meal; import everymeal.server.meal.entity.MealType; +import everymeal.server.meal.entity.Restaurant; import java.time.Instant; import java.util.List; import org.springframework.stereotype.Repository; @@ -14,4 +16,6 @@ public interface MealRepositoryCustom { List findAllByOfferedAtOnDateAndMealType( Instant offeredAt, MealType mealType, Long restaurantIdx); + + List getWeekMealList(Restaurant restaurant, Instant mondayInstant, Instant sundayInstant); } diff --git a/src/main/java/everymeal/server/meal/repository/MealRepositoryImpl.java b/src/main/java/everymeal/server/meal/repository/MealRepositoryImpl.java index bc89051..d618700 100644 --- a/src/main/java/everymeal/server/meal/repository/MealRepositoryImpl.java +++ b/src/main/java/everymeal/server/meal/repository/MealRepositoryImpl.java @@ -1,17 +1,28 @@ package everymeal.server.meal.repository; +import static com.querydsl.core.group.GroupBy.groupBy; + +import com.querydsl.core.Tuple; +import com.querydsl.core.group.GroupBy; +import com.querydsl.core.types.Projections; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.jpa.impl.JPAQueryFactory; import everymeal.server.meal.controller.dto.request.MealRegisterReq; +import everymeal.server.meal.controller.dto.response.DayMealListGetRes; +import everymeal.server.meal.controller.dto.response.DayMealListGetResTest; +import everymeal.server.meal.controller.dto.response.WeekMealListGetRes; +import everymeal.server.meal.controller.dto.response.WeekMealListGetResTest; import everymeal.server.meal.entity.Meal; import everymeal.server.meal.entity.MealCategory; import everymeal.server.meal.entity.MealType; import everymeal.server.meal.entity.QMeal; import everymeal.server.meal.entity.QRestaurant; +import everymeal.server.meal.entity.Restaurant; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.List; +import java.util.Map; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @@ -62,6 +73,33 @@ public List findAllByOfferedAtOnDateAndMealType( return queryResult; } + @Override + public List getWeekMealList(Restaurant restaurant, Instant mondayInstant, + Instant sundayInstant) { + QMeal qMeal = QMeal.meal; + Map transform = jpaQueryFactory.selectFrom(qMeal) + .where(qMeal.restaurant.eq(restaurant) + .and(qMeal.offeredAt.between(mondayInstant, sundayInstant))) + .transform( + groupBy(qMeal.offeredAt) + .as(Projections.constructor(WeekMealListGetResTest.class, + qMeal.offeredAt, + GroupBy.list(Projections.constructor(DayMealListGetResTest.class, + qMeal.menu.as("menu"), + qMeal.mealType.as("mealType"), + qMeal.mealStatus.as("mealStatus"), + qMeal.offeredAt.as("offeredAt"), + qMeal.price.as("price"), + qMeal.category.as("category"), + qMeal.restaurant.name.as("restaurantName") + )) + )) + ); + return transform.keySet().stream() + .map(transform::get) + .toList(); + } + /** 단일 메뉴, 복합 메뉴를 판별 */ private BooleanExpression isSingleMenu(boolean isSingle) { return isSingle ? QMeal.meal.category.eq(MealCategory.DEFAULT) : null; diff --git a/src/main/java/everymeal/server/meal/service/MealService.java b/src/main/java/everymeal/server/meal/service/MealService.java index 6d9aa4e..5a9c907 100644 --- a/src/main/java/everymeal/server/meal/service/MealService.java +++ b/src/main/java/everymeal/server/meal/service/MealService.java @@ -6,6 +6,7 @@ import everymeal.server.meal.controller.dto.response.DayMealListGetRes; import everymeal.server.meal.controller.dto.response.RestaurantListGetRes; import everymeal.server.meal.controller.dto.response.WeekMealListGetRes; +import everymeal.server.meal.controller.dto.response.WeekMealListGetResTest; import java.util.List; public interface MealService { @@ -19,4 +20,6 @@ public interface MealService { List getDayMealList(Long restaurantIdx, String offeredAt); List getWeekMealList(Long restaurantIdx, String offeredAt); + + List getWeekMealListTest(Long restaurantIdx, String offeredAt); } diff --git a/src/main/java/everymeal/server/meal/service/MealServiceImpl.java b/src/main/java/everymeal/server/meal/service/MealServiceImpl.java index 1dcf7cf..6581221 100644 --- a/src/main/java/everymeal/server/meal/service/MealServiceImpl.java +++ b/src/main/java/everymeal/server/meal/service/MealServiceImpl.java @@ -9,6 +9,7 @@ import everymeal.server.meal.controller.dto.response.DayMealListGetRes; import everymeal.server.meal.controller.dto.response.RestaurantListGetRes; import everymeal.server.meal.controller.dto.response.WeekMealListGetRes; +import everymeal.server.meal.controller.dto.response.WeekMealListGetResTest; import everymeal.server.meal.entity.Meal; import everymeal.server.meal.entity.MealCategory; import everymeal.server.meal.entity.MealStatus; @@ -19,7 +20,10 @@ import everymeal.server.meal.repository.RestaurantRepository; import everymeal.server.university.entity.University; import everymeal.server.university.repository.UniversityRepository; +import java.time.DayOfWeek; import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Comparator; @@ -234,51 +238,89 @@ public List getWeekMealList(Long restaurantIdx, String offer } // 비동기로 아침/점심/저녁 조회 쿼리 수행 List>> mealFutures = - dateList.stream() - .map( - ldOfferedAt -> { - CompletableFuture> breakfastFuture = - getMealsByDateAndTypeAsync( - ldOfferedAt.truncatedTo(ChronoUnit.DAYS), - MealType.BREAKFAST, - restaurant.getIdx()); - CompletableFuture> lunchFuture = - getMealsByDateAndTypeAsync( - ldOfferedAt.truncatedTo(ChronoUnit.DAYS), - MealType.LUNCH, - restaurant.getIdx()); - CompletableFuture> dinnerFuture = - getMealsByDateAndTypeAsync( - ldOfferedAt.truncatedTo(ChronoUnit.DAYS), - MealType.DINNER, - restaurant.getIdx()); + dateList.stream() + .map( + ldOfferedAt -> { + CompletableFuture> breakfastFuture = + getMealsByDateAndTypeAsync( + ldOfferedAt.truncatedTo(ChronoUnit.DAYS), + MealType.BREAKFAST, + restaurant.getIdx()); + CompletableFuture> lunchFuture = + getMealsByDateAndTypeAsync( + ldOfferedAt.truncatedTo(ChronoUnit.DAYS), + MealType.LUNCH, + restaurant.getIdx()); + CompletableFuture> dinnerFuture = + getMealsByDateAndTypeAsync( + ldOfferedAt.truncatedTo(ChronoUnit.DAYS), + MealType.DINNER, + restaurant.getIdx()); - return CompletableFuture.allOf( - breakfastFuture, lunchFuture, dinnerFuture) - .thenApply( - ignored -> { - List breakfastMeals = - breakfastFuture.join(); - List lunchMeals = lunchFuture.join(); - List dinnerMeals = - dinnerFuture.join(); + return CompletableFuture.allOf( + breakfastFuture, lunchFuture, dinnerFuture) + .thenApply( + ignored -> { + List breakfastMeals = + breakfastFuture.join(); + List lunchMeals = lunchFuture.join(); + List dinnerMeals = + dinnerFuture.join(); - // 등록되지 않은 식단 처리 - return chkEmptyDayMeal( - ldOfferedAt.truncatedTo( - ChronoUnit.DAYS), - restaurant.getName(), - breakfastMeals, - lunchMeals, - dinnerMeals); - }); - }) - .toList(); + // 등록되지 않은 식단 처리 + return chkEmptyDayMeal( + ldOfferedAt.truncatedTo( + ChronoUnit.DAYS), + restaurant.getName(), + breakfastMeals, + lunchMeals, + dinnerMeals); + }); + }) + .toList(); // CompletableFuture를 모두 조합하고 결과를 가져옴 List res = - mealFutures.stream().map(CompletableFuture::join).flatMap(List::stream).toList(); + mealFutures.stream().map(CompletableFuture::join).flatMap(List::stream).toList(); return WeekMealListGetRes.of(res); } + + @Override + public List getWeekMealListTest(Long restaurantIdx, String offeredAt) { + Restaurant restaurant = + restaurantRepository + .findById(restaurantIdx) + .orElseThrow( + () -> new ApplicationException(ExceptionList.RESTAURANT_NOT_FOUND)); + + // 현재 날짜와 시간을 가져옵니다. + LocalDateTime now = LocalDateTime.now().plusDays(1); + + // 현재 요일을 가져옵니다. + DayOfWeek currentDayOfWeek = now.getDayOfWeek(); + + // 월요일과 일요일의 날짜를 계산합니다. + LocalDateTime monday; + LocalDateTime sunday; + + if (currentDayOfWeek == DayOfWeek.MONDAY) { + // 현재 요일이 월요일인 경우, 현재 날짜를 월요일로 설정하고 일요일을 6일 후로 설정합니다. + monday = now; + sunday = now.plusDays(6); + } else { + // 그 외의 경우, 현재 요일로부터 월요일과 일요일을 계산합니다. + monday = now.minusDays(currentDayOfWeek.getValue() - DayOfWeek.MONDAY.getValue()); + sunday = now.plusDays(DayOfWeek.SUNDAY.getValue() - currentDayOfWeek.getValue()); + } + + // LocalDateTime을 한국 시간 (Asia/Seoul)으로 변환한 다음 Instant로 변환합니다. + ZoneId seoulZoneId = ZoneId.of("Asia/Seoul"); + Instant mondayInstant = monday.atZone(seoulZoneId).toInstant(); + Instant sundayInstant = sunday.atZone(seoulZoneId).toInstant(); + + return mealRepositoryCustom.getWeekMealList(restaurant, + mondayInstant, sundayInstant); + } + /** * ============================================================================================ * 비동기 식사 조회
From bb63818fe978fd3749bcd9e392a4331f44e4eba3 Mon Sep 17 00:00:00 2001 From: dldmsql Date: Sun, 22 Oct 2023 13:53:13 +0900 Subject: [PATCH 4/6] =?UTF-8?q?fix:=20#44=20=EC=8B=9D=EC=82=AC=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=20(=EC=A3=BC=EA=B0=84/=ED=95=98=EB=A3=A8)=20?= =?UTF-8?q?=EC=BF=BC=EB=A6=AC=20=ED=8A=9C=EB=8B=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 등록되지 않은 데이터 포맷에 맞추어 반환 작업을 서비스 레이어에서 레포지토리 레이어로 변경하였습니다. --- .../server/global/aop/log/LogAspect.java | 3 +- .../meal/controller/MealController.java | 8 +- .../dto/response/DayMealListGetRes.java | 28 +-- .../dto/response/DayMealListGetResTest.java | 33 --- .../dto/response/WeekMealListGetRes.java | 29 +-- .../dto/response/WeekMealListGetResTest.java | 29 --- .../server/meal/entity/MealType.java | 1 - .../meal/repository/MealRepositoryCustom.java | 13 +- .../meal/repository/MealRepositoryImpl.java | 200 ++++++++++++----- .../server/meal/service/MealService.java | 5 +- .../server/meal/service/MealServiceImpl.java | 208 +++--------------- .../meal/service/MealServiceImplTest.java | 2 +- 12 files changed, 195 insertions(+), 364 deletions(-) delete mode 100644 src/main/java/everymeal/server/meal/controller/dto/response/DayMealListGetResTest.java delete mode 100644 src/main/java/everymeal/server/meal/controller/dto/response/WeekMealListGetResTest.java diff --git a/src/main/java/everymeal/server/global/aop/log/LogAspect.java b/src/main/java/everymeal/server/global/aop/log/LogAspect.java index 9407c38..8d2879b 100644 --- a/src/main/java/everymeal/server/global/aop/log/LogAspect.java +++ b/src/main/java/everymeal/server/global/aop/log/LogAspect.java @@ -19,8 +19,7 @@ public LogAspect(LogTrace logTrace) { this.logTrace = logTrace; } - @Around( - "everymeal.server.global.aop.log.Pointcuts.allQuery() || everymeal.server.global.aop.log.Pointcuts.allService()") + @Around("everymeal.server.global.aop.log.Pointcuts.allService()") public Object executingTimeLog(ProceedingJoinPoint joinPoint) throws Throwable { TraceInfo traceInfo = null; try { diff --git a/src/main/java/everymeal/server/meal/controller/MealController.java b/src/main/java/everymeal/server/meal/controller/MealController.java index 15ed12a..a59ccbb 100644 --- a/src/main/java/everymeal/server/meal/controller/MealController.java +++ b/src/main/java/everymeal/server/meal/controller/MealController.java @@ -7,7 +7,6 @@ import everymeal.server.meal.controller.dto.response.DayMealListGetRes; import everymeal.server.meal.controller.dto.response.RestaurantListGetRes; import everymeal.server.meal.controller.dto.response.WeekMealListGetRes; -import everymeal.server.meal.controller.dto.response.WeekMealListGetResTest; import everymeal.server.meal.service.MealService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Schema; @@ -98,18 +97,19 @@ public ApplicationResponse> getDayMeal( /** * 주간 단위 식단 조회 API * - * @param + * @param restaurantIdx 식당 아이디 + * @param offeredAt 조회날짜 + * @author dldmsql */ @GetMapping("/week") @Operation(summary = "주간 식단 조회") - public ApplicationResponse> getWeekMeal( + public ApplicationResponse> getWeekMeal( @RequestParam @Schema(description = "학생식당 PK", defaultValue = "1") Long restaurantIdx, @RequestParam @Schema( description = "조회하고자 하는 시작 날짜 ( yyyy-MM-dd )", defaultValue = "2023-10-01") String offeredAt) { -// return ApplicationResponse.ok(mealService.getWeekMealList(restaurantIdx, offeredAt)); return ApplicationResponse.ok(mealService.getWeekMealListTest(restaurantIdx, offeredAt)); } } diff --git a/src/main/java/everymeal/server/meal/controller/dto/response/DayMealListGetRes.java b/src/main/java/everymeal/server/meal/controller/dto/response/DayMealListGetRes.java index 52fb419..9c76901 100644 --- a/src/main/java/everymeal/server/meal/controller/dto/response/DayMealListGetRes.java +++ b/src/main/java/everymeal/server/meal/controller/dto/response/DayMealListGetRes.java @@ -2,10 +2,10 @@ import com.fasterxml.jackson.annotation.JsonFormat; -import everymeal.server.meal.entity.Meal; +import everymeal.server.meal.entity.MealCategory; +import everymeal.server.meal.entity.MealStatus; +import everymeal.server.meal.entity.MealType; import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.List; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -20,29 +20,13 @@ public class DayMealListGetRes { private String menu; - private String mealType; - private String mealStatus; + private MealType mealType; + private MealStatus mealStatus; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul") private Instant offeredAt; private Double price; - private String category; + private MealCategory category; private String restaurantName; - - public static List of(List mealList) { - return mealList.stream() - .map( - meal -> - DayMealListGetRes.builder() - .menu(meal.getMenu()) - .mealType(meal.getMealType().getValue()) - .mealStatus(meal.getMealStatus().getValue()) - .offeredAt(meal.getOfferedAt().truncatedTo(ChronoUnit.DAYS)) - .price(meal.getPrice()) - .category(meal.getCategory().name()) - .restaurantName(meal.getRestaurant().getName()) - .build()) - .toList(); - } } diff --git a/src/main/java/everymeal/server/meal/controller/dto/response/DayMealListGetResTest.java b/src/main/java/everymeal/server/meal/controller/dto/response/DayMealListGetResTest.java deleted file mode 100644 index b6ccb1a..0000000 --- a/src/main/java/everymeal/server/meal/controller/dto/response/DayMealListGetResTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package everymeal.server.meal.controller.dto.response; - -import com.fasterxml.jackson.annotation.JsonFormat; -import everymeal.server.meal.entity.MealCategory; -import everymeal.server.meal.entity.MealStatus; -import everymeal.server.meal.entity.MealType; -import java.time.Instant; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@NoArgsConstructor -@AllArgsConstructor -@Getter -@Setter -@Builder -public class DayMealListGetResTest { - - private String menu; - private MealType mealType; - private MealStatus mealStatus; - - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul") - private Instant offeredAt; - - private Double price; - private MealCategory category; - private String restaurantName; - - -} diff --git a/src/main/java/everymeal/server/meal/controller/dto/response/WeekMealListGetRes.java b/src/main/java/everymeal/server/meal/controller/dto/response/WeekMealListGetRes.java index 351d561..60dc2f4 100644 --- a/src/main/java/everymeal/server/meal/controller/dto/response/WeekMealListGetRes.java +++ b/src/main/java/everymeal/server/meal/controller/dto/response/WeekMealListGetRes.java @@ -2,39 +2,28 @@ import com.fasterxml.jackson.annotation.JsonFormat; +import com.querydsl.core.annotations.QueryProjection; import java.time.Instant; import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; import lombok.Builder; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; @Getter @Setter +@NoArgsConstructor @Builder public class WeekMealListGetRes { @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul") private Instant offeredAt; - private List dayMealListGetResList; + private List dayMealListGetResListTest; - public static List of(List mealListGetRes) { - // offeredAt을 기준으로 DayMealList 그룹핑 - Map> groupedData = - mealListGetRes.stream() - .collect(Collectors.groupingBy(DayMealListGetRes::getOfferedAt)); - List weekMealList = - groupedData.entrySet().stream() - .sorted(Map.Entry.comparingByKey()) - .map( - entry -> - WeekMealListGetRes.builder() - .offeredAt(entry.getKey()) - .dayMealListGetResList(entry.getValue()) - .build()) - .collect(Collectors.toList()); - - return weekMealList; + @QueryProjection + public WeekMealListGetRes( + Instant offeredAt, List dayMealListGetResListTest) { + this.offeredAt = offeredAt; + this.dayMealListGetResListTest = dayMealListGetResListTest; } } diff --git a/src/main/java/everymeal/server/meal/controller/dto/response/WeekMealListGetResTest.java b/src/main/java/everymeal/server/meal/controller/dto/response/WeekMealListGetResTest.java deleted file mode 100644 index 76e1eb7..0000000 --- a/src/main/java/everymeal/server/meal/controller/dto/response/WeekMealListGetResTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package everymeal.server.meal.controller.dto.response; - -import com.fasterxml.jackson.annotation.JsonFormat; -import com.querydsl.core.annotations.QueryProjection; -import java.time.Instant; -import java.util.List; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@Getter -@Setter -@NoArgsConstructor -@Builder -public class WeekMealListGetResTest { - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul") - private Instant offeredAt; - - private List dayMealListGetResListTest; - - @QueryProjection - public WeekMealListGetResTest(Instant offeredAt, - List dayMealListGetResListTest) { - this.offeredAt = offeredAt; - this.dayMealListGetResListTest = dayMealListGetResListTest; - } -} diff --git a/src/main/java/everymeal/server/meal/entity/MealType.java b/src/main/java/everymeal/server/meal/entity/MealType.java index 1383972..26dfa49 100644 --- a/src/main/java/everymeal/server/meal/entity/MealType.java +++ b/src/main/java/everymeal/server/meal/entity/MealType.java @@ -10,7 +10,6 @@ public enum MealType { BREAKFAST("아침"), LUNCH("점심"), DINNER("저녁"), - SPECIAL("특식"), ; private String value; diff --git a/src/main/java/everymeal/server/meal/repository/MealRepositoryCustom.java b/src/main/java/everymeal/server/meal/repository/MealRepositoryCustom.java index 218a67c..0dc0d2d 100644 --- a/src/main/java/everymeal/server/meal/repository/MealRepositoryCustom.java +++ b/src/main/java/everymeal/server/meal/repository/MealRepositoryCustom.java @@ -1,8 +1,8 @@ package everymeal.server.meal.repository; -import everymeal.server.meal.controller.dto.request.MealRegisterReq; -import everymeal.server.meal.controller.dto.response.WeekMealListGetResTest; +import everymeal.server.meal.controller.dto.response.DayMealListGetRes; +import everymeal.server.meal.controller.dto.response.WeekMealListGetRes; import everymeal.server.meal.entity.Meal; import everymeal.server.meal.entity.MealType; import everymeal.server.meal.entity.Restaurant; @@ -12,10 +12,11 @@ @Repository public interface MealRepositoryCustom { - List findAllByAfterOfferedAt(MealRegisterReq mealRegisterReq, Long restaurantIdx); - List findAllByOfferedAtOnDateAndMealType( - Instant offeredAt, MealType mealType, Long restaurantIdx); + Instant offeredAt, MealType mealType, Restaurant restaurant); + + List findAllByOfferedAtOnDate(Instant offeredAt, Restaurant restaurant); - List getWeekMealList(Restaurant restaurant, Instant mondayInstant, Instant sundayInstant); + List getWeekMealList( + Restaurant restaurant, Instant mondayInstant, Instant sundayInstant); } diff --git a/src/main/java/everymeal/server/meal/repository/MealRepositoryImpl.java b/src/main/java/everymeal/server/meal/repository/MealRepositoryImpl.java index d618700..7cd9bfe 100644 --- a/src/main/java/everymeal/server/meal/repository/MealRepositoryImpl.java +++ b/src/main/java/everymeal/server/meal/repository/MealRepositoryImpl.java @@ -1,26 +1,24 @@ package everymeal.server.meal.repository; - import static com.querydsl.core.group.GroupBy.groupBy; +import static everymeal.server.meal.entity.QMeal.meal; -import com.querydsl.core.Tuple; import com.querydsl.core.group.GroupBy; import com.querydsl.core.types.Projections; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.jpa.impl.JPAQueryFactory; -import everymeal.server.meal.controller.dto.request.MealRegisterReq; import everymeal.server.meal.controller.dto.response.DayMealListGetRes; -import everymeal.server.meal.controller.dto.response.DayMealListGetResTest; import everymeal.server.meal.controller.dto.response.WeekMealListGetRes; -import everymeal.server.meal.controller.dto.response.WeekMealListGetResTest; import everymeal.server.meal.entity.Meal; import everymeal.server.meal.entity.MealCategory; +import everymeal.server.meal.entity.MealStatus; import everymeal.server.meal.entity.MealType; import everymeal.server.meal.entity.QMeal; -import everymeal.server.meal.entity.QRestaurant; import everymeal.server.meal.entity.Restaurant; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import lombok.RequiredArgsConstructor; @@ -30,94 +28,178 @@ public class MealRepositoryImpl implements MealRepositoryCustom { private final JPAQueryFactory jpaQueryFactory; /** * ============================================================================================ - * 현재 사용하지 않는 함수입니다. -- 추후 사용 가능성을 고려해서 유지하고 있습니다.
- * 요청 DTO 내의 제공일자 이후의 데이터가 존재하는 지를 확인합니다. + * GLOBAL STATIC CONSTANTS + * ============================================================================================= + */ + private final String UN_REGISTERED_MEAL = "등록된 식단이 없습니다."; + /** + * ============================================================================================ + * 요청 DTO 내의 제공일자 이후의 데이터가 존재하는 지를 확인합니다.
* - * @param restaurantIdx 식당 아이디 - * @param mealRegisterReq 요청 dto + * @param offeredAt 제공일자 + * @param mealType 식사구분 ( 조식/중식/석식 ) + * @param restaurant 학생식당 * @return List * ========================================================================================= */ @Override - public List findAllByAfterOfferedAt(MealRegisterReq mealRegisterReq, Long restaurantIdx) { + public List findAllByOfferedAtOnDateAndMealType( + Instant offeredAt, MealType mealType, Restaurant restaurant) { + QMeal qMeal = meal; var queryResult = jpaQueryFactory - .select(QMeal.meal) - .from(QMeal.meal) - .leftJoin(QMeal.meal.restaurant, QRestaurant.restaurant) - .on(QRestaurant.restaurant.idx.eq(restaurantIdx)) - .where(isAfterOfferedAt(mealRegisterReq.offeredAt())) + .selectFrom(qMeal) + .where( + qMeal.restaurant.eq(restaurant), + isEqOfferedAt(offeredAt), + isEqMealType(mealType)) .fetch(); return queryResult; } + /** * ============================================================================================ * 일별 식사구분에 따른 식사 데이터 조회
* - * @param restaurantIdx 식당 아이디 * @param offeredAt 제공일자 - * @param mealType 식사구분 ( 아침/점심/저녁 ) + * @param restaurant 식당 * @return List * ========================================================================================= */ @Override - public List findAllByOfferedAtOnDateAndMealType( - Instant offeredAt, MealType mealType, Long restaurantIdx) { + public List findAllByOfferedAtOnDate( + Instant offeredAt, Restaurant restaurant) { + QMeal qMeal = meal; var queryResult = jpaQueryFactory - .selectFrom(QMeal.meal) - .leftJoin(QMeal.meal.restaurant, QRestaurant.restaurant) - .on(QRestaurant.restaurant.idx.eq(restaurantIdx)) - .where(isEqOfferedAt(offeredAt), isEqMealType(mealType)) - .fetch(); - return queryResult; - } + .selectFrom(qMeal) + .where(qMeal.restaurant.eq(restaurant), isEqOfferedAt(offeredAt)) + .transform( + groupBy(qMeal.mealType) + .as( + GroupBy.list( + Projections.constructor( + DayMealListGetRes.class, + qMeal.menu.as("menu"), + qMeal.mealType.as("mealType"), + qMeal.mealStatus.as("mealStatus"), + qMeal.offeredAt.as("offeredAt"), + qMeal.price.as("price"), + qMeal.category.as("category"), + qMeal.restaurant.name.as( + "restaurantName"))))); + List resultList = new ArrayList<>(); + for (MealType mealType : MealType.values()) { + List dayMeals = queryResult.get(mealType); + if (dayMeals == null || dayMeals.isEmpty()) { + resultList.add( + new DayMealListGetRes( + UN_REGISTERED_MEAL, + mealType, + MealStatus.CLOSED, + offeredAt, + 0.0, + MealCategory.DEFAULT, + restaurant.getName())); + } else { + resultList.addAll(dayMeals); + } + } + return resultList; + } + /** + * ============================================================================================ + * 주간 식사 데이터 조회
+ * + * @param restaurant 식당 + * @param mondayInstant 월요일 + * @param sundayInstant 일요일 + * @return List + * ========================================================================================= + */ @Override - public List getWeekMealList(Restaurant restaurant, Instant mondayInstant, - Instant sundayInstant) { - QMeal qMeal = QMeal.meal; - Map transform = jpaQueryFactory.selectFrom(qMeal) - .where(qMeal.restaurant.eq(restaurant) - .and(qMeal.offeredAt.between(mondayInstant, sundayInstant))) - .transform( - groupBy(qMeal.offeredAt) - .as(Projections.constructor(WeekMealListGetResTest.class, - qMeal.offeredAt, - GroupBy.list(Projections.constructor(DayMealListGetResTest.class, - qMeal.menu.as("menu"), - qMeal.mealType.as("mealType"), - qMeal.mealStatus.as("mealStatus"), - qMeal.offeredAt.as("offeredAt"), - qMeal.price.as("price"), - qMeal.category.as("category"), - qMeal.restaurant.name.as("restaurantName") - )) - )) - ); - return transform.keySet().stream() - .map(transform::get) - .toList(); + public List getWeekMealList( + Restaurant restaurant, Instant mondayInstant, Instant sundayInstant) { + QMeal qMeal = meal; + Map transform = + jpaQueryFactory + .selectFrom(qMeal) + .where( + qMeal.restaurant + .eq(restaurant) + .and(qMeal.offeredAt.between(mondayInstant, sundayInstant))) + .transform( + groupBy(qMeal.offeredAt) + .as( + Projections.constructor( + WeekMealListGetRes.class, + qMeal.offeredAt, + GroupBy.list( + Projections.constructor( + DayMealListGetRes.class, + qMeal.menu.as("menu"), + qMeal.mealType.as( + "mealType"), + qMeal.mealStatus.as( + "mealStatus"), + qMeal.offeredAt.as( + "offeredAt"), + qMeal.price.as("price"), + qMeal.category.as( + "category"), + qMeal.restaurant.name.as( + "restaurantName")))))); + Instant currentInstant = mondayInstant; + while (!currentInstant.isAfter(sundayInstant)) { + transform.putIfAbsent( + currentInstant, + new WeekMealListGetRes( + currentInstant, + Arrays.asList( + new DayMealListGetRes( + UN_REGISTERED_MEAL, + MealType.BREAKFAST, + MealStatus.CLOSED, + currentInstant, + 0.0, + MealCategory.DEFAULT, + restaurant.getName()), + new DayMealListGetRes( + UN_REGISTERED_MEAL, + MealType.LUNCH, + MealStatus.CLOSED, + currentInstant, + 0.0, + MealCategory.DEFAULT, + restaurant.getName()), + new DayMealListGetRes( + UN_REGISTERED_MEAL, + MealType.DINNER, + MealStatus.CLOSED, + currentInstant, + 0.0, + MealCategory.DEFAULT, + restaurant.getName())))); + currentInstant = currentInstant.plus(1, ChronoUnit.DAYS); + } + + return transform.keySet().stream().map(transform::get).toList(); } /** 단일 메뉴, 복합 메뉴를 판별 */ private BooleanExpression isSingleMenu(boolean isSingle) { - return isSingle ? QMeal.meal.category.eq(MealCategory.DEFAULT) : null; + return isSingle ? meal.category.eq(MealCategory.DEFAULT) : null; } /** offeredAt 과 동일한 경우 */ private BooleanExpression isEqOfferedAt(Instant offeredAt) { - Instant startOfDay = offeredAt.truncatedTo(ChronoUnit.DAYS); + Instant startOfDay = offeredAt.truncatedTo(ChronoUnit.DAYS); // 예: 2023-10-01 Instant endOfDay = startOfDay.plus(1, ChronoUnit.DAYS).minus(1, ChronoUnit.MILLIS); return QMeal.meal.offeredAt.between(startOfDay, endOfDay); } /** mealType 과 동일한 경우 */ private BooleanExpression isEqMealType(MealType mealType) { - return QMeal.meal.mealType.eq(mealType); - } - - /** offeredAt 이후인 경우 */ - private BooleanExpression isAfterOfferedAt(Instant offeredAt) { - return QMeal.meal.offeredAt.after(offeredAt); + return meal.mealType.eq(mealType); } } diff --git a/src/main/java/everymeal/server/meal/service/MealService.java b/src/main/java/everymeal/server/meal/service/MealService.java index 5a9c907..cc28cc9 100644 --- a/src/main/java/everymeal/server/meal/service/MealService.java +++ b/src/main/java/everymeal/server/meal/service/MealService.java @@ -6,7 +6,6 @@ import everymeal.server.meal.controller.dto.response.DayMealListGetRes; import everymeal.server.meal.controller.dto.response.RestaurantListGetRes; import everymeal.server.meal.controller.dto.response.WeekMealListGetRes; -import everymeal.server.meal.controller.dto.response.WeekMealListGetResTest; import java.util.List; public interface MealService { @@ -19,7 +18,5 @@ public interface MealService { List getDayMealList(Long restaurantIdx, String offeredAt); - List getWeekMealList(Long restaurantIdx, String offeredAt); - - List getWeekMealListTest(Long restaurantIdx, String offeredAt); + List getWeekMealListTest(Long restaurantIdx, String offeredAt); } diff --git a/src/main/java/everymeal/server/meal/service/MealServiceImpl.java b/src/main/java/everymeal/server/meal/service/MealServiceImpl.java index 6581221..bbd1462 100644 --- a/src/main/java/everymeal/server/meal/service/MealServiceImpl.java +++ b/src/main/java/everymeal/server/meal/service/MealServiceImpl.java @@ -9,7 +9,6 @@ import everymeal.server.meal.controller.dto.response.DayMealListGetRes; import everymeal.server.meal.controller.dto.response.RestaurantListGetRes; import everymeal.server.meal.controller.dto.response.WeekMealListGetRes; -import everymeal.server.meal.controller.dto.response.WeekMealListGetResTest; import everymeal.server.meal.entity.Meal; import everymeal.server.meal.entity.MealCategory; import everymeal.server.meal.entity.MealStatus; @@ -28,9 +27,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import java.util.concurrent.CompletableFuture; import lombok.RequiredArgsConstructor; -import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -48,9 +45,7 @@ public class MealServiceImpl implements MealService { * GLOBAL STATIC CONSTANTS * ============================================================================================= */ - private final String UN_REGISTERED_MEAL = "등록된 식단이 없습니다."; - - private final String TIME_PARSING_INFO = "T00:00:00Z"; + private final String TIME_PARSING_INFO = "T00:00:00"; /** * ============================================================================================ @@ -117,7 +112,7 @@ public Boolean createWeekMeal(WeekMealRegisterReq weekMealRegisterReq) { // 제공날짜, 학생식당, 식사분류가 동일한 데이터가 이미 존재하면, 덮어쓰기 불가능 오류 if (!mealRepositoryCustom .findAllByOfferedAtOnDateAndMealType( - req.offeredAt(), MealType.valueOf(req.mealType()), restaurant.getIdx()) + req.offeredAt(), MealType.valueOf(req.mealType()), restaurant) .isEmpty()) { throw new ApplicationException(ExceptionList.INVALID_MEAL_OFFEREDAT_REQUEST); } else { @@ -183,120 +178,47 @@ public List getRestaurantList(String universityName, Strin */ @Override public List getDayMealList(Long restaurantIdx, String offeredAt) { - Instant ldOfferedAt = Instant.parse(offeredAt + TIME_PARSING_INFO); + // offeredAt을 LocalDateTime로 바꿉니다. + LocalDateTime ldOfferedAt = LocalDateTime.parse(offeredAt + TIME_PARSING_INFO); + // 학생 식당 등록 여부 판단 Restaurant restaurant = restaurantRepository .findById(restaurantIdx) .orElseThrow( () -> new ApplicationException(ExceptionList.RESTAURANT_NOT_FOUND)); - // REQ offeredAt에 해당하는 식단 조회 - List breakfastMeals = - mealRepositoryCustom.findAllByOfferedAtOnDateAndMealType( - ldOfferedAt, MealType.BREAKFAST, restaurant.getIdx()); - List lunchMeals = - mealRepositoryCustom.findAllByOfferedAtOnDateAndMealType( - ldOfferedAt, MealType.LUNCH, restaurant.getIdx()); - List dinnerMeals = - mealRepositoryCustom.findAllByOfferedAtOnDateAndMealType( - ldOfferedAt, MealType.DINNER, restaurant.getIdx()); - // 등록되지 않은 식단 처리 - List res = - chkEmptyDayMeal( - ldOfferedAt, restaurant.getName(), breakfastMeals, lunchMeals, dinnerMeals); - return res; - } + // LocalDateTime을 한국 시간 (Asia/Seoul)으로 변환한 다음 Instant로 변환합니다. + ZoneId seoulZoneId = ZoneId.of("Asia/Seoul"); + Instant iOfferedAt = ldOfferedAt.atZone(seoulZoneId).toInstant().plus(1, ChronoUnit.DAYS); + + return mealRepositoryCustom.findAllByOfferedAtOnDate(iOfferedAt, restaurant); + } /** * ============================================================================================ * 학식 식단 Week 조회
- * 등록되지 않은 식단 데이터는 아침/점심/저녁 포맷팅에 맞게 응답 데이터를 생성해서 반환합니다. ( 7일 데이터 ) + * 등록되지 않은 식단 데이터는 아침/점심/저녁 포맷팅에 맞게 응답 데이터를 생성해서 반환합니다. * * @param restaurantIdx 식당 아이디 - * @param offeredAt 제공일자 --- yyyy-MM-dd - * @return List + * @param offeredAt 제공 일자 --- yyyy-MM-dd + * @return List *

식당이 없는 경우, * @throws ApplicationException 404 존재하지 않는 식당입니다.
* ========================================================================================= */ @Override - public List getWeekMealList(Long restaurantIdx, String offeredAt) { - // 학생 식당 등록 여부 판단 + public List getWeekMealListTest(Long restaurantIdx, String offeredAt) { Restaurant restaurant = restaurantRepository .findById(restaurantIdx) .orElseThrow( () -> new ApplicationException(ExceptionList.RESTAURANT_NOT_FOUND)); - // REQ offeredAt을 시작 일자로 주간 단위 식단 조회 - Instant startedAt = Instant.parse(offeredAt + TIME_PARSING_INFO); - Instant endedAt = startedAt.plus(6, ChronoUnit.DAYS); - List dateList = new ArrayList<>(); - Instant current = startedAt; - while (!current.isAfter(endedAt)) { - dateList.add(current); - current = current.plus(1, ChronoUnit.DAYS); - } - // 비동기로 아침/점심/저녁 조회 쿼리 수행 - List>> mealFutures = - dateList.stream() - .map( - ldOfferedAt -> { - CompletableFuture> breakfastFuture = - getMealsByDateAndTypeAsync( - ldOfferedAt.truncatedTo(ChronoUnit.DAYS), - MealType.BREAKFAST, - restaurant.getIdx()); - CompletableFuture> lunchFuture = - getMealsByDateAndTypeAsync( - ldOfferedAt.truncatedTo(ChronoUnit.DAYS), - MealType.LUNCH, - restaurant.getIdx()); - CompletableFuture> dinnerFuture = - getMealsByDateAndTypeAsync( - ldOfferedAt.truncatedTo(ChronoUnit.DAYS), - MealType.DINNER, - restaurant.getIdx()); - - return CompletableFuture.allOf( - breakfastFuture, lunchFuture, dinnerFuture) - .thenApply( - ignored -> { - List breakfastMeals = - breakfastFuture.join(); - List lunchMeals = lunchFuture.join(); - List dinnerMeals = - dinnerFuture.join(); - - // 등록되지 않은 식단 처리 - return chkEmptyDayMeal( - ldOfferedAt.truncatedTo( - ChronoUnit.DAYS), - restaurant.getName(), - breakfastMeals, - lunchMeals, - dinnerMeals); - }); - }) - .toList(); - // CompletableFuture를 모두 조합하고 결과를 가져옴 - List res = - mealFutures.stream().map(CompletableFuture::join).flatMap(List::stream).toList(); - return WeekMealListGetRes.of(res); - } - - @Override - public List getWeekMealListTest(Long restaurantIdx, String offeredAt) { - Restaurant restaurant = - restaurantRepository - .findById(restaurantIdx) - .orElseThrow( - () -> new ApplicationException(ExceptionList.RESTAURANT_NOT_FOUND)); // 현재 날짜와 시간을 가져옵니다. - LocalDateTime now = LocalDateTime.now().plusDays(1); + LocalDateTime ldOfferedAt = LocalDateTime.parse(offeredAt + TIME_PARSING_INFO); // 현재 요일을 가져옵니다. - DayOfWeek currentDayOfWeek = now.getDayOfWeek(); + DayOfWeek currentDayOfWeek = ldOfferedAt.getDayOfWeek(); // 월요일과 일요일의 날짜를 계산합니다. LocalDateTime monday; @@ -304,12 +226,15 @@ public List getWeekMealListTest(Long restaurantIdx, Stri if (currentDayOfWeek == DayOfWeek.MONDAY) { // 현재 요일이 월요일인 경우, 현재 날짜를 월요일로 설정하고 일요일을 6일 후로 설정합니다. - monday = now; - sunday = now.plusDays(6); + monday = ldOfferedAt; + sunday = ldOfferedAt.plusDays(6); } else { // 그 외의 경우, 현재 요일로부터 월요일과 일요일을 계산합니다. - monday = now.minusDays(currentDayOfWeek.getValue() - DayOfWeek.MONDAY.getValue()); - sunday = now.plusDays(DayOfWeek.SUNDAY.getValue() - currentDayOfWeek.getValue()); + monday = + ldOfferedAt.minusDays( + currentDayOfWeek.getValue() - DayOfWeek.MONDAY.getValue()); + sunday = + ldOfferedAt.plusDays(DayOfWeek.SUNDAY.getValue() - currentDayOfWeek.getValue()); } // LocalDateTime을 한국 시간 (Asia/Seoul)으로 변환한 다음 Instant로 변환합니다. @@ -317,89 +242,6 @@ public List getWeekMealListTest(Long restaurantIdx, Stri Instant mondayInstant = monday.atZone(seoulZoneId).toInstant(); Instant sundayInstant = sunday.atZone(seoulZoneId).toInstant(); - return mealRepositoryCustom.getWeekMealList(restaurant, - mondayInstant, sundayInstant); - } - - /** - * ============================================================================================ - * 비동기 식사 조회
- * 일별 아침/점심/저녁에 따른 식사 조회 쿼리를 수행합니다. - * - * @param restaurantIdx 식당 아이디 - * @param date 제공일자 --- yyyy-MM-dd - * @param mealType 식사 구분 ( 아침/점심/저녁 ) - * @return CompletableFuture> - * ========================================================================================= - */ - @Async - public CompletableFuture> getMealsByDateAndTypeAsync( - Instant date, MealType mealType, Long restaurantIdx) { - // 비동기로 아침/점심/저녁 조회 쿼리 수행 - List meals = - mealRepositoryCustom.findAllByOfferedAtOnDateAndMealType( - date, mealType, restaurantIdx); - return CompletableFuture.completedFuture(meals); - } - /** - * ============================================================================================ - * 등록되지 않은 식단 존재 여부 확인
- * 등록되지 않은 식단이 존재하는 경우, 응답을 위한 더미 데이터를 생성합니다. - * - * @param offeredAt 제공일자 - * @param restaurantName 식당명 - * @param breakfastMeals 아침 식사 - * @param lunchMeals 점심 식사 - * @param dinnerMeals 저녁 식사 - * @return List - * ========================================================================================= - */ - private List chkEmptyDayMeal( - Instant offeredAt, - String restaurantName, - List breakfastMeals, - List lunchMeals, - List dinnerMeals) { - List res = new ArrayList<>(); - if (breakfastMeals.isEmpty()) { - res.add( - new DayMealListGetRes( - UN_REGISTERED_MEAL, - MealType.BREAKFAST.getValue(), - MealStatus.OPEN.getValue(), - offeredAt, - 0.0, - MealCategory.DEFAULT.getValue(), - restaurantName)); - } else { - res.addAll(DayMealListGetRes.of(breakfastMeals)); - } - if (lunchMeals.isEmpty()) { - res.add( - new DayMealListGetRes( - UN_REGISTERED_MEAL, - MealType.LUNCH.getValue(), - MealStatus.OPEN.getValue(), - offeredAt, - 0.0, - MealCategory.DEFAULT.getValue(), - restaurantName)); - } else { - res.addAll(DayMealListGetRes.of(lunchMeals)); - } - if (dinnerMeals.isEmpty()) { - res.add( - new DayMealListGetRes( - UN_REGISTERED_MEAL, - MealType.DINNER.getValue(), - MealStatus.OPEN.getValue(), - offeredAt, - 0.0, - MealCategory.DEFAULT.getValue(), - restaurantName)); - } else { - res.addAll(DayMealListGetRes.of(dinnerMeals)); - } - return res; + return mealRepositoryCustom.getWeekMealList(restaurant, mondayInstant, sundayInstant); } } diff --git a/src/test/java/everymeal/server/meal/service/MealServiceImplTest.java b/src/test/java/everymeal/server/meal/service/MealServiceImplTest.java index c1a2f8f..6663d90 100644 --- a/src/test/java/everymeal/server/meal/service/MealServiceImplTest.java +++ b/src/test/java/everymeal/server/meal/service/MealServiceImplTest.java @@ -148,7 +148,7 @@ void getWeekMealList() throws Exception { // when String offeredAt = today.toString().split("T")[0]; List response = - mealService.getWeekMealList(restaurant.getIdx(), offeredAt); + mealService.getWeekMealListTest(restaurant.getIdx(), offeredAt); // then assertEquals(response.size(), req.registerReqList().size()); From 8ed92bd327d8cc888fc387a92e95664054d30592 Mon Sep 17 00:00:00 2001 From: dldmsql Date: Sun, 22 Oct 2023 14:18:37 +0900 Subject: [PATCH 5/6] =?UTF-8?q?fix:=20#44=20sonarcloud=20=EB=B2=84?= =?UTF-8?q?=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/everymeal/server/meal/service/MealServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/everymeal/server/meal/service/MealServiceImpl.java b/src/main/java/everymeal/server/meal/service/MealServiceImpl.java index bbd1462..29f72dc 100644 --- a/src/main/java/everymeal/server/meal/service/MealServiceImpl.java +++ b/src/main/java/everymeal/server/meal/service/MealServiceImpl.java @@ -232,9 +232,9 @@ public List getWeekMealListTest(Long restaurantIdx, String o // 그 외의 경우, 현재 요일로부터 월요일과 일요일을 계산합니다. monday = ldOfferedAt.minusDays( - currentDayOfWeek.getValue() - DayOfWeek.MONDAY.getValue()); + currentDayOfWeek.getValue() - (long)DayOfWeek.MONDAY.getValue()); sunday = - ldOfferedAt.plusDays(DayOfWeek.SUNDAY.getValue() - currentDayOfWeek.getValue()); + ldOfferedAt.plusDays(DayOfWeek.SUNDAY.getValue() - (long)currentDayOfWeek.getValue()); } // LocalDateTime을 한국 시간 (Asia/Seoul)으로 변환한 다음 Instant로 변환합니다. From 002a9ca0408606689cbb17133ff6e46767ba2065 Mon Sep 17 00:00:00 2001 From: dldmsql Date: Sun, 22 Oct 2023 14:18:37 +0900 Subject: [PATCH 6/6] =?UTF-8?q?style:=20Spotless=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/everymeal/server/meal/service/MealServiceImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/everymeal/server/meal/service/MealServiceImpl.java b/src/main/java/everymeal/server/meal/service/MealServiceImpl.java index bbd1462..aa6eefb 100644 --- a/src/main/java/everymeal/server/meal/service/MealServiceImpl.java +++ b/src/main/java/everymeal/server/meal/service/MealServiceImpl.java @@ -232,9 +232,10 @@ public List getWeekMealListTest(Long restaurantIdx, String o // 그 외의 경우, 현재 요일로부터 월요일과 일요일을 계산합니다. monday = ldOfferedAt.minusDays( - currentDayOfWeek.getValue() - DayOfWeek.MONDAY.getValue()); + currentDayOfWeek.getValue() - (long) DayOfWeek.MONDAY.getValue()); sunday = - ldOfferedAt.plusDays(DayOfWeek.SUNDAY.getValue() - currentDayOfWeek.getValue()); + ldOfferedAt.plusDays( + DayOfWeek.SUNDAY.getValue() - (long) currentDayOfWeek.getValue()); } // LocalDateTime을 한국 시간 (Asia/Seoul)으로 변환한 다음 Instant로 변환합니다.