diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/reserve/ReserveApiController.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/ReserveApiController.java similarity index 81% rename from backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/reserve/ReserveApiController.java rename to backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/ReserveApiController.java index 2885120..c0dc310 100644 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/reserve/ReserveApiController.java +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/ReserveApiController.java @@ -1,29 +1,32 @@ -package org.egovframe.cloud.reservechecksevice.api.reserve; +package org.egovframe.cloud.reservechecksevice.api; import java.time.LocalDate; - +import javax.validation.Valid; import lombok.RequiredArgsConstructor; -import org.egovframe.cloud.common.dto.RequestDto; -import org.egovframe.cloud.reservechecksevice.api.reserve.dto.*; -import org.egovframe.cloud.reservechecksevice.domain.reserve.Category; -import org.egovframe.cloud.reservechecksevice.service.reserve.ReserveService; +import org.egovframe.cloud.reservechecksevice.api.dto.ReserveCancelRequestDto; +import org.egovframe.cloud.reservechecksevice.api.dto.ReserveListResponseDto; +import org.egovframe.cloud.reservechecksevice.api.dto.ReserveRequestDto; +import org.egovframe.cloud.reservechecksevice.api.dto.ReserveResponseDto; +import org.egovframe.cloud.reservechecksevice.api.dto.ReserveSaveRequestDto; +import org.egovframe.cloud.reservechecksevice.api.dto.ReserveUpdateRequestDto; +import org.egovframe.cloud.reservechecksevice.service.ReserveService; import org.springframework.core.env.Environment; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.HttpStatus; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.ReactiveSecurityContextHolder; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.bind.annotation.*; -import reactor.core.publisher.Flux; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; -import javax.validation.Valid; - /** - * org.egovframe.cloud.reservechecksevice.api.reserve.ReserveApiController + * org.egovframe.cloud.reservechecksevice.api.ReserveApiController *

* 예약 확인 rest controller class * diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/reserve/dto/ReserveCancelRequestDto.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/dto/ReserveCancelRequestDto.java similarity index 83% rename from backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/reserve/dto/ReserveCancelRequestDto.java rename to backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/dto/ReserveCancelRequestDto.java index cdc0a0a..8afc3da 100644 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/reserve/dto/ReserveCancelRequestDto.java +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/dto/ReserveCancelRequestDto.java @@ -1,14 +1,13 @@ -package org.egovframe.cloud.reservechecksevice.api.reserve.dto; +package org.egovframe.cloud.reservechecksevice.api.dto; import javax.validation.constraints.NotBlank; - import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; /** - * org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveCancelRequestDto + * org.egovframe.cloud.reservechecksevice.api.dto.ReserveCancelRequestDto *

* 예약 취소 요청 dto class * diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/reserve/dto/ReserveListResponseDto.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/dto/ReserveListResponseDto.java similarity index 88% rename from backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/reserve/dto/ReserveListResponseDto.java rename to backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/dto/ReserveListResponseDto.java index 0d6577f..3a13136 100644 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/reserve/dto/ReserveListResponseDto.java +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/dto/ReserveListResponseDto.java @@ -1,15 +1,14 @@ -package org.egovframe.cloud.reservechecksevice.api.reserve.dto; +package org.egovframe.cloud.reservechecksevice.api.dto; +import java.time.LocalDateTime; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; -import org.egovframe.cloud.reservechecksevice.domain.reserve.Reserve; - -import java.time.LocalDateTime; +import org.egovframe.cloud.reservechecksevice.domain.Reserve; /** - * org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveListResponseDto + * org.egovframe.cloud.reservechecksevice.api.dto.ReserveListResponseDto *

* 예약 목록 응답 dto class * diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/reserve/dto/ReserveRequestDto.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/dto/ReserveRequestDto.java similarity index 81% rename from backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/reserve/dto/ReserveRequestDto.java rename to backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/dto/ReserveRequestDto.java index 9c9e361..de9f060 100644 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/reserve/dto/ReserveRequestDto.java +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/dto/ReserveRequestDto.java @@ -1,11 +1,11 @@ -package org.egovframe.cloud.reservechecksevice.api.reserve.dto; +package org.egovframe.cloud.reservechecksevice.api.dto; import lombok.Getter; import lombok.NoArgsConstructor; import org.egovframe.cloud.common.dto.RequestDto; /** - * org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveRequestDto + * org.egovframe.cloud.reservechecksevice.api.dto.ReserveRequestDto *

* 얘약 목록 요청 dto class * diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/reserve/dto/ReserveResponseDto.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/dto/ReserveResponseDto.java similarity index 89% rename from backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/reserve/dto/ReserveResponseDto.java rename to backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/dto/ReserveResponseDto.java index 52c73cf..7d7ec85 100644 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/reserve/dto/ReserveResponseDto.java +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/dto/ReserveResponseDto.java @@ -1,17 +1,15 @@ -package org.egovframe.cloud.reservechecksevice.api.reserve.dto; +package org.egovframe.cloud.reservechecksevice.api.dto; +import java.time.LocalDateTime; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; import org.egovframe.cloud.reservechecksevice.client.dto.ReserveItemRelationResponseDto; -import org.egovframe.cloud.reservechecksevice.domain.reserve.Reserve; - -import java.math.BigDecimal; -import java.time.LocalDateTime; +import org.egovframe.cloud.reservechecksevice.domain.Reserve; /** - * org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveResponseDto + * org.egovframe.cloud.reservechecksevice.api.dto.ReserveResponseDto *

* 예약 확인(신청) 응답 dto class * diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/reserve/dto/ReserveSaveRequestDto.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/dto/ReserveSaveRequestDto.java similarity index 89% rename from backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/reserve/dto/ReserveSaveRequestDto.java rename to backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/dto/ReserveSaveRequestDto.java index 7eaa3b6..54dafeb 100644 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/reserve/dto/ReserveSaveRequestDto.java +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/dto/ReserveSaveRequestDto.java @@ -1,20 +1,18 @@ -package org.egovframe.cloud.reservechecksevice.api.reserve.dto; +package org.egovframe.cloud.reservechecksevice.api.dto; +import java.time.LocalDateTime; +import java.util.UUID; +import javax.validation.constraints.NotNull; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; -import lombok.With; - -import org.egovframe.cloud.reservechecksevice.domain.reserve.Reserve; +import org.egovframe.cloud.reservechecksevice.domain.Reserve; import org.egovframe.cloud.reservechecksevice.validator.annotation.ReserveSaveValid; -import javax.validation.constraints.NotNull; -import java.time.LocalDateTime; - /** - * org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveSaveRequestDto + * org.egovframe.cloud.reservechecksevice.api.dto.ReserveSaveRequestDto *

* 예약 신청 요청 dto class * @@ -77,6 +75,11 @@ public class ReserveSaveRequestDto { this.userEmail = userEmail; } + public Reserve createNewReserve() { + this.reserveId = String.valueOf(UUID.randomUUID()); + return toEntity(); + } + public Reserve toEntity() { Reserve reserve = Reserve.builder() .reserveId(this.reserveId) diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/reserve/dto/ReserveUpdateRequestDto.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/dto/ReserveUpdateRequestDto.java similarity index 93% rename from backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/reserve/dto/ReserveUpdateRequestDto.java rename to backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/dto/ReserveUpdateRequestDto.java index 50efc06..160ffd1 100644 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/reserve/dto/ReserveUpdateRequestDto.java +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/api/dto/ReserveUpdateRequestDto.java @@ -1,16 +1,15 @@ -package org.egovframe.cloud.reservechecksevice.api.reserve.dto; +package org.egovframe.cloud.reservechecksevice.api.dto; +import java.time.LocalDateTime; +import javax.validation.constraints.NotNull; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; import org.egovframe.cloud.reservechecksevice.validator.annotation.ReserveSaveValid; -import javax.validation.constraints.NotNull; -import java.time.LocalDateTime; - /** - * org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveUpdateRequestDto + * org.egovframe.cloud.reservechecksevice.api.dto.ReserveUpdateRequestDto *

* 예약 신청 정보 수정 요청 dto class * diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/client/ReserveItemServiceClient.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/client/ReserveItemServiceClient.java index 2bbb0b9..c81948e 100644 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/client/ReserveItemServiceClient.java +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/client/ReserveItemServiceClient.java @@ -6,7 +6,6 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; - import reactivefeign.spring.config.ReactiveFeignClient; import reactor.core.publisher.Mono; diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/client/UserServiceClient.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/client/UserServiceClient.java index 26346fa..35cb848 100644 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/client/UserServiceClient.java +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/client/UserServiceClient.java @@ -3,7 +3,6 @@ package org.egovframe.cloud.reservechecksevice.client; import org.egovframe.cloud.reservechecksevice.client.dto.UserResponseDto; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; - import reactivefeign.spring.config.ReactiveFeignClient; import reactor.core.publisher.Mono; diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/client/dto/ReserveItemRelationResponseDto.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/client/dto/ReserveItemRelationResponseDto.java index e951eb9..7afd96a 100644 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/client/dto/ReserveItemRelationResponseDto.java +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/client/dto/ReserveItemRelationResponseDto.java @@ -1,16 +1,14 @@ package org.egovframe.cloud.reservechecksevice.client.dto; +import java.math.BigDecimal; +import java.time.LocalDateTime; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; - +import org.egovframe.cloud.reservechecksevice.domain.ReserveItem; import org.egovframe.cloud.reservechecksevice.domain.location.Location; -import org.egovframe.cloud.reservechecksevice.domain.reserve.ReserveItem; - -import java.math.BigDecimal; -import java.time.LocalDateTime; /** * org.egovframe.cloud.reservechecksevice.client.dto.ReserveItemRelationResponseDto diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/client/dto/ReserveItemResponseDto.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/client/dto/ReserveItemResponseDto.java index b68c3a7..f1df3c2 100644 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/client/dto/ReserveItemResponseDto.java +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/client/dto/ReserveItemResponseDto.java @@ -1,13 +1,12 @@ package org.egovframe.cloud.reservechecksevice.client.dto; +import java.math.BigDecimal; +import java.time.LocalDateTime; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; -import org.egovframe.cloud.reservechecksevice.domain.reserve.ReserveItem; - -import java.math.BigDecimal; -import java.time.LocalDateTime; +import org.egovframe.cloud.reservechecksevice.domain.ReserveItem; /** * org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemResponseDto @@ -30,6 +29,8 @@ import java.time.LocalDateTime; @NoArgsConstructor @ToString public class ReserveItemResponseDto { + private static final int MIN_QTY = 0; + private Long reserveItemId; // 예약 물품 id private String reserveItemName; //예약 물품 명 private Long locationId; @@ -90,4 +91,16 @@ public class ReserveItemResponseDto { this.managerName = reserveItem.getManagerName(); this.managerContact = reserveItem.getManagerContact(); } + + public boolean isPossibleQty(Integer max, Integer reserveQty) { + return (totalQty - max) >= reserveQty; + } + + public boolean isPositiveInventory() { + return inventoryQty > MIN_QTY; + } + + public boolean isPossibleInventoryQty(Integer reserveQty) { + return inventoryQty >= reserveQty; + } } diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/config/Resilience4JConfig.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/config/Resilience4JConfig.java index 89881a7..cb35abb 100644 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/config/Resilience4JConfig.java +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/config/Resilience4JConfig.java @@ -1,12 +1,10 @@ package org.egovframe.cloud.reservechecksevice.config; -import java.time.Duration; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; +import java.time.Duration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; /** * org.egovframe.cloud.portalservice.config.Resilience4JConfig diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/reserve/Category.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/Category.java similarity index 84% rename from backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/reserve/Category.java rename to backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/Category.java index 525f7ac..eabbbd4 100644 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/reserve/Category.java +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/Category.java @@ -1,10 +1,10 @@ -package org.egovframe.cloud.reservechecksevice.domain.reserve; +package org.egovframe.cloud.reservechecksevice.domain; import lombok.Getter; import lombok.RequiredArgsConstructor; /** - * org.egovframe.cloud.reservechecksevice.domain.reserve.Category + * org.egovframe.cloud.reservechecksevice.domain.Category * * 예약 유형 enum class * diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/reserve/Reserve.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/Reserve.java similarity index 83% rename from backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/reserve/Reserve.java rename to backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/Reserve.java index c258e9a..4deb7ef 100644 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/reserve/Reserve.java +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/Reserve.java @@ -1,21 +1,23 @@ -package org.egovframe.cloud.reservechecksevice.domain.reserve; +package org.egovframe.cloud.reservechecksevice.domain; -import lombok.*; +import java.time.LocalDateTime; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.With; +import org.egovframe.cloud.common.exception.BusinessMessageException; import org.egovframe.cloud.reactive.domain.BaseEntity; -import org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveUpdateRequestDto; +import org.egovframe.cloud.reservechecksevice.api.dto.ReserveUpdateRequestDto; import org.egovframe.cloud.reservechecksevice.client.dto.UserResponseDto; -import org.egovframe.cloud.reservechecksevice.domain.location.Location; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Transient; import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Table; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; -import java.time.LocalDateTime; - /** - * org.egovframe.cloud.reservechecksevice.domain.reserve.Reserve + * org.egovframe.cloud.reservechecksevice.domain.Reserve * * 예약 도메인 클래스 * @@ -112,6 +114,10 @@ public class Reserve extends BaseEntity { this.userEmail = userEmail; } + public boolean isReserveUser(String userId) { + return this.userId.equals(userId); + } + /** * 물품 정보 세팅 * @@ -203,4 +209,26 @@ public class Reserve extends BaseEntity { return this; } + + public boolean isDone() { + return ReserveStatus.DONE.isEquals(this.reserveStatusId); + } + + public Reserve updateStatusCancel(String reason, String errorMessage) { + if (isDone()) { + throw new BusinessMessageException(errorMessage); + } + + this.reserveStatusId = ReserveStatus.CANCEL.getKey(); + this.reasonCancelContent = reason; + return this; + } + + public boolean isEducation() { + return Category.EDUCATION.isEquals(this.getCategoryId()); + } + + public boolean isRequest() { + return ReserveStatus.REQUEST.isEquals(this.reserveStatusId); + } } diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/reserve/ReserveItem.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/ReserveItem.java similarity index 98% rename from backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/reserve/ReserveItem.java rename to backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/ReserveItem.java index fe43fc1..dcbcc26 100644 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/reserve/ReserveItem.java +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/ReserveItem.java @@ -1,6 +1,10 @@ -package org.egovframe.cloud.reservechecksevice.domain.reserve; +package org.egovframe.cloud.reservechecksevice.domain; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -10,12 +14,6 @@ import org.egovframe.cloud.reservechecksevice.domain.location.Location; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Transient; import org.springframework.data.relational.core.mapping.Column; -import org.springframework.data.relational.core.mapping.Table; - -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; -import java.math.BigDecimal; -import java.time.LocalDateTime; /** * org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/reserve/ReserveRepository.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/ReserveRepository.java similarity index 79% rename from backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/reserve/ReserveRepository.java rename to backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/ReserveRepository.java index 837aafa..23ce968 100644 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/reserve/ReserveRepository.java +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/ReserveRepository.java @@ -1,9 +1,9 @@ -package org.egovframe.cloud.reservechecksevice.domain.reserve; +package org.egovframe.cloud.reservechecksevice.domain; import org.springframework.data.r2dbc.repository.R2dbcRepository; /** - * org.egovframe.cloud.reservechecksevice.domain.reserve.ReserveRepository + * org.egovframe.cloud.reservechecksevice.domain.ReserveRepository * * 예약 도메인 Repository interface * diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/reserve/ReserveRepositoryCustom.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/ReserveRepositoryCustom.java similarity index 84% rename from backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/reserve/ReserveRepositoryCustom.java rename to backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/ReserveRepositoryCustom.java index 787dff9..192e058 100644 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/reserve/ReserveRepositoryCustom.java +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/ReserveRepositoryCustom.java @@ -1,15 +1,13 @@ -package org.egovframe.cloud.reservechecksevice.domain.reserve; +package org.egovframe.cloud.reservechecksevice.domain; -import org.egovframe.cloud.common.dto.RequestDto; -import org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveRequestDto; +import java.time.LocalDateTime; +import org.egovframe.cloud.reservechecksevice.api.dto.ReserveRequestDto; import org.springframework.data.domain.Pageable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import java.time.LocalDateTime; - /** - * org.egovframe.cloud.reservechecksevice.domain.reserve.ReserveRepositoryCustom + * org.egovframe.cloud.reservechecksevice.domain.ReserveRepositoryCustom * * 예약 도메인 custom Repository interface * diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/reserve/ReserveRepositoryImpl.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/ReserveRepositoryImpl.java similarity index 97% rename from backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/reserve/ReserveRepositoryImpl.java rename to backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/ReserveRepositoryImpl.java index 21fc308..bece766 100644 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/reserve/ReserveRepositoryImpl.java +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/ReserveRepositoryImpl.java @@ -1,14 +1,15 @@ -package org.egovframe.cloud.reservechecksevice.domain.reserve; +package org.egovframe.cloud.reservechecksevice.domain; -import static org.springframework.data.relational.core.query.Criteria.*; +import static org.springframework.data.relational.core.query.Criteria.where; -import java.time.LocalDate; +import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; +import io.github.resilience4j.reactor.circuitbreaker.operator.CircuitBreakerOperator; import java.time.LocalDateTime; -import java.time.LocalTime; import java.util.ArrayList; import java.util.List; - -import org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveRequestDto; +import lombok.RequiredArgsConstructor; +import org.egovframe.cloud.reservechecksevice.api.dto.ReserveRequestDto; import org.egovframe.cloud.reservechecksevice.client.ReserveItemServiceClient; import org.egovframe.cloud.reservechecksevice.client.UserServiceClient; import org.egovframe.cloud.reservechecksevice.client.dto.UserResponseDto; @@ -18,16 +19,11 @@ import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; import org.springframework.data.relational.core.query.Criteria; import org.springframework.data.relational.core.query.Query; import org.springframework.util.StringUtils; - -import io.github.resilience4j.circuitbreaker.CircuitBreaker; -import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; -import io.github.resilience4j.reactor.circuitbreaker.operator.CircuitBreakerOperator; -import lombok.RequiredArgsConstructor; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; /** - * org.egovframe.cloud.reservechecksevice.domain.reserve.ReserveRepositoryImpl + * org.egovframe.cloud.reservechecksevice.domain.ReserveRepositoryImpl * * 예약 도메인 custom repository 구현 클래스 * diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/reserve/ReserveStatus.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/ReserveStatus.java similarity index 85% rename from backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/reserve/ReserveStatus.java rename to backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/ReserveStatus.java index 9568c96..52a24a0 100644 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/reserve/ReserveStatus.java +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/ReserveStatus.java @@ -1,10 +1,10 @@ -package org.egovframe.cloud.reservechecksevice.domain.reserve; +package org.egovframe.cloud.reservechecksevice.domain; import lombok.Getter; import lombok.RequiredArgsConstructor; /** - * org.egovframe.cloud.reservechecksevice.domain.reserve.ReserveStatus + * org.egovframe.cloud.reservechecksevice.domain.ReserveStatus * * 예약 상태 enum class * diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/ReserveValidator.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/ReserveValidator.java new file mode 100644 index 0000000..b65d98a --- /dev/null +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/ReserveValidator.java @@ -0,0 +1,259 @@ +package org.egovframe.cloud.reservechecksevice.domain; + +import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; +import io.github.resilience4j.reactor.circuitbreaker.operator.CircuitBreakerOperator; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.stream.IntStream; +import javax.annotation.Resource; +import lombok.RequiredArgsConstructor; +import org.egovframe.cloud.common.exception.BusinessMessageException; +import org.egovframe.cloud.common.util.MessageUtil; +import org.egovframe.cloud.reservechecksevice.client.ReserveItemServiceClient; +import org.egovframe.cloud.reservechecksevice.client.dto.ReserveItemResponseDto; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@Component +@RequiredArgsConstructor +public class ReserveValidator { + private static final String CHECK_RESERVE_MEANS = "realtime"; + private static final String RESERVE_ITEM_CIRCUIT_BREAKER_NAME = "reserve-item"; + + @Resource( + name = "messageUtil" + ) + protected MessageUtil messageUtil; + + private final ReserveRepository reserveRepository; + private final ReserveItemServiceClient reserveItemServiceClient; + private final CircuitBreakerRegistry circuitBreakerRegistry; + + /** + * 공간 예약 시 예약 날짜에 다른 예약이 있는지 체크 + * + * @param reserveItem + * @param reserve + * @return + */ + public Mono checkSpace(ReserveItemResponseDto reserveItem, Reserve reserve) { + return this.checkReserveDate(reserveItem, reserve) + .flatMap(isValid -> reserveRepository.findAllByReserveDateWithoutSelfCount( + reserve.getReserveId(), + reserveItem.getReserveItemId(), + reserve.getReserveStartDate(), + reserve.getReserveEndDate()) + .flatMap(count -> { + if (count > 0) { + //"해당 날짜에는 예약할 수 없습니다." + return Mono.error(new BusinessMessageException(getMessage("valid.reserve_date"))); + } + return Mono.just(reserve); + }) + ); + } + + /** + * 장비 예약 시 예약 날짜에 예약 가능한 재고 체크 + * + * @param reserveItem + * @param reserve + * @return + */ + public Mono checkEquipment(ReserveItemResponseDto reserveItem, Reserve reserve) { + return this.checkReserveDate(reserveItem, reserve) + .flatMap(entity -> this.getMaxByReserveDateWithoutSelf( + entity.getReserveId(), + reserveItem.getReserveItemId(), + entity.getReserveStartDate(), + entity.getReserveEndDate()) + .flatMap(max -> Mono.just((reserveItem.isPossibleQty(max, reserve.getReserveQty())))) + .flatMap(isValid -> { + if (isValid) { + return Mono.just(reserve); + } + //해당 날짜에 예약할 수 있는 재고수량이 없습니다. + return Mono.error(new BusinessMessageException(getMessage("valid.reserve_count"))); + }) + ); + } + + /** + * 교육 예약 시 재고 체크 + * + * @param reserveItem + * @param reserve + * @return + */ + public Mono checkEducation(ReserveItemResponseDto reserveItem, Reserve reserve) { + return Mono.just(reserveItem) + .flatMap(reserveItemResponseDto -> { + LocalDateTime now = LocalDateTime.now(); + LocalDateTime startDate = reserveItemResponseDto.getReserveMeansId().equals(CHECK_RESERVE_MEANS) ? + reserveItemResponseDto.getRequestStartDate() : reserveItemResponseDto.getOperationStartDate(); + LocalDateTime endDate = reserveItemResponseDto.getReserveMeansId().equals(CHECK_RESERVE_MEANS) ? + reserveItemResponseDto.getRequestEndDate() : reserveItemResponseDto.getOperationEndDate(); + + if (!(now.isAfter(startDate) && now.isBefore(endDate))) { + //해당 날짜에는 예약할 수 없습니다. + return Mono.error(new BusinessMessageException(getMessage("valid.reserve_date"))); + } + + if (!reserveItemResponseDto.isPositiveInventory()) { + //"예약이 마감되었습니다." + return Mono.error(new BusinessMessageException(getMessage("valid.reserve_close"))); + } + + if (reserveItemResponseDto.isPossibleInventoryQty(reserve.getReserveQty())) { + //예약가능한 인원이 부족합니다. (남은 인원 : {0}) + return Mono.error(new BusinessMessageException(getMessage("valid.reserve_number_of_people", new Object[]{reserveItemResponseDto.getInventoryQty()}))); + } + return Mono.just(reserve); + }); + } + + /** + * 예약물품에 대해 날짜별 예약된 수량 max 조회 + * + * @param reserveItemId + * @param startDate + * @param endDate + * @return + */ + public Mono getMaxByReserveDate(Long reserveItemId, LocalDateTime startDate, LocalDateTime endDate) { + Flux reserveFlux = reserveRepository.findAllByReserveDate(reserveItemId, startDate, endDate) + .switchIfEmpty(Flux.empty()); + return countMax(reserveFlux, startDate, endDate); + } + + /** + * 예약 물품 재고 및 예약 일자 체크 + * + * @param reserve + * @return + */ + public Mono checkReserveItems(Reserve reserve) { + return reserveItemServiceClient.findById(reserve.getReserveItemId()) + .transform(CircuitBreakerOperator + .of(circuitBreakerRegistry.circuitBreaker(RESERVE_ITEM_CIRCUIT_BREAKER_NAME))) + .onErrorResume(throwable -> Mono.empty()) + .flatMap(reserveItemResponseDto -> { + // validation check + if (Category.SPACE.isEquals(reserveItemResponseDto.getCategoryId())) { + return checkSpace(reserveItemResponseDto, reserve); + } + if (Category.EQUIPMENT.isEquals(reserveItemResponseDto.getCategoryId())) { + return checkEquipment(reserveItemResponseDto, reserve); + } + if (Category.EDUCATION.isEquals(reserveItemResponseDto.getCategoryId())) { + return checkEducation(reserveItemResponseDto, reserve); + } + return Mono.just(reserve); + }); + } + + + /** + * 예약 날짜 validation + * + * @param reserveItem + * @param reserve + * @return + */ + private Mono checkReserveDate(ReserveItemResponseDto reserveItem, Reserve reserve) { + LocalDateTime startDate = reserveItem.getReserveMeansId().equals(CHECK_RESERVE_MEANS) ? + reserveItem.getRequestStartDate() : reserveItem.getOperationStartDate(); + LocalDateTime endDate = reserveItem.getReserveMeansId().equals(CHECK_RESERVE_MEANS) ? + reserveItem.getRequestEndDate() : reserveItem.getOperationEndDate(); + + if (reserve.getReserveStartDate().isBefore(startDate)) { + //{0}이 {1} 보다 빠릅니다. 시작일, 운영/예약 시작일 + return Mono.error(new BusinessMessageException(getMessage("valid.to_be_fast.format", new Object[]{getMessage("common.start_date"), + getMessage("reserve_item.operation")+getMessage("reserve")+" "+getMessage("common.start_date")}))); + } + + if (reserve.getReserveEndDate().isAfter(endDate)) { + //{0}이 {1} 보다 늦습니다. 종료일, 운영/예약 종료일 + return Mono.error(new BusinessMessageException(getMessage("valid.to_be_slow.format", new Object[]{getMessage("common.end_date"), + getMessage("reserve_item.operation")+getMessage("reserve")+" "+getMessage("common.end_date")}))); + } + + if (reserveItem.getIsPeriod()) { + long between = ChronoUnit.DAYS.between(reserve.getReserveStartDate(), reserve.getReserveEndDate()); + if (reserveItem.getPeriodMaxCount() < between) { + //최대 예약 가능 일수보다 예약기간이 깁니다. (최대 예약 가능일 수 : {0}) + return Mono.error(new BusinessMessageException(getMessage("valid.reserve_period", new Object[]{reserveItem.getPeriodMaxCount()}))); + } + } + + return Mono.just(reserve); + } + + /** + * 예약물품에 대해 날짜별 예약된 수량 max 조회 + * 현 예약 건 제외 + * + * @param reserveItemId + * @param startDate + * @param endDate + * @return + */ + private Mono getMaxByReserveDateWithoutSelf(String reserveId, Long reserveItemId, LocalDateTime startDate, LocalDateTime endDate) { + Flux reserveFlux = reserveRepository.findAllByReserveDateWithoutSelf(reserveId, reserveItemId, startDate, endDate) + .switchIfEmpty(Flux.empty()); + return countMax(reserveFlux, startDate, endDate); + } + + /** + * get max + * + * @param reserveFlux + * @param startDate + * @param endDate + * @return + */ + private Mono countMax(Flux reserveFlux, LocalDateTime startDate, LocalDateTime endDate) { + if (reserveFlux.equals(Flux.empty())) { + return Mono.just(0); + } + + long between = ChronoUnit.DAYS.between(startDate, endDate); + + if (between == 0) { + return reserveFlux.map(reserve -> { + if (startDate.isAfter(reserve.getReserveStartDate()) + || startDate.isBefore(reserve.getReserveEndDate()) + || startDate.isEqual(reserve.getReserveStartDate()) || startDate.isEqual(reserve.getReserveEndDate())) { + return reserve.getReserveQty(); + } + return 0; + }).reduce(0, (x1, x2) -> x1 + x2); + } + + return Flux.fromStream(IntStream.iterate(0, i -> i + 1) + .limit(between) + .mapToObj(i -> startDate.plusDays(i))) + .flatMap(localDateTime -> + reserveFlux.map(findReserve -> { + if (localDateTime.isAfter(findReserve.getReserveStartDate()) + || localDateTime.isBefore(findReserve.getReserveEndDate()) + || localDateTime.isEqual(findReserve.getReserveStartDate()) || localDateTime.isEqual(findReserve.getReserveEndDate())) { + return findReserve.getReserveQty(); + } + return 0; + }).reduce(0, (x1, x2) -> x1 + x2)) + .groupBy(integer -> integer) + .flatMap(group -> group.reduce((x1,x2) -> x1 > x2?x1:x2)) + .last(0); + } + + private String getMessage(String code) { + return this.messageUtil.getMessage(code); + } + + private String getMessage(String code, Object[] args) { + return this.messageUtil.getMessage(code, args); + } + +} diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/location/Location.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/location/Location.java index f9001e8..a100242 100644 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/location/Location.java +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/domain/location/Location.java @@ -1,12 +1,13 @@ package org.egovframe.cloud.reservechecksevice.domain.location; -import lombok.*; +import javax.validation.constraints.Size; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; import org.egovframe.cloud.reactive.domain.BaseEntity; import org.springframework.data.annotation.Id; import org.springframework.data.relational.core.mapping.Column; -import org.springframework.data.relational.core.mapping.Table; - -import javax.validation.constraints.Size; /** * org.egovframe.cloud.reserveitemservice.domain.location.Location diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/service/ReserveService.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/service/ReserveService.java new file mode 100644 index 0000000..2372e88 --- /dev/null +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/service/ReserveService.java @@ -0,0 +1,411 @@ +package org.egovframe.cloud.reservechecksevice.service; + +import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; +import io.github.resilience4j.reactor.circuitbreaker.operator.CircuitBreakerOperator; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.egovframe.cloud.common.domain.Role; +import org.egovframe.cloud.common.dto.AttachmentEntityMessage; +import org.egovframe.cloud.common.exception.BusinessMessageException; +import org.egovframe.cloud.reactive.service.ReactiveAbstractService; +import org.egovframe.cloud.reservechecksevice.api.dto.ReserveCancelRequestDto; +import org.egovframe.cloud.reservechecksevice.api.dto.ReserveListResponseDto; +import org.egovframe.cloud.reservechecksevice.api.dto.ReserveRequestDto; +import org.egovframe.cloud.reservechecksevice.api.dto.ReserveResponseDto; +import org.egovframe.cloud.reservechecksevice.api.dto.ReserveSaveRequestDto; +import org.egovframe.cloud.reservechecksevice.api.dto.ReserveUpdateRequestDto; +import org.egovframe.cloud.reservechecksevice.client.ReserveItemServiceClient; +import org.egovframe.cloud.reservechecksevice.domain.Reserve; +import org.egovframe.cloud.reservechecksevice.domain.ReserveRepository; +import org.egovframe.cloud.reservechecksevice.domain.ReserveStatus; +import org.egovframe.cloud.reservechecksevice.domain.ReserveValidator; +import org.springframework.cloud.stream.function.StreamBridge; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.ReactiveSecurityContextHolder; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +/** + * org.egovframe.cloud.reservechecksevice.service.ReserveService + *

+ * 예약 service 클래스 + * + * @author 표준프레임워크센터 shinmj + * @version 1.0 + * @since 2021/09/15 + * + *

+ * << 개정이력(Modification Information) >>
+ *
+ *     수정일        수정자           수정내용
+ *  ----------    --------    ---------------------------
+ *  2021/09/15    shinmj       최초 생성
+ * 
+ */ +@Slf4j +@RequiredArgsConstructor +@Transactional +@Service +public class ReserveService extends ReactiveAbstractService { + + private static final String RESERVE_ITEM_CIRCUIT_BREAKER_NAME = "reserve-item"; + + private final ReserveRepository reserveRepository; + private final ReserveItemServiceClient reserveItemServiceClient; + private final CircuitBreakerRegistry circuitBreakerRegistry; + private final StreamBridge streamBridge; + private final ReserveValidator validator; + + /** + * 목록 조회 + * + * @param requestDto + * @param pageable + * @return + */ + @Transactional(readOnly = true) + public Mono> search(ReserveRequestDto requestDto, + Pageable pageable) { + return reserveRepository.search(requestDto, pageable) + .switchIfEmpty(Flux.empty()) + .flatMap(this::convertReserveListResponseDto) + .collectList() + .zipWith(reserveRepository.searchCount(requestDto, pageable)) + .flatMap(tuple -> Mono.just(new PageImpl<>(tuple.getT1(), pageable, tuple.getT2()))); + } + + /** + * 한건 조회 dto return + * + * @param reserveId + * @return + */ + @Transactional(readOnly = true) + public Mono findReserveById(String reserveId) { + return reserveRepository.findReserveById(reserveId) + .switchIfEmpty(monoResponseStatusEntityNotFoundException(reserveId)) + .flatMap(this::convertReserveResponseDto); + } + + /** + * 사용자용 예약 목록 조회 (로그인 사용자의 예약정보만 조회) + * + * @param userId + * @param requestDto + * @param pageable + * @return + */ + @Transactional(readOnly = true) + public Mono> searchForUser(String userId, + ReserveRequestDto requestDto, Pageable pageable) { + return reserveRepository.searchForUser(requestDto, pageable, userId) + .switchIfEmpty(Flux.empty()) + .flatMap(this::convertReserveListResponseDto) + .collectList() + .zipWith(reserveRepository.searchCountForUser(requestDto, pageable, userId)) + .flatMap(tuple -> Mono.just(new PageImpl<>(tuple.getT1(), pageable, tuple.getT2()))); + } + + /** + * 예약 정보 취소 + * + * @param reserveId + * @param cancelRequestDto + * @return + */ + public Mono cancel(String reserveId, ReserveCancelRequestDto cancelRequestDto) { + return getIsAdmin().flatMap(isAdmin -> { + if (isAdmin) { + return reserveCancel(reserveId, cancelRequestDto); + } + return findById(reserveId) + .zipWith(getUserId()) + .flatMap(tuple -> { + if (tuple.getT1().isReserveUser(tuple.getT2())) { + return Mono.just(tuple.getT1()); + } + //해당 예약은 취소할 수 없습니다. + return Mono + .error(new BusinessMessageException(getMessage("valid.cant_cancel"))); + }) + .onErrorResume(throwable -> Mono.error(throwable)) + .flatMap(reserve -> reserveCancel(reserveId, cancelRequestDto)); + }); + + } + + /** + * 예약 상태 취소로 변경 + * + * @param reserveId + * @param cancelRequestDto + * @return + */ + private Mono reserveCancel(String reserveId, ReserveCancelRequestDto cancelRequestDto) { + return findById(reserveId) + .map(reserve -> + reserve.updateStatusCancel(cancelRequestDto.getReasonCancelContent(), getMessage("valid.cant_cancel_because_done"))) + .flatMap(reserve -> Mono.just(reserve.conversionReserveQty())) + .flatMap(this::updateInventory) + .onErrorResume(throwable -> Mono.error(throwable)) + .flatMap(reserve -> Mono.just(reserve.conversionReserveQty())) + .flatMap(reserveRepository::save) + .then(); + } + + /** + * 예약 정보 승인 + * + * @param reserveId + * @return + */ + public Mono approve(String reserveId) { + return getIsAdmin() + .flatMap(isAdmin -> { + if (isAdmin) { + return Mono.just(reserveId); + } + //관리자만 승인할 수 있습니다. + return Mono.error(new BusinessMessageException(getMessage("valid.manager_approve"))); + }) + .onErrorResume(Mono::error) + .flatMap(this::checkApprove) + .onErrorResume(Mono::error) + .flatMap(reserveRepository::save).then(); + } + + /** + * 예약 정보 수정 + * + * @param reserveId + * @return + */ + public Mono update(String reserveId, ReserveUpdateRequestDto updateRequestDto) { + return getIsAdmin().flatMap(isAdmin -> { + if (isAdmin) { + return updateReserve(reserveId, updateRequestDto); + } + return updateReserveForUser(reserveId, updateRequestDto); + }); + } + + /** + * 관리자 예약 신청 관리자의 경우 실시간이어도 이벤트 스트림 거치지 않고 바로 예약 처리 + * + * @param saveRequestDto + * @return + */ + public Mono create(ReserveSaveRequestDto saveRequestDto) { + return Mono.just(saveRequestDto) + .map(ReserveSaveRequestDto::createNewReserve) + .zipWith(getUserId()) + .flatMap(tuple -> Mono.just(tuple.getT1().setCreatedInfo(LocalDateTime.now(), tuple.getT2()))) + .flatMap(validator::checkReserveItems) + .onErrorResume(Mono::error) + .flatMap(this::updateInventory) + .onErrorResume(Mono::error) + .flatMap(reserveRepository::insert) + .flatMap(reserveRepository::loadRelations) + .doOnNext(reserve -> sendAttachmentEntityInfo(streamBridge, + AttachmentEntityMessage.builder() + .attachmentCode(reserve.getAttachmentCode()) + .entityName(reserve.getClass().getName()) + .entityId(reserve.getReserveId()) + .build())) + .flatMap(this::convertReserveResponseDto); + + + } + + /** + * 예약 물품별 기간안에 있는 예약된 수량 max 조회 + * + * @param reserveItemId + * @param startDate + * @param endDate + * @return + */ + public Mono countInventory(Long reserveItemId, LocalDateTime startDate, + LocalDateTime endDate) { + return reserveItemServiceClient.findById(reserveItemId) + .transform(CircuitBreakerOperator.of(circuitBreakerRegistry.circuitBreaker(RESERVE_ITEM_CIRCUIT_BREAKER_NAME))) + .onErrorResume(throwable -> Mono.empty()) + .zipWith(validator.getMaxByReserveDate(reserveItemId, startDate, endDate)) + .flatMap(tuple -> Mono.just(tuple.getT1().getTotalQty() - tuple.getT2())); + } + + /** + * 승인 전 validate check 및 교육인 경우 재고 업데이트 + * + * @param reserveId + * @return + */ + private Mono checkApprove(String reserveId) { + return findById(reserveId) + .flatMap(validator::checkReserveItems) + .onErrorResume(Mono::error) + .map(reserve -> reserve.updateStatus(ReserveStatus.APPROVE.getKey())) + .flatMap(this::updateInventory); + } + + /** + * 사용자 예약 수정 + * + * @param reserveId + * @param updateRequestDto + * @return + */ + private Mono updateReserveForUser(String reserveId, + ReserveUpdateRequestDto updateRequestDto) { + return findById(reserveId) + .zipWith(getUserId()) + .map(tuple -> { + if (!tuple.getT1().isReserveUser(tuple.getT2())) { + //"해당 예약은 수정할 수 없습니다." + throw new BusinessMessageException(getMessage("valid.reserve_not_update")); + } + + if (!tuple.getT1().isRequest()) { + //예약 신청 상태인 경우에만 수정 가능합니다. + throw new BusinessMessageException(getMessage("valid.reserve_not_update_status")); + } + + return tuple.getT1().update(updateRequestDto); + }) + .flatMap(validator::checkReserveItems) + .onErrorResume(Mono::error) + .flatMap(this::updateInventory) + .onErrorResume(Mono::error) + .flatMap(reserveRepository::save); + } + + /** + * 관리자 예약 수정 + * + * @param reserveId + * @param updateRequestDto + * @return + */ + private Mono updateReserve(String reserveId, + ReserveUpdateRequestDto updateRequestDto) { + return findById(reserveId) + .map(reserve -> { + if (!reserve.isRequest()) { + //예약 신청 상태인 경우에만 수정 가능합니다. + throw new BusinessMessageException( + getMessage("valid.reserve_not_update_status")); + } + return reserve.update(updateRequestDto); + }) + .flatMap(validator::checkReserveItems) + .onErrorResume(Mono::error) + .flatMap(this::updateInventory) + .onErrorResume(Mono::error) + .flatMap(reserveRepository::save); + } + + /** + * 예약 정보 저장 시 재고 변경 + * + * @param reserve + * @return + */ + private Mono updateInventory(Reserve reserve) { + return Mono.just(reserve) + .flatMap(it -> { + if (it.isEducation()) { + return reserveItemServiceClient + .updateInventory(reserve.getReserveItemId(), reserve.getReserveQty()) + .transform(CircuitBreakerOperator.of(circuitBreakerRegistry + .circuitBreaker(RESERVE_ITEM_CIRCUIT_BREAKER_NAME))) + .onErrorResume(throwable -> Mono.just(false)) + .flatMap(isSuccess -> { + if (isSuccess) { + return Mono.just(reserve); + } + //재고 업데이트에 실패했습니다. + return Mono.error(new BusinessMessageException(getMessage("msg.inventory_failed"))); + }); + } + return Mono.just(it); + }); + } + + /** + * 한건 정보 조회 entity return + * + * @param reserveId + * @return + */ + private Mono findById(String reserveId) { + return reserveRepository.findById(reserveId) + .switchIfEmpty(monoResponseStatusEntityNotFoundException(reserveId)); + } + + /** + * entity -> dto 변환 + * + * @param reserve + * @return + */ + private Mono convertReserveResponseDto(Reserve reserve) { + return Mono.just(ReserveResponseDto.builder() + .entity(reserve) + .build()); + } + + /** + * entity -> 목록 dto 변환 + * + * @param reserve + * @return + */ + private Mono convertReserveListResponseDto(Reserve reserve) { + return Mono.just(ReserveListResponseDto.builder() + .entity(reserve) + .build()); + } + + /** + * 현재 로그인 사용자가 관리자인지 체크 + * + * @return + */ + private Mono getIsAdmin() { + return ReactiveSecurityContextHolder.getContext() + .map(SecurityContext::getAuthentication) + .filter(Authentication::isAuthenticated) + .map(Authentication::getAuthorities) + .map(grantedAuthorities -> { + List authorities = + new ArrayList<>((Collection) grantedAuthorities); + SimpleGrantedAuthority adminRole = new SimpleGrantedAuthority(Role.ADMIN.getKey()); + return authorities.contains(adminRole); + }); + } + + /** + * 현재 로그인 사용자 id + * + * @return + */ + private Mono getUserId() { + return ReactiveSecurityContextHolder.getContext() + .map(SecurityContext::getAuthentication) + .filter(Authentication::isAuthenticated) + .map(Authentication::getPrincipal) + .map(String.class::cast); + } + +} diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/service/reserve/ReserveService.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/service/reserve/ReserveService.java deleted file mode 100644 index ee29be4..0000000 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/service/reserve/ReserveService.java +++ /dev/null @@ -1,640 +0,0 @@ -package org.egovframe.cloud.reservechecksevice.service.reserve; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.UUID; -import java.util.stream.IntStream; - -import org.egovframe.cloud.common.domain.Role; -import org.egovframe.cloud.common.dto.AttachmentEntityMessage; -import org.egovframe.cloud.common.exception.BusinessMessageException; -import org.egovframe.cloud.reactive.service.ReactiveAbstractService; -import org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveCancelRequestDto; -import org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveListResponseDto; -import org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveRequestDto; -import org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveResponseDto; -import org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveSaveRequestDto; -import org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveUpdateRequestDto; -import org.egovframe.cloud.reservechecksevice.client.ReserveItemServiceClient; -import org.egovframe.cloud.reservechecksevice.client.dto.ReserveItemResponseDto; -import org.egovframe.cloud.reservechecksevice.domain.reserve.Category; -import org.egovframe.cloud.reservechecksevice.domain.reserve.Reserve; -import org.egovframe.cloud.reservechecksevice.domain.reserve.ReserveRepository; -import org.egovframe.cloud.reservechecksevice.domain.reserve.ReserveStatus; -import org.springframework.cloud.stream.function.StreamBridge; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.Pageable; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.context.ReactiveSecurityContextHolder; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; -import io.github.resilience4j.reactor.circuitbreaker.operator.CircuitBreakerOperator; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -/** - * org.egovframe.cloud.reservechecksevice.service.reserve.ReserveService - * - * 예약 service 클래스 - * - * @author 표준프레임워크센터 shinmj - * @version 1.0 - * @since 2021/09/15 - * - *
- * << 개정이력(Modification Information) >>
- *
- *     수정일        수정자           수정내용
- *  ----------    --------    ---------------------------
- *  2021/09/15    shinmj       최초 생성
- * 
- */ -@Slf4j -@RequiredArgsConstructor -@Transactional -@Service -public class ReserveService extends ReactiveAbstractService { - private static final String RESERVE_ITEM_CIRCUIT_BREAKER_NAME = "reserve-item"; - private static final String CHECK_RESERVE_MEANS = "realtime"; - - private final ReserveRepository reserveRepository; - private final ReserveItemServiceClient reserveItemServiceClient; - private final CircuitBreakerRegistry circuitBreakerRegistry; - private final StreamBridge streamBridge; - - /** - * entity -> dto 변환 - * - * @param reserve - * @return - */ - private Mono convertReserveResponseDto(Reserve reserve) { - return Mono.just(ReserveResponseDto.builder() - .entity(reserve) - .build()); - } - - /** - * entity -> 목록 dto 변환 - * - * @param reserve - * @return - */ - private Mono convertReserveListResponseDto(Reserve reserve) { - return Mono.just(ReserveListResponseDto.builder() - .entity(reserve) - .build()); - } - - /** - * 현재 로그인 사용자가 관리자인지 체크 - * - * @return - */ - private Mono getIsAdmin() { - return ReactiveSecurityContextHolder.getContext() - .map(SecurityContext::getAuthentication) - .filter(Authentication::isAuthenticated) - .map(Authentication::getAuthorities) - .map(grantedAuthorities -> { - List authorities = - new ArrayList<>((Collection) grantedAuthorities); - SimpleGrantedAuthority adminRole = new SimpleGrantedAuthority(Role.ADMIN.getKey()); - return authorities.contains(adminRole); - }); - } - - /** - * 현재 로그인 사용자 id - * - * @return - */ - private Mono getUserId() { - return ReactiveSecurityContextHolder.getContext() - .map(SecurityContext::getAuthentication) - .filter(Authentication::isAuthenticated) - .map(Authentication::getPrincipal) - .map(String.class::cast); - } - - /** - * 목록 조회 - * - * @param requestDto - * @param pageable - * @return - */ - @Transactional(readOnly = true) - public Mono> search(ReserveRequestDto requestDto, Pageable pageable) { - return reserveRepository.search(requestDto, pageable) - .switchIfEmpty(Flux.empty()) - .flatMap(this::convertReserveListResponseDto) - .collectList() - .zipWith(reserveRepository.searchCount(requestDto, pageable)) - .flatMap(tuple -> Mono.just(new PageImpl<>(tuple.getT1(), pageable, tuple.getT2()))); - } - - /** - * 한건 조회 dto return - * - * @param reserveId - * @return - */ - @Transactional(readOnly = true) - public Mono findReserveById(String reserveId) { - return reserveRepository.findReserveById(reserveId) - .switchIfEmpty(monoResponseStatusEntityNotFoundException(reserveId)) - .flatMap(this::convertReserveResponseDto); - } - - /** - * 사용자용 예약 목록 조회 (로그인 사용자의 예약정보만 조회) - * - * @param userId - * @param requestDto - * @param pageable - * @return - */ - @Transactional(readOnly = true) - public Mono> searchForUser(String userId, ReserveRequestDto requestDto, Pageable pageable) { - return reserveRepository.searchForUser(requestDto, pageable, userId) - .switchIfEmpty(Flux.empty()) - .flatMap(this::convertReserveListResponseDto) - .collectList() - .zipWith(reserveRepository.searchCountForUser(requestDto, pageable, userId)) - .flatMap(tuple -> Mono.just(new PageImpl<>(tuple.getT1(), pageable, tuple.getT2()))); - } - - /** - * 예약 정보 취소 - * - * @param reserveId - * @param cancelRequestDto - * @return - */ - public Mono cancel(String reserveId, ReserveCancelRequestDto cancelRequestDto) { - return getIsAdmin().flatMap(isAdmin -> { - if (isAdmin) { - return reserveCancel(reserveId, cancelRequestDto); - } - return findById(reserveId) - .zipWith(getUserId()) - .flatMap(tuple -> { - if (tuple.getT1().getUserId().equals(tuple.getT2())) { - return Mono.just(tuple.getT1()); - } - //해당 예약은 취소할 수 없습니다. - return Mono.error(new BusinessMessageException(getMessage("valid.cant_cancel"))); - }) - .onErrorResume(throwable -> Mono.error(throwable)) - .flatMap(reserve -> reserveCancel(reserveId, cancelRequestDto)); - }); - - } - - /** - * 예약 상태 취소로 변경 - * - * @param reserveId - * @param cancelRequestDto - * @return - */ - private Mono reserveCancel(String reserveId, ReserveCancelRequestDto cancelRequestDto) { - return findById(reserveId) - .map(reserve -> { - if (ReserveStatus.DONE.isEquals(reserve.getReserveStatusId())) { - //해당 예약은 이미 실행되어 취소할 수 없습니다. - throw new BusinessMessageException(getMessage("valid.cant_cancel_because_done")); - }else { - return reserve.updateStatus(ReserveStatus.CANCEL.getKey()) - .updateReasonCancel(cancelRequestDto.getReasonCancelContent()); - } - }) - .flatMap(reserve -> Mono.just(reserve.conversionReserveQty())) - .flatMap(this::updateInventory) - .onErrorResume(throwable -> Mono.error(throwable)) - .flatMap(reserve -> Mono.just(reserve.conversionReserveQty())) - .flatMap(reserveRepository::save) - .then(); - } - - /** - * 예약 정보 승인 - * - * @param reserveId - * @return - */ - public Mono approve(String reserveId) { - return getIsAdmin() - .flatMap(isAdmin -> { - if (isAdmin) { - return Mono.just(reserveId); - } - //관리자만 승인할 수 있습니다. - return Mono.error(new BusinessMessageException(getMessage("valid.manager_approve"))); - }) - .onErrorResume(throwable -> Mono.error(throwable)) - .flatMap(this::checkApprove) - .onErrorResume(throwable -> Mono.error(throwable)) - .flatMap(reserveRepository::save).then(); - } - - /** - * 승인 전 validate check 및 교육인 경우 재고 업데이트 - * - * @param reserveId - * @return - */ - private Mono checkApprove(String reserveId) { - return findById(reserveId) - .flatMap(this::checkReserveItems) - .onErrorResume(throwable -> Mono.error(throwable)) - .map(reserve -> reserve.updateStatus(ReserveStatus.APPROVE.getKey())) - .flatMap(this::updateInventory); - } - - /** - * 예약 물품 재고 및 예약 일자 체크 - * - * @param reserve - * @return - */ - private Mono checkReserveItems(Reserve reserve) { - return reserveItemServiceClient.findById(reserve.getReserveItemId()) - .transform(CircuitBreakerOperator.of(circuitBreakerRegistry.circuitBreaker(RESERVE_ITEM_CIRCUIT_BREAKER_NAME))) - .onErrorResume(throwable -> Mono.empty()) - .flatMap(reserveItemResponseDto -> { - // validation check - if (Category.SPACE.isEquals(reserveItemResponseDto.getCategoryId())) { - return this.checkSpace(reserveItemResponseDto, reserve); - }else if (Category.EQUIPMENT.isEquals(reserveItemResponseDto.getCategoryId())) { - return this.checkEquipment(reserveItemResponseDto, reserve); - }else if (Category.EDUCATION.isEquals(reserveItemResponseDto.getCategoryId())) { - return this.checkEducation(reserveItemResponseDto, reserve); - } - return Mono.just(reserve); - }); - } - - /** - * 예약 날짜 validation - * - * @param reserveItem - * @param reserve - * @return - */ - private Mono checkReserveDate(ReserveItemResponseDto reserveItem, Reserve reserve) { - LocalDateTime startDate = reserveItem.getReserveMeansId().equals(CHECK_RESERVE_MEANS) ? - reserveItem.getRequestStartDate() : reserveItem.getOperationStartDate(); - LocalDateTime endDate = reserveItem.getReserveMeansId().equals(CHECK_RESERVE_MEANS) ? - reserveItem.getRequestEndDate() : reserveItem.getOperationEndDate(); - - if (reserve.getReserveStartDate().isBefore(startDate)) { - //{0}이 {1} 보다 빠릅니다. 시작일, 운영/예약 시작일 - return Mono.error(new BusinessMessageException(getMessage("valid.to_be_fast.format", new Object[]{getMessage("common.start_date"), - getMessage("reserve_item.operation")+getMessage("reserve")+" "+getMessage("common.start_date")}))); - } - - if (reserve.getReserveEndDate().isAfter(endDate)) { - //{0}이 {1} 보다 늦습니다. 종료일, 운영/예약 종료일 - return Mono.error(new BusinessMessageException(getMessage("valid.to_be_slow.format", new Object[]{getMessage("common.end_date"), - getMessage("reserve_item.operation")+getMessage("reserve")+" "+getMessage("common.end_date")}))); - } - - if (reserveItem.getIsPeriod()) { - long between = ChronoUnit.DAYS.between(reserve.getReserveStartDate(), reserve.getReserveEndDate()); - if (reserveItem.getPeriodMaxCount() < between) { - //최대 예약 가능 일수보다 예약기간이 깁니다. (최대 예약 가능일 수 : {0}) - return Mono.error(new BusinessMessageException(getMessage("valid.reserve_period", new Object[]{reserveItem.getPeriodMaxCount()}))); - } - } - - return Mono.just(reserve); - } - - /** - * 공간 예약 시 예약 날짜에 다른 예약이 있는지 체크 - * - * @param reserveItem - * @param reserve - * @return - */ - private Mono checkSpace(ReserveItemResponseDto reserveItem, Reserve reserve) { - return this.checkReserveDate(reserveItem, reserve) - .flatMap(isValid -> reserveRepository.findAllByReserveDateWithoutSelfCount( - reserve.getReserveId(), - reserveItem.getReserveItemId(), - reserve.getReserveStartDate(), - reserve.getReserveEndDate()) - .flatMap(count -> { - if (count > 0) { - //"해당 날짜에는 예약할 수 없습니다." - return Mono.error(new BusinessMessageException(getMessage("valid.reserve_date"))); - } - return Mono.just(reserve); - }) - ); - } - - /** - * 장비 예약 시 예약 날짜에 예약 가능한 재고 체크 - * - * @param reserveItem - * @param reserve - * @return - */ - private Mono checkEquipment(ReserveItemResponseDto reserveItem, Reserve reserve) { - return this.checkReserveDate(reserveItem, reserve) - .flatMap(entity -> this.getMaxByReserveDateWithoutSelf( - entity.getReserveId(), - reserveItem.getReserveItemId(), - entity.getReserveStartDate(), - entity.getReserveEndDate()) - .flatMap(max -> { - if ((reserveItem.getTotalQty() - max) < reserve.getReserveQty()) { - return Mono.just(false); - } - return Mono.just(true); - }) - .flatMap(isValid -> { - if (!isValid) { - //해당 날짜에 예약할 수 있는 재고수량이 없습니다. - return Mono.error(new BusinessMessageException(getMessage("valid.reserve_count"))); - } - return Mono.just(reserve); - }) - ); - } - - /** - * 교육 예약 시 재고 체크 - * - * @param reserveItem - * @param reserve - * @return - */ - private Mono checkEducation(ReserveItemResponseDto reserveItem, Reserve reserve) { - return Mono.just(reserveItem) - .flatMap(reserveItemResponseDto -> { - LocalDateTime now = LocalDateTime.now(); - LocalDateTime startDate = reserveItemResponseDto.getReserveMeansId().equals(CHECK_RESERVE_MEANS) ? - reserveItemResponseDto.getRequestStartDate() : reserveItemResponseDto.getOperationStartDate(); - LocalDateTime endDate = reserveItemResponseDto.getReserveMeansId().equals(CHECK_RESERVE_MEANS) ? - reserveItemResponseDto.getRequestEndDate() : reserveItemResponseDto.getOperationEndDate(); - - if (!(now.isAfter(startDate) && now.isBefore(endDate))) { - //해당 날짜에는 예약할 수 없습니다. - return Mono.error(new BusinessMessageException(getMessage("valid.reserve_date"))); - } - - if (reserveItemResponseDto.getInventoryQty() <= 0) { - //"예약이 마감되었습니다." - return Mono.error(new BusinessMessageException(getMessage("valid.reserve_close"))); - } - - if (reserveItemResponseDto.getInventoryQty() < reserve.getReserveQty()) { - //예약가능한 인원이 부족합니다. (남은 인원 : {0}) - return Mono.error(new BusinessMessageException(getMessage("valid.reserve_number_of_people", new Object[]{reserveItemResponseDto.getInventoryQty()}))); - } - return Mono.just(reserve); - }); - } - - /** - * 예약 정보 수정 - * - * @param reserveId - * @return - */ - public Mono update(String reserveId, ReserveUpdateRequestDto updateRequestDto) { - return getIsAdmin().flatMap(isAdmin -> { - if (isAdmin) { - return updateReserve(reserveId, updateRequestDto); - } - return updateReserveForUser(reserveId, updateRequestDto); - }); - } - - /** - * 사용자 예약 수정 - * - * @param reserveId - * @param updateRequestDto - * @return - */ - private Mono updateReserveForUser(String reserveId, ReserveUpdateRequestDto updateRequestDto) { - return findById(reserveId) - .zipWith(getUserId()) - .map(tuple -> { - if (!tuple.getT1().getUserId().equals(tuple.getT2())) { - //"해당 예약은 수정할 수 없습니다." - throw new BusinessMessageException(getMessage("valid.reserve_not_update")); - } - - if (!ReserveStatus.REQUEST.getKey().equals(tuple.getT1().getReserveStatusId())) { - //예약 신청 상태인 경우에만 수정 가능합니다. - throw new BusinessMessageException(getMessage("valid.reserve_not_update_status")); - } - - return tuple.getT1().update(updateRequestDto); - }) - .flatMap(this::checkReserveItems) - .onErrorResume(throwable -> Mono.error(throwable)) - .flatMap(this::updateInventory) - .onErrorResume(throwable -> Mono.error(throwable)) - .flatMap(reserveRepository::save); - } - - /** - * 관리자 예약 수정 - * - * @param reserveId - * @param updateRequestDto - * @return - */ - private Mono updateReserve(String reserveId, ReserveUpdateRequestDto updateRequestDto) { - return findById(reserveId) - .map(reserve -> { - if (!ReserveStatus.REQUEST.getKey().equals(reserve.getReserveStatusId())) { - //예약 신청 상태인 경우에만 수정 가능합니다. - throw new BusinessMessageException(getMessage("valid.reserve_not_update_status")); - } - return reserve.update(updateRequestDto); - }) - .flatMap(this::checkReserveItems) - .onErrorResume(throwable -> Mono.error(throwable)) - .flatMap(this::updateInventory) - .onErrorResume(throwable -> Mono.error(throwable)) - .flatMap(reserveRepository::save); - } - - /** - * 한건 정보 조회 entity return - * - * @param reserveId - * @return - */ - private Mono findById(String reserveId) { - return reserveRepository.findById(reserveId) - .switchIfEmpty(monoResponseStatusEntityNotFoundException(reserveId)); - } - - /** - * 관리자 예약 신청 - * 관리자의 경우 실시간이어도 이벤트 스트림 거치지 않고 바로 예약 처리 - * - * @param saveRequestDto - * @return - */ - public Mono create(ReserveSaveRequestDto saveRequestDto) { - return Mono.just(saveRequestDto) - .map(dto -> { - String uuid = UUID.randomUUID().toString(); - dto.setReserveId(uuid); - return dto.toEntity(); - }) - .zipWith(getUserId()) - .flatMap(tuple -> Mono.just(tuple.getT1().setCreatedInfo(LocalDateTime.now(), tuple.getT2()))) - .flatMap(this::checkReserveItems) - .onErrorResume(throwable -> Mono.error(throwable)) - .flatMap(this::updateInventory) - .onErrorResume(throwable -> Mono.error(throwable)) - .flatMap(reserveRepository::insert) - .flatMap(reserveRepository::loadRelations) - .doOnNext(reserve -> sendAttachmentEntityInfo(streamBridge, - AttachmentEntityMessage.builder() - .attachmentCode(reserve.getAttachmentCode()) - .entityName(reserve.getClass().getName()) - .entityId(reserve.getReserveId()) - .build())) - .flatMap(this::convertReserveResponseDto); - - - } - - /** - * 예약 정보 저장 시 재고 변경 - * - * @param reserve - * @return - */ - private Mono updateInventory(Reserve reserve) { - return Mono.just(reserve) - .flatMap(reserve1 -> { - if (!Category.EDUCATION.isEquals(reserve1.getCategoryId())) { - return Mono.just(reserve1); - } - return reserveItemServiceClient.updateInventory(reserve.getReserveItemId(), reserve.getReserveQty()) - .transform(CircuitBreakerOperator.of(circuitBreakerRegistry.circuitBreaker(RESERVE_ITEM_CIRCUIT_BREAKER_NAME))) - .onErrorResume(throwable -> Mono.just(false)) - .flatMap(isSuccess -> { - if (isSuccess) { - return Mono.just(reserve); - } - //재고 업데이트에 실패했습니다. - return Mono.error(new BusinessMessageException(getMessage("msg.inventory_failed"))); - }); - }); - } - - /** - * 예약 물품별 기간안에 있는 예약된 수량 max 조회 - * - * @param reserveItemId - * @param startDate - * @param endDate - * @return - */ - public Mono countInventory(Long reserveItemId, LocalDateTime startDate, LocalDateTime endDate) { - return reserveItemServiceClient.findById(reserveItemId) - .transform(CircuitBreakerOperator.of(circuitBreakerRegistry.circuitBreaker(RESERVE_ITEM_CIRCUIT_BREAKER_NAME))) - .onErrorResume(throwable -> Mono.empty()) - .zipWith(getMaxByReserveDate(reserveItemId, startDate, endDate)) - .log("countinventory") - .flatMap(tuple -> Mono.just(tuple.getT1().getTotalQty() - tuple.getT2())); - } - - /** - * 예약물품에 대해 날짜별 예약된 수량 max 조회 - * 현 예약 건 제외 - * - * @param reserveItemId - * @param startDate - * @param endDate - * @return - */ - private Mono getMaxByReserveDateWithoutSelf(String reserveId, Long reserveItemId, LocalDateTime startDate, LocalDateTime endDate) { - Flux reserveFlux = reserveRepository.findAllByReserveDateWithoutSelf(reserveId, reserveItemId, startDate, endDate) - .switchIfEmpty(Flux.empty()); - return countMax(reserveFlux, startDate, endDate); - } - - /** - * 예약물품에 대해 날짜별 예약된 수량 max 조회 - * - * @param reserveItemId - * @param startDate - * @param endDate - * @return - */ - private Mono getMaxByReserveDate(Long reserveItemId, LocalDateTime startDate, LocalDateTime endDate) { - Flux reserveFlux = reserveRepository.findAllByReserveDate(reserveItemId, startDate, endDate) - .switchIfEmpty(Flux.empty()); - return countMax(reserveFlux, startDate, endDate); - } - - /** - * get max - * - * @param reserveFlux - * @param startDate - * @param endDate - * @return - */ - private Mono countMax(Flux reserveFlux, LocalDateTime startDate, LocalDateTime endDate) { - if (reserveFlux.equals(Flux.empty())) { - return Mono.just(0); - } - - long between = ChronoUnit.DAYS.between(startDate, endDate); - - if (between == 0) { - return reserveFlux.map(reserve -> { - if (startDate.isAfter(reserve.getReserveStartDate()) - || startDate.isBefore(reserve.getReserveEndDate()) - || startDate.isEqual(reserve.getReserveStartDate()) || startDate.isEqual(reserve.getReserveEndDate())) { - return reserve.getReserveQty(); - } - return 0; - }).reduce(0, (x1, x2) -> x1 + x2); - } - - return Flux.fromStream(IntStream.iterate(0, i -> i + 1) - .limit(between) - .mapToObj(i -> startDate.plusDays(i))) - .flatMap(localDateTime -> - reserveFlux.map(findReserve -> { - if (localDateTime.isAfter(findReserve.getReserveStartDate()) - || localDateTime.isBefore(findReserve.getReserveEndDate()) - || localDateTime.isEqual(findReserve.getReserveStartDate()) || localDateTime.isEqual(findReserve.getReserveEndDate())) { - return findReserve.getReserveQty(); - } - return 0; - }).reduce(0, (x1, x2) -> x1 + x2)) - .groupBy(integer -> integer) - .flatMap(group -> group.reduce((x1,x2) -> x1 > x2?x1:x2)) - .last(0); - } - -} diff --git a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/validator/ReserveSaveValidator.java b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/validator/ReserveSaveValidator.java index dd673fa..ca47911 100644 --- a/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/validator/ReserveSaveValidator.java +++ b/backend/reserve-check-service/src/main/java/org/egovframe/cloud/reservechecksevice/validator/ReserveSaveValidator.java @@ -2,22 +2,19 @@ package org.egovframe.cloud.reservechecksevice.validator; import java.lang.reflect.Field; import java.time.LocalDateTime; - import javax.annotation.Resource; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; - +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; import org.egovframe.cloud.common.util.MessageUtil; import org.egovframe.cloud.reservechecksevice.validator.annotation.ReserveSaveValid; import org.springframework.util.StringUtils; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; - /** * org.egovframe.cloud.reservechecksevice.validator.ReserveSaveValidator - * + *

* 예약 신청 시 validation check를 하기 위한 custom validator * * @author 표준프레임워크센터 shinmj @@ -42,6 +39,7 @@ public class ReserveSaveValidator implements ConstraintValidator clazz = object.getClass(); Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); @@ -167,10 +182,12 @@ public class ReserveSaveValidator implements ConstraintValidator clazz = object.getClass(); Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); - return field.get(object) == null || !StringUtils.hasLength(String.valueOf(field.get(object))); + return field.get(object) == null || !StringUtils + .hasLength(String.valueOf(field.get(object))); } } diff --git a/backend/reserve-check-service/src/test/java/org/egovframe/cloud/reservechecksevice/api/ReserveApiControllerTest.java b/backend/reserve-check-service/src/test/java/org/egovframe/cloud/reservechecksevice/api/ReserveApiControllerTest.java index 2282eb5..6909d24 100644 --- a/backend/reserve-check-service/src/test/java/org/egovframe/cloud/reservechecksevice/api/ReserveApiControllerTest.java +++ b/backend/reserve-check-service/src/test/java/org/egovframe/cloud/reservechecksevice/api/ReserveApiControllerTest.java @@ -1,27 +1,26 @@ package org.egovframe.cloud.reservechecksevice.api; -import static org.assertj.core.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.time.LocalDateTime; - import org.egovframe.cloud.common.domain.Role; import org.egovframe.cloud.common.exception.dto.ErrorCode; import org.egovframe.cloud.common.exception.dto.ErrorResponse; -import org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveCancelRequestDto; -import org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveListResponseDto; -import org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveSaveRequestDto; -import org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveUpdateRequestDto; +import org.egovframe.cloud.reservechecksevice.api.dto.ReserveCancelRequestDto; +import org.egovframe.cloud.reservechecksevice.api.dto.ReserveListResponseDto; +import org.egovframe.cloud.reservechecksevice.api.dto.ReserveSaveRequestDto; +import org.egovframe.cloud.reservechecksevice.api.dto.ReserveUpdateRequestDto; import org.egovframe.cloud.reservechecksevice.client.ReserveItemServiceClient; import org.egovframe.cloud.reservechecksevice.client.UserServiceClient; import org.egovframe.cloud.reservechecksevice.client.dto.ReserveItemRelationResponseDto; import org.egovframe.cloud.reservechecksevice.client.dto.ReserveItemResponseDto; import org.egovframe.cloud.reservechecksevice.client.dto.UserResponseDto; +import org.egovframe.cloud.reservechecksevice.domain.Reserve; +import org.egovframe.cloud.reservechecksevice.domain.ReserveItem; +import org.egovframe.cloud.reservechecksevice.domain.ReserveRepository; +import org.egovframe.cloud.reservechecksevice.domain.ReserveStatus; import org.egovframe.cloud.reservechecksevice.domain.location.Location; -import org.egovframe.cloud.reservechecksevice.domain.reserve.Reserve; -import org.egovframe.cloud.reservechecksevice.domain.reserve.ReserveItem; -import org.egovframe.cloud.reservechecksevice.domain.reserve.ReserveRepository; -import org.egovframe.cloud.reservechecksevice.domain.reserve.ReserveStatus; import org.egovframe.cloud.reservechecksevice.util.RestResponsePage; import org.egovframe.cloud.reservechecksevice.util.WithCustomMockUser; import org.junit.jupiter.api.AfterEach; @@ -34,11 +33,9 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.core.ParameterizedTypeReference; -import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.reactive.server.WebTestClient; - import reactor.core.publisher.Mono; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @@ -59,8 +56,6 @@ public class ReserveApiControllerTest { @Autowired private WebTestClient webTestClient; - @Autowired - private R2dbcEntityTemplate entityTemplate; private static final String API_URL = "/api/v1/reserves"; @@ -118,7 +113,7 @@ public class ReserveApiControllerTest { } @Test - public void 예약신청관리_목록_조회_성공() throws Exception { + public void 예약신청관리_목록_조회_성공() { //given BDDMockito.when(userServiceClient.findByUserId(ArgumentMatchers.anyString())) .thenReturn(Mono.just(user)); @@ -146,7 +141,7 @@ public class ReserveApiControllerTest { @Test @WithCustomMockUser(userId = "admin", role = Role.ADMIN) - public void 관리자_취소_성공() throws Exception { + public void 관리자_취소_성공() { BDDMockito.when(reserveItemServiceClient.updateInventory(ArgumentMatchers.anyLong(), ArgumentMatchers.anyInt())) .thenReturn(Mono.just(true)); @@ -189,7 +184,7 @@ public class ReserveApiControllerTest { @Test @WithCustomMockUser(userId = "test", role = Role.USER) - public void 다른사용자_예약_취소_실패() throws Exception { + public void 다른사용자_예약_취소_실패() { BDDMockito.when(reserveItemServiceClient.updateInventory(ArgumentMatchers.anyLong(), ArgumentMatchers.anyInt())) .thenReturn(Mono.just(true)); Reserve saved = reserveRepository.insert(reserve).block(); @@ -209,7 +204,7 @@ public class ReserveApiControllerTest { @Test @WithCustomMockUser(userId = "user", role = Role.USER) - public void 예약상태_완료_취소_실패() throws Exception { + public void 예약상태_완료_취소_실패() { Reserve done = reserve.updateStatus(ReserveStatus.DONE.getKey()); Reserve saved = reserveRepository.insert(done).block(); assertNotNull(saved); @@ -231,7 +226,7 @@ public class ReserveApiControllerTest { @Test @WithCustomMockUser(userId = "user", role = Role.USER) - public void 관리자가_아닌_경우_승인_실패() throws Exception { + public void 관리자가_아닌_경우_승인_실패() { Reserve saved = reserveRepository.insert(reserve).block(); assertNotNull(saved); @@ -252,7 +247,7 @@ public class ReserveApiControllerTest { @Test @WithCustomMockUser(userId = "admin", role = Role.ADMIN) - public void 예약승인_성공() throws Exception { + public void 예약승인_성공() { Reserve saved = reserveRepository.insert(reserve).block(); assertNotNull(saved); @@ -274,7 +269,7 @@ public class ReserveApiControllerTest { @Test @WithCustomMockUser(userId = "admin", role = Role.ADMIN) - public void 예약승인_실패_재고부족() throws Exception { + public void 예약승인_실패_재고부족() { ReserveItem failReserveItem = ReserveItem.builder() .reserveItemId(1L) .reserveItemName("test") @@ -313,7 +308,7 @@ public class ReserveApiControllerTest { @Test @WithCustomMockUser(userId = "admin", role = Role.ADMIN) - public void 관리자_예약정보_수정_성공() throws Exception { + public void 관리자_예약정보_수정_성공() { Reserve saved = reserveRepository.insert(reserve).block(); assertNotNull(saved); @@ -351,7 +346,7 @@ public class ReserveApiControllerTest { @Test @WithCustomMockUser(userId = "test", role = Role.USER) - public void 다른사용자_예약정보_수정_실패() throws Exception { + public void 다른사용자_예약정보_수정_실패() { Reserve saved = reserveRepository.insert(reserve).block(); assertNotNull(saved); @@ -387,7 +382,7 @@ public class ReserveApiControllerTest { @Test @WithCustomMockUser(userId = "user", role = Role.USER) - public void 사용자_예약정보_수정_성공() throws Exception { + public void 사용자_예약정보_수정_성공() { Reserve saved = reserveRepository.insert(reserve).block(); assertNotNull(saved); @@ -426,7 +421,7 @@ public class ReserveApiControllerTest { @Test @WithCustomMockUser(userId = "user", role = Role.USER) - public void 사용자_상태승인인예약정보_수정_실패() throws Exception { + public void 사용자_상태승인인예약정보_수정_실패() { Reserve failedReserve = reserve.withReserveStatusId(ReserveStatus.APPROVE.getKey()); Reserve saved = reserveRepository.insert(failedReserve).block(); assertNotNull(saved); @@ -463,7 +458,7 @@ public class ReserveApiControllerTest { } @Test - public void 관리자_예약_성공() throws Exception { + public void 관리자_예약_성공() { BDDMockito.when(reserveItemServiceClient.findById(ArgumentMatchers.anyLong())) .thenReturn(Mono.just(ReserveItemResponseDto.builder().reserveItem(reserveItem).build())); BDDMockito.when(reserveItemServiceClient.updateInventory(ArgumentMatchers.anyLong(), ArgumentMatchers.anyInt())) @@ -495,7 +490,7 @@ public class ReserveApiControllerTest { } @Test - public void 예약신청_valid_실패() throws Exception { + public void 예약신청_valid_실패() { ReserveItem validReserveItem = ReserveItem.builder() .reserveItemId(1L) .reserveItemName("test") @@ -532,7 +527,7 @@ public class ReserveApiControllerTest { } @Test - public void 물품재고조회_성공() throws Exception { + public void 물품재고조회_성공() { BDDMockito.when(reserveItemServiceClient.findById(ArgumentMatchers.anyLong())) .thenReturn(Mono.just(ReserveItemResponseDto.builder().reserveItem(reserveItem).build())); diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/ReserveItemServiceApplication.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/ReserveItemServiceApplication.java index 97fd8c8..40c1512 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/ReserveItemServiceApplication.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/ReserveItemServiceApplication.java @@ -1,15 +1,11 @@ package org.egovframe.cloud.reserveitemservice; +import java.security.Security; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; -import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.ComponentScan; - import reactivefeign.spring.config.EnableReactiveFeignClients; -//import reactor.blockhound.BlockHound; - -import java.security.Security; @ComponentScan({"org.egovframe.cloud.common", "org.egovframe.cloud.reactive", "org.egovframe.cloud.reserveitemservice"}) // org.egovframe.cloud.common package 포함하기 위해 @EnableDiscoveryClient @@ -25,5 +21,4 @@ public class ReserveItemServiceApplication { SpringApplication.run(ReserveItemServiceApplication.class, args); } - } diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/location/LocationApiController.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/location/LocationApiController.java index 4a48935..67118b7 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/location/LocationApiController.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/location/LocationApiController.java @@ -1,6 +1,7 @@ package org.egovframe.cloud.reserveitemservice.api.location; import io.swagger.v3.oas.annotations.Operation; +import javax.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.egovframe.cloud.common.dto.RequestDto; @@ -11,12 +12,18 @@ import org.egovframe.cloud.reserveitemservice.service.location.LocationService; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import javax.validation.Valid; - /** * org.egovframe.cloud.reserveitemservice.api.location.LocationApiController diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/location/dto/LocationResponseDto.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/location/dto/LocationResponseDto.java index bb51565..29da56b 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/location/dto/LocationResponseDto.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/location/dto/LocationResponseDto.java @@ -1,9 +1,11 @@ package org.egovframe.cloud.reserveitemservice.api.location.dto; -import lombok.*; -import org.egovframe.cloud.reserveitemservice.domain.location.Location; - import java.time.LocalDateTime; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.egovframe.cloud.reserveitemservice.domain.location.Location; /** * org.egovframe.cloud.reserveitemservice.api.location.dto.LocationResponseDto diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/location/dto/LocationSaveRequestDto.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/location/dto/LocationSaveRequestDto.java index 1285d92..03d2194 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/location/dto/LocationSaveRequestDto.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/location/dto/LocationSaveRequestDto.java @@ -1,13 +1,12 @@ package org.egovframe.cloud.reserveitemservice.api.location.dto; +import javax.validation.constraints.NotNull; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; import org.egovframe.cloud.reserveitemservice.domain.location.Location; -import javax.validation.constraints.NotNull; - /** * org.egovframe.cloud.reserveitemservice.api.location.dto.LocationSaveRequestDto *

diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/location/dto/LocationUpdateRequestDto.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/location/dto/LocationUpdateRequestDto.java index 8f94449..450ff16 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/location/dto/LocationUpdateRequestDto.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/location/dto/LocationUpdateRequestDto.java @@ -1,12 +1,11 @@ package org.egovframe.cloud.reserveitemservice.api.location.dto; +import javax.validation.constraints.NotNull; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; import org.egovframe.cloud.reserveitemservice.domain.location.Location; - -import javax.validation.constraints.NotNull; /** * org.egovframe.cloud.reserveitemservice.api.location.dto.LocationUpdateRequestDto *

diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/ReserveItemApiController.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/ReserveItemApiController.java index 997aa23..fb558b5 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/ReserveItemApiController.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/ReserveItemApiController.java @@ -1,11 +1,10 @@ package org.egovframe.cloud.reserveitemservice.api.reserveItem; import java.util.Collection; -import java.util.List; import java.util.Map; - import javax.validation.Valid; - +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemListResponseDto; import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemMainResponseDto; import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemRelationResponseDto; @@ -26,10 +25,6 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; /** diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemListResponseDto.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemListResponseDto.java index 59946c8..8a2ff15 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemListResponseDto.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemListResponseDto.java @@ -1,14 +1,11 @@ package org.egovframe.cloud.reserveitemservice.api.reserveItem.dto; +import java.time.LocalDateTime; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem; -import org.springframework.util.NumberUtils; -import org.springframework.util.StringUtils; - -import java.time.LocalDateTime; /** diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemMainResponseDto.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemMainResponseDto.java index 57de071..6029d90 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemMainResponseDto.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemMainResponseDto.java @@ -1,13 +1,11 @@ package org.egovframe.cloud.reserveitemservice.api.reserveItem.dto; import java.time.LocalDateTime; - -import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem; - import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; +import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem; @Getter @NoArgsConstructor diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemRelationResponseDto.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemRelationResponseDto.java index 4e77acc..2292a2a 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemRelationResponseDto.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemRelationResponseDto.java @@ -1,16 +1,14 @@ package org.egovframe.cloud.reserveitemservice.api.reserveItem.dto; +import java.math.BigDecimal; +import java.time.LocalDateTime; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; - import org.egovframe.cloud.reserveitemservice.domain.location.Location; import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem; -import java.math.BigDecimal; -import java.time.LocalDateTime; - /** * org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemRelationResponseDto *

diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemRequestDto.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemRequestDto.java index 00929ac..1324bfe 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemRequestDto.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemRequestDto.java @@ -1,6 +1,10 @@ package org.egovframe.cloud.reserveitemservice.api.reserveItem.dto; -import lombok.*; +import java.util.Objects; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; import org.egovframe.cloud.common.dto.RequestDto; /** @@ -28,4 +32,16 @@ public class ReserveItemRequestDto extends RequestDto { private Long locationId; private String categoryId; private Boolean isUse; + + public boolean hasLocationId() { + return hasId(locationId); + } + + public boolean hasCategoryId() { + return hasId(categoryId); + } + + private boolean hasId(Object id) { + return Objects.nonNull(id) && !Objects.equals("null", id) && !Objects.equals("undefined", id); + } } diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemResponseDto.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemResponseDto.java index 5d217b7..7098d14 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemResponseDto.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemResponseDto.java @@ -1,12 +1,12 @@ package org.egovframe.cloud.reserveitemservice.api.reserveItem.dto; -import lombok.*; -import lombok.experimental.Accessors; -import org.egovframe.cloud.reserveitemservice.domain.location.Location; -import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem; - import java.math.BigDecimal; import java.time.LocalDateTime; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem; /** * org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemResponseDto diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemSaveRequestDto.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemSaveRequestDto.java index 4963ed5..c66da3d 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemSaveRequestDto.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemSaveRequestDto.java @@ -1,5 +1,11 @@ package org.egovframe.cloud.reserveitemservice.api.reserveItem.dto; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.PositiveOrZero; +import javax.validation.constraints.Size; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -7,10 +13,6 @@ import lombok.ToString; import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem; import org.egovframe.cloud.reserveitemservice.validator.annotation.ReserveItemSaveValid; -import javax.validation.constraints.*; -import java.math.BigDecimal; -import java.time.LocalDateTime; - /** * org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemSaveRequestDto *

diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemUpdateRequestDto.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemUpdateRequestDto.java index 5998df1..664c19a 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemUpdateRequestDto.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveItemUpdateRequestDto.java @@ -1,16 +1,17 @@ package org.egovframe.cloud.reserveitemservice.api.reserveItem.dto; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.PositiveOrZero; +import javax.validation.constraints.Size; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem; import org.egovframe.cloud.reserveitemservice.validator.annotation.ReserveItemSaveValid; -import org.hibernate.validator.constraints.Length; - -import javax.validation.constraints.*; -import java.math.BigDecimal; -import java.time.LocalDateTime; /** * org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemUpdateRequestDto diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveSaveRequestDto.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveSaveRequestDto.java index 7514c5e..894c06c 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveSaveRequestDto.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/dto/ReserveSaveRequestDto.java @@ -1,9 +1,12 @@ package org.egovframe.cloud.reserveitemservice.api.reserveItem.dto; -import lombok.*; - -import javax.validation.constraints.NotNull; import java.time.LocalDateTime; +import javax.validation.constraints.NotNull; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; /** * org.egovframe.cloud.reserverequestservice.api.dto.ReserveSaveRequestDto diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/config/ReserveEventConfig.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/config/ReserveEventConfig.java index 8cafd6d..3cf1873 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/config/ReserveEventConfig.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/config/ReserveEventConfig.java @@ -1,25 +1,13 @@ package org.egovframe.cloud.reserveitemservice.config; +import java.util.function.Consumer; import lombok.extern.slf4j.Slf4j; import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveSaveRequestDto; import org.egovframe.cloud.reserveitemservice.service.reserveItem.ReserveItemService; -import org.springframework.amqp.rabbit.connection.ConnectionFactory; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; -import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.core.scheduler.Scheduler; -import reactor.core.scheduler.Schedulers; - -import java.util.function.Consumer; -import java.util.function.Function; /** * org.egovframe.cloud.reserverequestservice.config.ReserveEventConfig diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/config/Resilience4JConfig.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/config/Resilience4JConfig.java index f92c6c0..04f1975 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/config/Resilience4JConfig.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/config/Resilience4JConfig.java @@ -1,12 +1,10 @@ package org.egovframe.cloud.reserveitemservice.config; -import java.time.Duration; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; +import java.time.Duration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; /** * org.egovframe.cloud.portalservice.config.Resilience4JConfig diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/domain/location/Location.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/domain/location/Location.java index 92d1af0..47f1196 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/domain/location/Location.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/domain/location/Location.java @@ -1,13 +1,15 @@ package org.egovframe.cloud.reserveitemservice.domain.location; -import lombok.*; +import javax.validation.constraints.Size; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; import org.egovframe.cloud.reactive.domain.BaseEntity; import org.springframework.data.annotation.Id; import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Table; -import javax.validation.constraints.Size; - /** * org.egovframe.cloud.reserveitemservice.domain.location.Location * diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/domain/reserveItem/ReserveItem.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/domain/reserveItem/ReserveItem.java index 9434957..a3c00bd 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/domain/reserveItem/ReserveItem.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/domain/reserveItem/ReserveItem.java @@ -1,8 +1,14 @@ package org.egovframe.cloud.reserveitemservice.domain.reserveItem; -import lombok.*; -import lombok.experimental.Accessors; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; import org.egovframe.cloud.reactive.domain.BaseEntity; import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemUpdateRequestDto; import org.egovframe.cloud.reserveitemservice.domain.location.Location; @@ -11,11 +17,6 @@ import org.springframework.data.annotation.Transient; import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Table; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; -import java.math.BigDecimal; -import java.time.LocalDateTime; - /** * org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem * @@ -312,11 +313,11 @@ public class ReserveItem extends BaseEntity { /** * 재고 변경 * - * @param inventoryQty + * @param reserveQty * @return */ - public ReserveItem updateInventoryQty(Integer inventoryQty) { - this.inventoryQty = inventoryQty; + public ReserveItem updateInventoryQty(Integer reserveQty) { + this.inventoryQty = calcInventoryQty(reserveQty); return this; } @@ -371,6 +372,31 @@ public class ReserveItem extends BaseEntity { } } } + + public String validate(int reserveQty) { + if (!Category.EDUCATION.isEquals(categoryId)) { + //해당 예약은 수정할 수 없습니다. + return "valid.reserve_not_update"; + } + + LocalDateTime now = LocalDateTime.now(); + if (!(now.isAfter(requestStartDate) && now.isBefore(requestEndDate))) { + //해당 날짜에는 예약할 수 없습니다. + return "valid.reserve_date"; + } + + int qty = calcInventoryQty(reserveQty); + if (qty < 0) { + //해당 날짜에 예약할 수 있는 재고수량이 없습니다. + return "valid.reserve_count"; + } + + return "valid"; + } + + private int calcInventoryQty(int reserveQty) { + return inventoryQty - reserveQty; + } } diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/domain/reserveItem/ReserveItemRepository.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/domain/reserveItem/ReserveItemRepository.java index 6eb0808..261abb8 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/domain/reserveItem/ReserveItemRepository.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/domain/reserveItem/ReserveItemRepository.java @@ -1,7 +1,6 @@ package org.egovframe.cloud.reserveitemservice.domain.reserveItem; import org.springframework.data.r2dbc.repository.R2dbcRepository; -import reactor.core.publisher.Flux; /** * org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItemRepository diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/domain/reserveItem/ReserveItemRepositoryCustom.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/domain/reserveItem/ReserveItemRepositoryCustom.java index 1f73c57..65389f0 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/domain/reserveItem/ReserveItemRepositoryCustom.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/domain/reserveItem/ReserveItemRepositoryCustom.java @@ -1,12 +1,8 @@ package org.egovframe.cloud.reserveitemservice.domain.reserveItem; -import java.time.LocalDateTime; - -import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemMainResponseDto; import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemRequestDto; import org.egovframe.cloud.reserveitemservice.domain.code.Code; import org.springframework.data.domain.Pageable; - import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/domain/reserveItem/ReserveItemRepositoryImpl.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/domain/reserveItem/ReserveItemRepositoryImpl.java index 789b9d7..0198769 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/domain/reserveItem/ReserveItemRepositoryImpl.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/domain/reserveItem/ReserveItemRepositoryImpl.java @@ -1,10 +1,11 @@ package org.egovframe.cloud.reserveitemservice.domain.reserveItem; -import static org.springframework.data.relational.core.query.Criteria.*; +import static org.springframework.data.relational.core.query.Criteria.where; import java.util.ArrayList; import java.util.List; - +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemRequestDto; import org.egovframe.cloud.reserveitemservice.domain.code.Code; import org.egovframe.cloud.reserveitemservice.domain.location.Location; @@ -14,9 +15,6 @@ import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; import org.springframework.data.relational.core.query.Criteria; import org.springframework.data.relational.core.query.Query; import org.springframework.util.StringUtils; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -40,7 +38,7 @@ import reactor.core.publisher.Mono; @Slf4j @RequiredArgsConstructor public class ReserveItemRepositoryImpl implements ReserveItemRepositoryCustom{ - + private static final String SORT_COLUMN = "create_date"; private final R2dbcEntityTemplate entityTemplate; /** @@ -54,7 +52,7 @@ public class ReserveItemRepositoryImpl implements ReserveItemRepositoryCustom{ public Flux search(ReserveItemRequestDto requestDto, Pageable pageable) { return entityTemplate.select(ReserveItem.class) .matching(Query.query(Criteria.from(whereQuery(requestDto))) - .sort(Sort.by(Sort.Direction.DESC, "create_date")) + .sort(Sort.by(Sort.Direction.DESC, SORT_COLUMN)) .with(pageable)) .all() .flatMap(this::loadRelations) @@ -72,7 +70,7 @@ public class ReserveItemRepositoryImpl implements ReserveItemRepositoryCustom{ public Mono searchCount(ReserveItemRequestDto requestDto, Pageable pageable) { return entityTemplate.select(ReserveItem.class) .matching(Query.query(Criteria.from(whereQuery(requestDto))) - .sort(Sort.by(Sort.Direction.DESC, "create_date")) + .sort(Sort.by(Sort.Direction.DESC, SORT_COLUMN)) .with(pageable)) .count(); } @@ -101,7 +99,7 @@ public class ReserveItemRepositoryImpl implements ReserveItemRepositoryCustom{ @Override public Flux findLatestByCategory(Integer count, String categoryId) { Query query =Query.query(where("category_id").is(categoryId) - .and("use_at").isTrue()).sort(Sort.by(Sort.Order.desc("create_date"))); + .and("use_at").isTrue()).sort(Sort.by(Sort.Order.desc(SORT_COLUMN))); if (count > 0) { query.limit(count); @@ -216,17 +214,15 @@ public class ReserveItemRepositoryImpl implements ReserveItemRepositoryCustom{ List whereCriteria = new ArrayList<>(); - if (StringUtils.hasText(keyword)) { - if ("item".equals(keywordType)) { - whereCriteria.add(where("reserve_item_name").like(likeText(keyword))); - } + if (StringUtils.hasText(keyword) && "item".equals(keywordType)) { + whereCriteria.add(where("reserve_item_name").like(likeText(keyword))); } - if (requestDto.getLocationId() != null && !"null".equals(requestDto.getLocationId()) && !"undefined".equals(requestDto.getLocationId())) { + if (requestDto.hasLocationId()) { whereCriteria.add(where("location_id").in(requestDto.getLocationId())); } - if (requestDto.getCategoryId() != null && !"null".equals(requestDto.getCategoryId()) && !"undefined".equals(requestDto.getCategoryId())) { + if (requestDto.hasCategoryId()) { whereCriteria.add(where("category_id").in(requestDto.getCategoryId())); } diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/service/location/LocationService.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/service/location/LocationService.java index 6963e5b..8e2ab88 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/service/location/LocationService.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/service/location/LocationService.java @@ -4,9 +4,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.egovframe.cloud.common.dto.RequestDto; import org.egovframe.cloud.common.exception.BusinessMessageException; -import org.egovframe.cloud.common.exception.EntityNotFoundException; -import org.egovframe.cloud.common.service.AbstractService; -import org.egovframe.cloud.common.util.MessageUtil; import org.egovframe.cloud.reactive.service.ReactiveAbstractService; import org.egovframe.cloud.reserveitemservice.api.location.dto.LocationResponseDto; import org.egovframe.cloud.reserveitemservice.api.location.dto.LocationSaveRequestDto; diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/service/reserveItem/ReserveItemService.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/service/reserveItem/ReserveItemService.java index e3ef3d5..7eb2f71 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/service/reserveItem/ReserveItemService.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/service/reserveItem/ReserveItemService.java @@ -1,12 +1,10 @@ package org.egovframe.cloud.reserveitemservice.service.reserveItem; import java.time.Duration; -import java.time.LocalDateTime; import java.util.Collection; -import java.util.Collections; -import java.util.List; import java.util.Map; - +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.egovframe.cloud.common.exception.BusinessMessageException; import org.egovframe.cloud.reactive.service.ReactiveAbstractService; import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemListResponseDto; @@ -17,7 +15,6 @@ import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemRes import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemSaveRequestDto; import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemUpdateRequestDto; import org.egovframe.cloud.reserveitemservice.config.RequestMessage; -import org.egovframe.cloud.reserveitemservice.domain.reserveItem.Category; import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem; import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItemRepository; import org.springframework.cloud.stream.function.StreamBridge; @@ -27,10 +24,6 @@ import org.springframework.data.domain.Pageable; import org.springframework.messaging.support.MessageBuilder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; @@ -58,31 +51,14 @@ import reactor.core.scheduler.Schedulers; public class ReserveItemService extends ReactiveAbstractService { private static final String RESERVE_CATEGORY_CODE = "reserve-category"; + private static final String RESERVE_CATEGORY_CODE_ALL = "all"; + private static final String INVENTORY_UPDATED_BINDING_NAME = "inventoryUpdated-out-0"; + private static final String EVENT_HEADER_NAME = "reserveUUID"; private final ReserveItemRepository reserveItemRepository; private final StreamBridge streamBridge; - /** - * entity -> dto 변환 - * - * @param reserveItem - * @return - */ - private Mono convertReserveItemResponseDto(ReserveItem reserveItem) { - return Mono.just(ReserveItemResponseDto.builder().reserveItem(reserveItem).build()); - } - - /** - * entity -> dto 변환 - * - * @param reserveItem - * @return - */ - private Mono convertReserveItemListResponseDto(ReserveItem reserveItem) { - return Mono.just(ReserveItemListResponseDto.builder().entity(reserveItem).build()); - } - /** * 목록 조회 * @@ -109,7 +85,7 @@ public class ReserveItemService extends ReactiveAbstractService { */ @Transactional(readOnly = true) public Mono> searchForUser(String categoryId, ReserveItemRequestDto requestDto, Pageable pageable) { - if (!"all".equals(categoryId)) { + if (!RESERVE_CATEGORY_CODE_ALL.equals(categoryId)) { requestDto.setCategoryId(categoryId); } return reserveItemRepository.search(requestDto, pageable) @@ -203,24 +179,11 @@ public class ReserveItemService extends ReactiveAbstractService { return reserveItemRepository.findById(reserveItemId) .switchIfEmpty(monoResponseStatusEntityNotFoundException(reserveItemId)) .flatMap(reserveItem -> { - if (!Category.EDUCATION.isEquals(reserveItem.getCategoryId())) { - //해당 예약은 수정할 수 없습니다. - return Mono.error(new BusinessMessageException(getMessage("valid.reserve_not_update"))); + String validate = reserveItem.validate(reserveQty); + if (!"valid".equals(validate)) { + return Mono.error(new BusinessMessageException(getMessage(validate))); } - - LocalDateTime now = LocalDateTime.now(); - if (!(now.isAfter(reserveItem.getRequestStartDate()) && now.isBefore(reserveItem.getRequestEndDate()))) { - //해당 날짜에는 예약할 수 없습니다. - return Mono.error(new BusinessMessageException(getMessage("valid.reserve_date"))); - } - - int qty = reserveItem.getInventoryQty() - reserveQty; - if (qty < 0) { - //해당 날짜에 예약할 수 있는 재고수량이 없습니다. - return Mono.error(new BusinessMessageException(getMessage("valid.reserve_count"))); - } - - return Mono.just(reserveItem.updateInventoryQty(qty)); + return Mono.just(reserveItem.updateInventoryQty(reserveQty)); }) .flatMap(reserveItemRepository::save) .delayElement(Duration.ofSeconds(5)) @@ -236,23 +199,6 @@ public class ReserveItemService extends ReactiveAbstractService { } - - /** - * 재고 변경 성공 여부 이벤트 발생 - * - * @param reserveId - * @param isItemUpdated - */ - private void sendMessage(String reserveId, Boolean isItemUpdated) { - streamBridge.send("inventoryUpdated-out-0", - MessageBuilder.withPayload( - RequestMessage.builder() - .reserveId(reserveId) - .isItemUpdated(isItemUpdated) - .build()) - .setHeader("reserveUUID", reserveId).build()); - } - /** * 한건 조회 - 연관된 데이터도 같이 조회 (e.g. codename, location) * @@ -274,13 +220,48 @@ public class ReserveItemService extends ReactiveAbstractService { * @return */ public Mono>> findLatest(Integer count) { - return reserveItemRepository.findCodeDetail( - RESERVE_CATEGORY_CODE) + return reserveItemRepository.findCodeDetail(RESERVE_CATEGORY_CODE) .flatMap(code -> reserveItemRepository.findLatestByCategory(count, code.getCodeId())) .map(reserveItem -> ReserveItemMainResponseDto.builder().entity(reserveItem).build()) - .collectMultimap(reserveItem -> reserveItem.getCategoryName()); + .collectMultimap(ReserveItemMainResponseDto::getCategoryName); } + /** + * entity -> dto 변환 + * + * @param reserveItem + * @return + */ + private Mono convertReserveItemResponseDto(ReserveItem reserveItem) { + return Mono.just(ReserveItemResponseDto.builder().reserveItem(reserveItem).build()); + } + + /** + * entity -> dto 변환 + * + * @param reserveItem + * @return + */ + private Mono convertReserveItemListResponseDto(ReserveItem reserveItem) { + return Mono.just(ReserveItemListResponseDto.builder().entity(reserveItem).build()); + } + + /** + * 재고 변경 성공 여부 이벤트 발생 + * + * @param reserveId + * @param isItemUpdated + */ + private void sendMessage(String reserveId, Boolean isItemUpdated) { + streamBridge.send(INVENTORY_UPDATED_BINDING_NAME, + MessageBuilder.withPayload( + RequestMessage.builder() + .reserveId(reserveId) + .isItemUpdated(isItemUpdated) + .build()) + .setHeader(EVENT_HEADER_NAME, reserveId).build()); + } + } diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/validator/ReserveItemSaveValidator.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/validator/ReserveItemSaveValidator.java index d159d32..9008fd4 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/validator/ReserveItemSaveValidator.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/validator/ReserveItemSaveValidator.java @@ -1,21 +1,19 @@ package org.egovframe.cloud.reserveitemservice.validator; +import java.lang.reflect.Field; +import java.time.LocalDateTime; +import javax.annotation.Resource; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; - import org.egovframe.cloud.common.util.MessageUtil; import org.egovframe.cloud.reserveitemservice.validator.annotation.ReserveItemSaveValid; import org.springframework.util.StringUtils; -import javax.annotation.Resource; -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import java.lang.reflect.Field; -import java.time.LocalDateTime; - /** * org.egovframe.cloud.reserveitemservice.validator.ReserveItemSaveValidator - * + *

* 예약 물품 저장 시 validation check를 하기 위한 custom validator * * @author 표준프레임워크센터 shinmj @@ -39,6 +37,7 @@ public class ReserveItemSaveValidator implements ConstraintValidator clazz = object.getClass(); Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); @@ -206,10 +360,12 @@ public class ReserveItemSaveValidator implements ConstraintValidator clazz = object.getClass(); Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); - return field.get(object) == null || !StringUtils.hasLength(String.valueOf(field.get(object))); + return field.get(object) == null || !StringUtils + .hasLength(String.valueOf(field.get(object))); } } diff --git a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/validator/annotation/ReserveItemSaveValid.java b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/validator/annotation/ReserveItemSaveValid.java index d16a4ef..83ba97f 100644 --- a/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/validator/annotation/ReserveItemSaveValid.java +++ b/backend/reserve-item-service/src/main/java/org/egovframe/cloud/reserveitemservice/validator/annotation/ReserveItemSaveValid.java @@ -1,13 +1,12 @@ package org.egovframe.cloud.reserveitemservice.validator.annotation; -import org.egovframe.cloud.reserveitemservice.validator.ReserveItemSaveValidator; - -import javax.validation.Constraint; -import javax.validation.Payload; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import javax.validation.Constraint; +import javax.validation.Payload; +import org.egovframe.cloud.reserveitemservice.validator.ReserveItemSaveValidator; /** * org.egovframe.cloud.reserveitemservice.validator.annotation.ReserveItemSaveValid diff --git a/backend/reserve-item-service/src/test/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/ReserveItemApiControllerTest.java b/backend/reserve-item-service/src/test/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/ReserveItemApiControllerTest.java index 3df2b5e..095159a 100644 --- a/backend/reserve-item-service/src/test/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/ReserveItemApiControllerTest.java +++ b/backend/reserve-item-service/src/test/java/org/egovframe/cloud/reserveitemservice/api/reserveItem/ReserveItemApiControllerTest.java @@ -108,7 +108,7 @@ class ReserveItemApiControllerTest { @Test - public void 사용자목록조회_성공() throws Exception { + public void 사용자목록조회_성공() { ReserveItem saved = reserveItemRepository.save(reserveItem).block(); assertNotNull(saved); @@ -123,7 +123,7 @@ class ReserveItemApiControllerTest { } @Test - public void 관리자목록조회_성공() throws Exception { + public void 관리자목록조회_성공() { ReserveItem saved = reserveItemRepository.save(reserveItem).block(); assertNotNull(saved); @@ -138,7 +138,7 @@ class ReserveItemApiControllerTest { } @Test - public void 한건조회_성공() throws Exception { + public void 한건조회_성공() { ReserveItem saved = reserveItemRepository.save(reserveItem).block(); assertNotNull(saved); @@ -155,7 +155,7 @@ class ReserveItemApiControllerTest { } @Test - public void 사용자_포털_메인_예약목록_조회_성공() throws Exception { + public void 사용자_포털_메인_예약목록_조회_성공() { ReserveItem saved = reserveItemRepository.save(reserveItem).block(); assertNotNull(saved); @@ -176,7 +176,7 @@ class ReserveItemApiControllerTest { } @Test - public void 한건_등록_성공() throws Exception { + public void 한건_등록_성공() { ReserveItemSaveRequestDto requestDto = ReserveItemSaveRequestDto.builder() .reserveItemName(reserveItem.getReserveItemName()) .categoryId(reserveItem.getCategoryId()) @@ -208,7 +208,7 @@ class ReserveItemApiControllerTest { } @Test - public void 한건_수정_성공() throws Exception { + public void 한건_수정_성공() { ReserveItem saved = reserveItemRepository.save(reserveItem).block(); assertNotNull(saved); @@ -241,7 +241,7 @@ class ReserveItemApiControllerTest { } @Test - public void 사용여부_false_수정_성공() throws Exception { + public void 사용여부_false_수정_성공() { ReserveItem saved = reserveItemRepository.save(reserveItem).block(); assertNotNull(saved); @@ -255,7 +255,7 @@ class ReserveItemApiControllerTest { } @Test - public void 한건_저장_validation_실패() throws Exception { + public void 한건_저장_validation_실패() { ReserveItemSaveRequestDto requestDto = ReserveItemSaveRequestDto.builder() .reserveItemName(reserveItem.getReserveItemName()) .categoryId(reserveItem.getCategoryId()) @@ -271,6 +271,8 @@ class ReserveItemApiControllerTest { .selectionMeansId(reserveItem.getSelectionMeansId()) .build(); + System.out.println(requestDto); + ErrorResponse responseBody = webTestClient.post() .uri(API_URL) .bodyValue(requestDto) diff --git a/backend/reserve-request-service/src/main/java/org/egovframe/cloud/reserverequestservice/api/dto/ReserveSaveRequestDto.java b/backend/reserve-request-service/src/main/java/org/egovframe/cloud/reserverequestservice/api/dto/ReserveSaveRequestDto.java index 7071f81..79baa18 100644 --- a/backend/reserve-request-service/src/main/java/org/egovframe/cloud/reserverequestservice/api/dto/ReserveSaveRequestDto.java +++ b/backend/reserve-request-service/src/main/java/org/egovframe/cloud/reserverequestservice/api/dto/ReserveSaveRequestDto.java @@ -1,10 +1,12 @@ package org.egovframe.cloud.reserverequestservice.api.dto; +import java.util.UUID; import lombok.*; import org.egovframe.cloud.reserverequestservice.domain.Reserve; import javax.validation.constraints.NotNull; import java.time.LocalDateTime; +import org.egovframe.cloud.reserverequestservice.domain.ReserveStatus; /** * org.egovframe.cloud.reserverequestservice.api.dto.ReserveSaveRequestDto @@ -90,6 +92,18 @@ public class ReserveSaveRequestDto { this.userEmail = userEmail; } + public Reserve createRequestReserve() { + this.reserveId = String.valueOf(UUID.randomUUID()); + this.reserveStatusId = ReserveStatus.REQUEST.getKey(); + return toEntity(); + } + + public Reserve createApproveReserve() { + this.reserveId = String.valueOf(UUID.randomUUID()); + this.reserveStatusId = ReserveStatus.APPROVE.getKey(); + return toEntity(); + } + public Reserve toEntity() { return Reserve.builder() .reserveId(this.reserveId) diff --git a/backend/reserve-request-service/src/main/java/org/egovframe/cloud/reserverequestservice/domain/ReserveValidator.java b/backend/reserve-request-service/src/main/java/org/egovframe/cloud/reserverequestservice/domain/ReserveValidator.java new file mode 100644 index 0000000..0d0ab4a --- /dev/null +++ b/backend/reserve-request-service/src/main/java/org/egovframe/cloud/reserverequestservice/domain/ReserveValidator.java @@ -0,0 +1,176 @@ +package org.egovframe.cloud.reserverequestservice.domain; + +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.stream.IntStream; +import javax.annotation.Resource; +import lombok.RequiredArgsConstructor; +import org.egovframe.cloud.common.exception.BusinessMessageException; +import org.egovframe.cloud.common.util.MessageUtil; +import org.egovframe.cloud.reserverequestservice.api.dto.ReserveSaveRequestDto; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@RequiredArgsConstructor +@Component +public class ReserveValidator { + + @Resource( + name = "messageUtil" + ) + protected MessageUtil messageUtil; + + private final ReserveRepository reserveRepository; + + public Mono checkValidation(ReserveSaveRequestDto saveRequestDto) { + if (Category.EQUIPMENT.isEquals(saveRequestDto.getCategoryId())) { + return checkEquipment(saveRequestDto); + }else if (Category.SPACE.isEquals(saveRequestDto.getCategoryId())) { + return checkSpace(saveRequestDto); + } + //해당 날짜에는 예약할 수 없습니다. + return Mono.error(new BusinessMessageException(getMessage("valid.reserve_date"))); + } + + /** + * 예약 날자 validation + * + * @param saveRequestDto + * @return + */ + public Mono checkReserveDate(ReserveSaveRequestDto saveRequestDto) { + LocalDateTime startDate = saveRequestDto.getReserveMeansId().equals("realtime") ? + saveRequestDto.getRequestStartDate() : saveRequestDto.getOperationStartDate(); + LocalDateTime endDate = saveRequestDto.getReserveMeansId().equals("realtime") ? + saveRequestDto.getRequestEndDate() : saveRequestDto.getOperationEndDate(); + + if (saveRequestDto.getReserveStartDate().isBefore(startDate)) { + //{0}이 {1} 보다 빠릅니다. 시작일, 운영/예약 시작일 + return Mono.error(new BusinessMessageException(getMessage("valid.to_be_fast.format", new Object[]{getMessage("common.start_date"), + getMessage("reserve_item.operation")+getMessage("reserve")+" "+getMessage("common.start_date")}))); + } + + if (saveRequestDto.getReserveEndDate().isAfter(endDate)) { + //{0}이 {1} 보다 늦습니다. 종료일, 운영/예약 종료일 + return Mono.error(new BusinessMessageException(getMessage("valid.to_be_slow.format", new Object[]{getMessage("common.end_date"), + getMessage("reserve_item.operation")+getMessage("reserve")+" "+getMessage("common.end_date")}))); + } + + if (saveRequestDto.getIsPeriod()) { + long between = ChronoUnit.DAYS.between(saveRequestDto.getReserveStartDate(), + saveRequestDto.getReserveEndDate()); + if (saveRequestDto.getPeriodMaxCount() < between) { + //최대 예약 가능 일수보다 예약기간이 깁니다. (최대 예약 가능일 수 : {0}) + return Mono.error(new BusinessMessageException(getMessage("valid.reserve_period", new Object[]{saveRequestDto.getPeriodMaxCount()}))); + } + } + return Mono.just(saveRequestDto); + } + + /** + * 공간 예약 시 예약 날짜에 다른 예약이 있는지 체크 + * + * @param saveRequestDto + * @return + */ + public Mono checkSpace(ReserveSaveRequestDto saveRequestDto) { + return this.checkReserveDate(saveRequestDto) + .flatMap(result -> reserveRepository.findAllByReserveDateCount( + result.getReserveItemId(), + result.getReserveStartDate(), + result.getReserveEndDate()) + .flatMap(count -> { + if (count > 0) { + //해당 날짜에는 예약할 수 없습니다. + return Mono.error(new BusinessMessageException(getMessage("valid.reserve_date"))); + } + return Mono.just(result); + }) + ); + } + + /** + * 장비 예약 시 예약 날짜에 예약 가능한 재고 체크 + * + * @param saveRequestDto + * @return + */ + public Mono checkEquipment(ReserveSaveRequestDto saveRequestDto) { + return this.checkReserveDate(saveRequestDto) + .flatMap(result -> this.getMaxByReserveDate( + result.getReserveItemId(), + result.getReserveStartDate(), + result.getReserveEndDate()) + .flatMap(max -> { + if ((result.getTotalQty() - max) < result.getReserveQty()) { + return Mono.just(false); + } + return Mono.just(true); + }) + .flatMap(isValid -> { + if (!isValid) { + //해당 날짜에 예약할 수 있는 재고수량이 없습니다. + return Mono.error(new BusinessMessageException(getMessage("valid.reserve_count"))); + } + return Mono.just(saveRequestDto); + }) + ); + } + + /** + * 예약물품에 대해 날짜별 예약된 수량 max 조회 + * 현 예약 건 제외 + * + * @param reserveItemId + * @param startDate + * @param endDate + * @return + */ + private Mono getMaxByReserveDate( Long reserveItemId, LocalDateTime startDate, LocalDateTime endDate) { + Flux reserveFlux = reserveRepository.findAllByReserveDate(reserveItemId, startDate, endDate) + .switchIfEmpty(Flux.empty()); + + if (reserveFlux.equals(Flux.empty())) { + return Mono.just(0); + } + + long between = ChronoUnit.DAYS.between(startDate, endDate); + + if (between == 0) { + return reserveFlux.map(reserve -> { + if (startDate.isAfter(reserve.getReserveStartDate()) + || startDate.isBefore(reserve.getReserveEndDate()) + || startDate.isEqual(reserve.getReserveStartDate()) || startDate.isEqual(reserve.getReserveEndDate())) { + return reserve.getReserveQty(); + } + return 0; + }).reduce(0, (x1, x2) -> x1 + x2); + } + + return Flux.fromStream(IntStream.iterate(0, i -> i + 1) + .limit(between) + .mapToObj(i -> startDate.plusDays(i))) + .flatMap(localDateTime -> + reserveFlux.map(findReserve -> { + if (localDateTime.isAfter(findReserve.getReserveStartDate()) + || localDateTime.isBefore(findReserve.getReserveEndDate()) + || localDateTime.isEqual(findReserve.getReserveStartDate()) || localDateTime.isEqual(findReserve.getReserveEndDate()) + ) { + return findReserve.getReserveQty(); + } + return 0; + }).reduce(0, (x1, x2) -> x1 + x2)) + .groupBy(integer -> integer) + .flatMap(group -> group.reduce((x1,x2) -> x1 > x2?x1:x2)) + .last(0); + } + + private String getMessage(String code) { + return this.messageUtil.getMessage(code); + } + + private String getMessage(String code, Object[] args) { + return this.messageUtil.getMessage(code, args); + } +} diff --git a/backend/reserve-request-service/src/main/java/org/egovframe/cloud/reserverequestservice/service/ReserveService.java b/backend/reserve-request-service/src/main/java/org/egovframe/cloud/reserverequestservice/service/ReserveService.java index 76274f9..c639696 100644 --- a/backend/reserve-request-service/src/main/java/org/egovframe/cloud/reserverequestservice/service/ReserveService.java +++ b/backend/reserve-request-service/src/main/java/org/egovframe/cloud/reserverequestservice/service/ReserveService.java @@ -1,5 +1,6 @@ package org.egovframe.cloud.reserverequestservice.service; +import java.time.LocalDateTime; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.egovframe.cloud.common.config.GlobalConstant; @@ -12,23 +13,23 @@ import org.egovframe.cloud.reserverequestservice.domain.Category; import org.egovframe.cloud.reserverequestservice.domain.Reserve; import org.egovframe.cloud.reserverequestservice.domain.ReserveRepository; import org.egovframe.cloud.reserverequestservice.domain.ReserveStatus; -import org.springframework.amqp.core.*; +import org.egovframe.cloud.reserverequestservice.domain.ReserveValidator; +import org.springframework.amqp.core.AmqpAdmin; +import org.springframework.amqp.core.Binding; +import org.springframework.amqp.core.BindingBuilder; +import org.springframework.amqp.core.Exchange; +import org.springframework.amqp.core.ExchangeBuilder; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.core.QueueBuilder; import org.springframework.cloud.stream.function.StreamBridge; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.security.core.context.SecurityContext; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; - -import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import java.util.UUID; -import java.util.stream.IntStream; - /** * org.egovframe.cloud.reserverequestservice.service.ReserveService *

@@ -52,33 +53,10 @@ import java.util.stream.IntStream; @Service public class ReserveService extends ReactiveAbstractService { private final ReserveRepository reserveRepository; + private final ReserveValidator reserveValidator; private final StreamBridge streamBridge; private final AmqpAdmin amqpAdmin; - /** - * entity -> dto 변환 - * - * @param reserve - * @return - */ - private Mono convertReserveResponseDto(Reserve reserve) { - return Mono.just(ReserveResponseDto.builder() - .entity(reserve) - .build()); - } - - /** - * 현재 로그인 사용자 id - * - * @return - */ - private Mono getUserId() { - return ReactiveSecurityContextHolder.getContext() - .map(SecurityContext::getAuthentication) - .filter(Authentication::isAuthenticated) - .map(Authentication::getPrincipal) - .map(String.class::cast); - } /** * 예약 신청 저장 @@ -88,12 +66,7 @@ public class ReserveService extends ReactiveAbstractService { */ public Mono create(ReserveSaveRequestDto saveRequestDto) { return Mono.just(saveRequestDto) - .flatMap(dto -> { - String uuid = UUID.randomUUID().toString(); - dto.setReserveId(uuid); - dto.setReserveStatusId(ReserveStatus.REQUEST.getKey()); - return Mono.just(dto.toEntity()); - }) + .flatMap(dto -> Mono.just(dto.createRequestReserve())) .zipWith(getUserId()) .flatMap(tuple -> { tuple.getT1().setCreatedInfo(LocalDateTime.now(), tuple.getT2()); @@ -152,162 +125,14 @@ public class ReserveService extends ReactiveAbstractService { */ public Mono save(ReserveSaveRequestDto saveRequestDto) { return Mono.just(saveRequestDto) - .flatMap(this::checkValidation) + .flatMap(this::validate) .onErrorResume(throwable -> Mono.error(throwable)) - .flatMap(dto -> { - String uuid = UUID.randomUUID().toString(); - dto.setReserveId(uuid); - dto.setReserveStatusId(ReserveStatus.APPROVE.getKey()); - return Mono.just(dto.toEntity()); - }).zipWith(getUserId()) + .flatMap(dto -> Mono.just(dto.createApproveReserve())).zipWith(getUserId()) .flatMap(tuple -> Mono.just(tuple.getT1().setCreatedInfo(LocalDateTime.now(), tuple.getT2()))) .flatMap(reserveRepository::insert) .flatMap(this::convertReserveResponseDto); } - private Mono checkValidation(ReserveSaveRequestDto saveRequestDto) { - if (Category.EQUIPMENT.isEquals(saveRequestDto.getCategoryId())) { - return checkEquipment(saveRequestDto); - }else if (Category.SPACE.isEquals(saveRequestDto.getCategoryId())) { - return checkSpace(saveRequestDto); - } - //해당 날짜에는 예약할 수 없습니다. - return Mono.error(new BusinessMessageException(getMessage("valid.reserve_date"))); - } - - /** - * 예약 날자 validation - * - * @param saveRequestDto - * @return - */ - private Mono checkReserveDate(ReserveSaveRequestDto saveRequestDto) { - LocalDateTime startDate = saveRequestDto.getReserveMeansId().equals("realtime") ? - saveRequestDto.getRequestStartDate() : saveRequestDto.getOperationStartDate(); - LocalDateTime endDate = saveRequestDto.getReserveMeansId().equals("realtime") ? - saveRequestDto.getRequestEndDate() : saveRequestDto.getOperationEndDate(); - - if (saveRequestDto.getReserveStartDate().isBefore(startDate)) { - //{0}이 {1} 보다 빠릅니다. 시작일, 운영/예약 시작일 - return Mono.error(new BusinessMessageException(getMessage("valid.to_be_fast.format", new Object[]{getMessage("common.start_date"), - getMessage("reserve_item.operation")+getMessage("reserve")+" "+getMessage("common.start_date")}))); - } - - if (saveRequestDto.getReserveEndDate().isAfter(endDate)) { - //{0}이 {1} 보다 늦습니다. 종료일, 운영/예약 종료일 - return Mono.error(new BusinessMessageException(getMessage("valid.to_be_slow.format", new Object[]{getMessage("common.end_date"), - getMessage("reserve_item.operation")+getMessage("reserve")+" "+getMessage("common.end_date")}))); - } - - if (saveRequestDto.getIsPeriod()) { - long between = ChronoUnit.DAYS.between(saveRequestDto.getReserveStartDate(), - saveRequestDto.getReserveEndDate()); - if (saveRequestDto.getPeriodMaxCount() < between) { - //최대 예약 가능 일수보다 예약기간이 깁니다. (최대 예약 가능일 수 : {0}) - return Mono.error(new BusinessMessageException(getMessage("valid.reserve_period", new Object[]{saveRequestDto.getPeriodMaxCount()}))); - } - } - return Mono.just(saveRequestDto); - } - - /** - * 공간 예약 시 예약 날짜에 다른 예약이 있는지 체크 - * - * @param saveRequestDto - * @return - */ - private Mono checkSpace(ReserveSaveRequestDto saveRequestDto) { - return this.checkReserveDate(saveRequestDto) - .flatMap(result -> reserveRepository.findAllByReserveDateCount( - result.getReserveItemId(), - result.getReserveStartDate(), - result.getReserveEndDate()) - .flatMap(count -> { - if (count > 0) { - //해당 날짜에는 예약할 수 없습니다. - return Mono.error(new BusinessMessageException(getMessage("valid.reserve_date"))); - } - return Mono.just(result); - }) - ); - } - - /** - * 장비 예약 시 예약 날짜에 예약 가능한 재고 체크 - * - * @param saveRequestDto - * @return - */ - private Mono checkEquipment(ReserveSaveRequestDto saveRequestDto) { - return this.checkReserveDate(saveRequestDto) - .flatMap(result -> this.getMaxByReserveDate( - result.getReserveItemId(), - result.getReserveStartDate(), - result.getReserveEndDate()) - .flatMap(max -> { - if ((result.getTotalQty() - max) < result.getReserveQty()) { - return Mono.just(false); - } - return Mono.just(true); - }) - .flatMap(isValid -> { - if (!isValid) { - //해당 날짜에 예약할 수 있는 재고수량이 없습니다. - return Mono.error(new BusinessMessageException(getMessage("valid.reserve_count"))); - } - return Mono.just(saveRequestDto); - }) - ); - } - - /** - * 예약물품에 대해 날짜별 예약된 수량 max 조회 - * 현 예약 건 제외 - * - * @param reserveItemId - * @param startDate - * @param endDate - * @return - */ - private Mono getMaxByReserveDate( Long reserveItemId, LocalDateTime startDate, LocalDateTime endDate) { - Flux reserveFlux = reserveRepository.findAllByReserveDate(reserveItemId, startDate, endDate) - .switchIfEmpty(Flux.empty()); - - if (reserveFlux.equals(Flux.empty())) { - return Mono.just(0); - } - - long between = ChronoUnit.DAYS.between(startDate, endDate); - - if (between == 0) { - return reserveFlux.map(reserve -> { - if (startDate.isAfter(reserve.getReserveStartDate()) - || startDate.isBefore(reserve.getReserveEndDate()) - || startDate.isEqual(reserve.getReserveStartDate()) || startDate.isEqual(reserve.getReserveEndDate())) { - return reserve.getReserveQty(); - } - return 0; - }).reduce(0, (x1, x2) -> x1 + x2); - } - - return Flux.fromStream(IntStream.iterate(0, i -> i + 1) - .limit(between) - .mapToObj(i -> startDate.plusDays(i))) - .flatMap(localDateTime -> - reserveFlux.map(findReserve -> { - if (localDateTime.isAfter(findReserve.getReserveStartDate()) - || localDateTime.isBefore(findReserve.getReserveEndDate()) - || localDateTime.isEqual(findReserve.getReserveStartDate()) || localDateTime.isEqual(findReserve.getReserveEndDate()) - ) { - return findReserve.getReserveQty(); - } - return 0; - }).reduce(0, (x1, x2) -> x1 + x2)) - .groupBy(integer -> integer) - .flatMap(group -> group.reduce((x1,x2) -> x1 > x2?x1:x2)) - .last(0); - } - /** * 예약 신청 후 예약 물품 재고 변경 성공 시 예약승인으로 상태 변경 * @@ -336,4 +161,47 @@ public class ReserveService extends ReactiveAbstractService { .then(); } + /** + * entity -> dto 변환 + * + * @param reserve + * @return + */ + private Mono convertReserveResponseDto(Reserve reserve) { + return Mono.just(ReserveResponseDto.builder() + .entity(reserve) + .build()); + } + + /** + * 현재 로그인 사용자 id + * + * @return + */ + private Mono getUserId() { + return ReactiveSecurityContextHolder.getContext() + .map(SecurityContext::getAuthentication) + .filter(Authentication::isAuthenticated) + .map(Authentication::getPrincipal) + .map(String.class::cast); + } + + /** + * 저장 시 정합성 체크 + * + * @param saveRequestDto + * @return + */ + private Mono validate(ReserveSaveRequestDto saveRequestDto) { + if (Category.EQUIPMENT.isEquals(saveRequestDto.getCategoryId())) { + return reserveValidator.checkEquipment(saveRequestDto); + } + + if (Category.SPACE.isEquals(saveRequestDto.getCategoryId())) { + return reserveValidator.checkSpace(saveRequestDto); + } + //해당 날짜에는 예약할 수 없습니다. + return Mono.error(new BusinessMessageException(getMessage("valid.reserve_date"))); + } + } diff --git a/backend/reserve-request-service/src/test/java/org/egovframe/cloud/reserverequestservice/api/ReserveApiControllerTest.java b/backend/reserve-request-service/src/test/java/org/egovframe/cloud/reserverequestservice/api/ReserveApiControllerTest.java index a7ae064..8c9b0ca 100644 --- a/backend/reserve-request-service/src/test/java/org/egovframe/cloud/reserverequestservice/api/ReserveApiControllerTest.java +++ b/backend/reserve-request-service/src/test/java/org/egovframe/cloud/reserverequestservice/api/ReserveApiControllerTest.java @@ -55,7 +55,7 @@ class ReserveApiControllerTest { @Test @WithCustomMockUser(userId = "user", role = Role.USER) - public void 사용자_예약_성공() throws Exception { + public void 사용자_예약_성공() { ReserveSaveRequestDto saveRequestDto = ReserveSaveRequestDto.builder()