refactor: reformat code

This commit is contained in:
shinmj
2021-12-30 13:56:05 +09:00
parent c62eb513e4
commit 64a3877619
55 changed files with 1586 additions and 1305 deletions

View File

@@ -1,29 +1,32 @@
package org.egovframe.cloud.reservechecksevice.api.reserve; package org.egovframe.cloud.reservechecksevice.api;
import java.time.LocalDate; import java.time.LocalDate;
import javax.validation.Valid;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.egovframe.cloud.common.dto.RequestDto; import org.egovframe.cloud.reservechecksevice.api.dto.ReserveCancelRequestDto;
import org.egovframe.cloud.reservechecksevice.api.reserve.dto.*; import org.egovframe.cloud.reservechecksevice.api.dto.ReserveListResponseDto;
import org.egovframe.cloud.reservechecksevice.domain.reserve.Category; import org.egovframe.cloud.reservechecksevice.api.dto.ReserveRequestDto;
import org.egovframe.cloud.reservechecksevice.service.reserve.ReserveService; 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.core.env.Environment;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.security.core.context.SecurityContext; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.RequestBody;
import reactor.core.publisher.Flux; 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 reactor.core.publisher.Mono;
import javax.validation.Valid;
/** /**
* org.egovframe.cloud.reservechecksevice.api.reserve.ReserveApiController * org.egovframe.cloud.reservechecksevice.api.ReserveApiController
* <p> * <p>
* 예약 확인 rest controller class * 예약 확인 rest controller class
* *

View File

@@ -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 javax.validation.constraints.NotBlank;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
/** /**
* org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveCancelRequestDto * org.egovframe.cloud.reservechecksevice.api.dto.ReserveCancelRequestDto
* <p> * <p>
* 예약 취소 요청 dto class * 예약 취소 요청 dto class
* *

View File

@@ -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.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import org.egovframe.cloud.reservechecksevice.domain.reserve.Reserve; import org.egovframe.cloud.reservechecksevice.domain.Reserve;
import java.time.LocalDateTime;
/** /**
* org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveListResponseDto * org.egovframe.cloud.reservechecksevice.api.dto.ReserveListResponseDto
* <p> * <p>
* 예약 목록 응답 dto class * 예약 목록 응답 dto class
* *

View File

@@ -1,11 +1,11 @@
package org.egovframe.cloud.reservechecksevice.api.reserve.dto; package org.egovframe.cloud.reservechecksevice.api.dto;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.egovframe.cloud.common.dto.RequestDto; import org.egovframe.cloud.common.dto.RequestDto;
/** /**
* org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveRequestDto * org.egovframe.cloud.reservechecksevice.api.dto.ReserveRequestDto
* <p> * <p>
* 얘약 목록 요청 dto class * 얘약 목록 요청 dto class
* *

View File

@@ -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.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import org.egovframe.cloud.reservechecksevice.client.dto.ReserveItemRelationResponseDto; import org.egovframe.cloud.reservechecksevice.client.dto.ReserveItemRelationResponseDto;
import org.egovframe.cloud.reservechecksevice.domain.reserve.Reserve; import org.egovframe.cloud.reservechecksevice.domain.Reserve;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/** /**
* org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveResponseDto * org.egovframe.cloud.reservechecksevice.api.dto.ReserveResponseDto
* <p> * <p>
* 예약 확인(신청) 응답 dto class * 예약 확인(신청) 응답 dto class
* *

View File

@@ -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.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import lombok.ToString; import lombok.ToString;
import lombok.With; import org.egovframe.cloud.reservechecksevice.domain.Reserve;
import org.egovframe.cloud.reservechecksevice.domain.reserve.Reserve;
import org.egovframe.cloud.reservechecksevice.validator.annotation.ReserveSaveValid; 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
* <p> * <p>
* 예약 신청 요청 dto class * 예약 신청 요청 dto class
* *
@@ -77,6 +75,11 @@ public class ReserveSaveRequestDto {
this.userEmail = userEmail; this.userEmail = userEmail;
} }
public Reserve createNewReserve() {
this.reserveId = String.valueOf(UUID.randomUUID());
return toEntity();
}
public Reserve toEntity() { public Reserve toEntity() {
Reserve reserve = Reserve.builder() Reserve reserve = Reserve.builder()
.reserveId(this.reserveId) .reserveId(this.reserveId)

View File

@@ -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.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import org.egovframe.cloud.reservechecksevice.validator.annotation.ReserveSaveValid; 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
* <p> * <p>
* 예약 신청 정보 수정 요청 dto class * 예약 신청 정보 수정 요청 dto class
* *

View File

@@ -6,7 +6,6 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import reactivefeign.spring.config.ReactiveFeignClient; import reactivefeign.spring.config.ReactiveFeignClient;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;

View File

@@ -3,7 +3,6 @@ package org.egovframe.cloud.reservechecksevice.client;
import org.egovframe.cloud.reservechecksevice.client.dto.UserResponseDto; import org.egovframe.cloud.reservechecksevice.client.dto.UserResponseDto;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import reactivefeign.spring.config.ReactiveFeignClient; import reactivefeign.spring.config.ReactiveFeignClient;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;

View File

@@ -1,16 +1,14 @@
package org.egovframe.cloud.reservechecksevice.client.dto; package org.egovframe.cloud.reservechecksevice.client.dto;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import org.egovframe.cloud.reservechecksevice.domain.ReserveItem;
import org.egovframe.cloud.reservechecksevice.domain.location.Location; 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 * org.egovframe.cloud.reservechecksevice.client.dto.ReserveItemRelationResponseDto

View File

@@ -1,13 +1,12 @@
package org.egovframe.cloud.reservechecksevice.client.dto; package org.egovframe.cloud.reservechecksevice.client.dto;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import org.egovframe.cloud.reservechecksevice.domain.reserve.ReserveItem; import org.egovframe.cloud.reservechecksevice.domain.ReserveItem;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/** /**
* org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemResponseDto * org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemResponseDto
@@ -30,6 +29,8 @@ import java.time.LocalDateTime;
@NoArgsConstructor @NoArgsConstructor
@ToString @ToString
public class ReserveItemResponseDto { public class ReserveItemResponseDto {
private static final int MIN_QTY = 0;
private Long reserveItemId; // 예약 물품 id private Long reserveItemId; // 예약 물품 id
private String reserveItemName; //예약 물품 명 private String reserveItemName; //예약 물품 명
private Long locationId; private Long locationId;
@@ -90,4 +91,16 @@ public class ReserveItemResponseDto {
this.managerName = reserveItem.getManagerName(); this.managerName = reserveItem.getManagerName();
this.managerContact = reserveItem.getManagerContact(); 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;
}
} }

View File

@@ -1,12 +1,10 @@
package org.egovframe.cloud.reservechecksevice.config; 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.CircuitBreakerConfig;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; 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 * org.egovframe.cloud.portalservice.config.Resilience4JConfig

View File

@@ -1,10 +1,10 @@
package org.egovframe.cloud.reservechecksevice.domain.reserve; package org.egovframe.cloud.reservechecksevice.domain;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
/** /**
* org.egovframe.cloud.reservechecksevice.domain.reserve.Category * org.egovframe.cloud.reservechecksevice.domain.Category
* *
* 예약 유형 enum class * 예약 유형 enum class
* *

View File

@@ -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.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.client.dto.UserResponseDto;
import org.egovframe.cloud.reservechecksevice.domain.location.Location;
import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient; import org.springframework.data.annotation.Transient;
import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table; 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; this.userEmail = userEmail;
} }
public boolean isReserveUser(String userId) {
return this.userId.equals(userId);
}
/** /**
* 물품 정보 세팅 * 물품 정보 세팅
* *
@@ -203,4 +209,26 @@ public class Reserve extends BaseEntity {
return this; 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);
}
} }

View File

@@ -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.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; 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.Id;
import org.springframework.data.annotation.Transient; import org.springframework.data.annotation.Transient;
import org.springframework.data.relational.core.mapping.Column; 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 * org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem

View File

@@ -1,9 +1,9 @@
package org.egovframe.cloud.reservechecksevice.domain.reserve; package org.egovframe.cloud.reservechecksevice.domain;
import org.springframework.data.r2dbc.repository.R2dbcRepository; import org.springframework.data.r2dbc.repository.R2dbcRepository;
/** /**
* org.egovframe.cloud.reservechecksevice.domain.reserve.ReserveRepository * org.egovframe.cloud.reservechecksevice.domain.ReserveRepository
* *
* 예약 도메인 Repository interface * 예약 도메인 Repository interface
* *

View File

@@ -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 java.time.LocalDateTime;
import org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveRequestDto; import org.egovframe.cloud.reservechecksevice.api.dto.ReserveRequestDto;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; 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 * 예약 도메인 custom Repository interface
* *

View File

@@ -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.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import lombok.RequiredArgsConstructor;
import org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveRequestDto; import org.egovframe.cloud.reservechecksevice.api.dto.ReserveRequestDto;
import org.egovframe.cloud.reservechecksevice.client.ReserveItemServiceClient; import org.egovframe.cloud.reservechecksevice.client.ReserveItemServiceClient;
import org.egovframe.cloud.reservechecksevice.client.UserServiceClient; import org.egovframe.cloud.reservechecksevice.client.UserServiceClient;
import org.egovframe.cloud.reservechecksevice.client.dto.UserResponseDto; 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.Criteria;
import org.springframework.data.relational.core.query.Query; import org.springframework.data.relational.core.query.Query;
import org.springframework.util.StringUtils; 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.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* org.egovframe.cloud.reservechecksevice.domain.reserve.ReserveRepositoryImpl * org.egovframe.cloud.reservechecksevice.domain.ReserveRepositoryImpl
* *
* 예약 도메인 custom repository 구현 클래스 * 예약 도메인 custom repository 구현 클래스
* *

View File

@@ -1,10 +1,10 @@
package org.egovframe.cloud.reservechecksevice.domain.reserve; package org.egovframe.cloud.reservechecksevice.domain;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
/** /**
* org.egovframe.cloud.reservechecksevice.domain.reserve.ReserveStatus * org.egovframe.cloud.reservechecksevice.domain.ReserveStatus
* *
* 예약 상태 enum class * 예약 상태 enum class
* *

View File

@@ -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<Reserve> 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<Reserve> 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<Reserve> 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<Integer> getMaxByReserveDate(Long reserveItemId, LocalDateTime startDate, LocalDateTime endDate) {
Flux<Reserve> reserveFlux = reserveRepository.findAllByReserveDate(reserveItemId, startDate, endDate)
.switchIfEmpty(Flux.empty());
return countMax(reserveFlux, startDate, endDate);
}
/**
* 예약 물품 재고 및 예약 일자 체크
*
* @param reserve
* @return
*/
public Mono<Reserve> 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<Reserve> 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<Integer> getMaxByReserveDateWithoutSelf(String reserveId, Long reserveItemId, LocalDateTime startDate, LocalDateTime endDate) {
Flux<Reserve> 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<Integer> countMax(Flux<Reserve> 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);
}
}

View File

@@ -1,12 +1,13 @@
package org.egovframe.cloud.reservechecksevice.domain.location; 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.egovframe.cloud.reactive.domain.BaseEntity;
import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Column; 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 * org.egovframe.cloud.reserveitemservice.domain.location.Location

View File

@@ -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
* <p>
* 예약 service 클래스
*
* @author 표준프레임워크센터 shinmj
* @version 1.0
* @since 2021/09/15
*
* <pre>
* << 개정이력(Modification Information) >>
*
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2021/09/15 shinmj 최초 생성
* </pre>
*/
@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<Page<ReserveListResponseDto>> 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<ReserveResponseDto> 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<Page<ReserveListResponseDto>> 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<Void> 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<Void> 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<Void> 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<Reserve> update(String reserveId, ReserveUpdateRequestDto updateRequestDto) {
return getIsAdmin().flatMap(isAdmin -> {
if (isAdmin) {
return updateReserve(reserveId, updateRequestDto);
}
return updateReserveForUser(reserveId, updateRequestDto);
});
}
/**
* 관리자 예약 신청 관리자의 경우 실시간이어도 이벤트 스트림 거치지 않고 바로 예약 처리
*
* @param saveRequestDto
* @return
*/
public Mono<ReserveResponseDto> 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<Integer> 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<Reserve> 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<Reserve> 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<Reserve> 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<Reserve> 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<Reserve> findById(String reserveId) {
return reserveRepository.findById(reserveId)
.switchIfEmpty(monoResponseStatusEntityNotFoundException(reserveId));
}
/**
* entity -> dto 변환
*
* @param reserve
* @return
*/
private Mono<ReserveResponseDto> convertReserveResponseDto(Reserve reserve) {
return Mono.just(ReserveResponseDto.builder()
.entity(reserve)
.build());
}
/**
* entity -> 목록 dto 변환
*
* @param reserve
* @return
*/
private Mono<ReserveListResponseDto> convertReserveListResponseDto(Reserve reserve) {
return Mono.just(ReserveListResponseDto.builder()
.entity(reserve)
.build());
}
/**
* 현재 로그인 사용자가 관리자인지 체크
*
* @return
*/
private Mono<Boolean> getIsAdmin() {
return ReactiveSecurityContextHolder.getContext()
.map(SecurityContext::getAuthentication)
.filter(Authentication::isAuthenticated)
.map(Authentication::getAuthorities)
.map(grantedAuthorities -> {
List<SimpleGrantedAuthority> authorities =
new ArrayList<>((Collection<? extends SimpleGrantedAuthority>) grantedAuthorities);
SimpleGrantedAuthority adminRole = new SimpleGrantedAuthority(Role.ADMIN.getKey());
return authorities.contains(adminRole);
});
}
/**
* 현재 로그인 사용자 id
*
* @return
*/
private Mono<String> getUserId() {
return ReactiveSecurityContextHolder.getContext()
.map(SecurityContext::getAuthentication)
.filter(Authentication::isAuthenticated)
.map(Authentication::getPrincipal)
.map(String.class::cast);
}
}

View File

@@ -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
*
* <pre>
* << 개정이력(Modification Information) >>
*
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2021/09/15 shinmj 최초 생성
* </pre>
*/
@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<ReserveResponseDto> convertReserveResponseDto(Reserve reserve) {
return Mono.just(ReserveResponseDto.builder()
.entity(reserve)
.build());
}
/**
* entity -> 목록 dto 변환
*
* @param reserve
* @return
*/
private Mono<ReserveListResponseDto> convertReserveListResponseDto(Reserve reserve) {
return Mono.just(ReserveListResponseDto.builder()
.entity(reserve)
.build());
}
/**
* 현재 로그인 사용자가 관리자인지 체크
*
* @return
*/
private Mono<Boolean> getIsAdmin() {
return ReactiveSecurityContextHolder.getContext()
.map(SecurityContext::getAuthentication)
.filter(Authentication::isAuthenticated)
.map(Authentication::getAuthorities)
.map(grantedAuthorities -> {
List<SimpleGrantedAuthority> authorities =
new ArrayList<>((Collection<? extends SimpleGrantedAuthority>) grantedAuthorities);
SimpleGrantedAuthority adminRole = new SimpleGrantedAuthority(Role.ADMIN.getKey());
return authorities.contains(adminRole);
});
}
/**
* 현재 로그인 사용자 id
*
* @return
*/
private Mono<String> 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<Page<ReserveListResponseDto>> 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<ReserveResponseDto> 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<Page<ReserveListResponseDto>> 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<Void> 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<Void> 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<Void> 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<Reserve> 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<Reserve> 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<Reserve> 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<Reserve> 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<Reserve> 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<Reserve> 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<Reserve> 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<Reserve> 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<Reserve> 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<Reserve> findById(String reserveId) {
return reserveRepository.findById(reserveId)
.switchIfEmpty(monoResponseStatusEntityNotFoundException(reserveId));
}
/**
* 관리자 예약 신청
* 관리자의 경우 실시간이어도 이벤트 스트림 거치지 않고 바로 예약 처리
*
* @param saveRequestDto
* @return
*/
public Mono<ReserveResponseDto> 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<Reserve> 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<Integer> 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<Integer> getMaxByReserveDateWithoutSelf(String reserveId, Long reserveItemId, LocalDateTime startDate, LocalDateTime endDate) {
Flux<Reserve> 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<Integer> getMaxByReserveDate(Long reserveItemId, LocalDateTime startDate, LocalDateTime endDate) {
Flux<Reserve> 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<Integer> countMax(Flux<Reserve> 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);
}
}

View File

@@ -2,22 +2,19 @@ package org.egovframe.cloud.reservechecksevice.validator;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext; 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.common.util.MessageUtil;
import org.egovframe.cloud.reservechecksevice.validator.annotation.ReserveSaveValid; import org.egovframe.cloud.reservechecksevice.validator.annotation.ReserveSaveValid;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
/** /**
* org.egovframe.cloud.reservechecksevice.validator.ReserveSaveValidator * org.egovframe.cloud.reservechecksevice.validator.ReserveSaveValidator
* * <p>
* 예약 신청 시 validation check를 하기 위한 custom validator * 예약 신청 시 validation check를 하기 위한 custom validator
* *
* @author 표준프레임워크센터 shinmj * @author 표준프레임워크센터 shinmj
@@ -42,6 +39,7 @@ public class ReserveSaveValidator implements ConstraintValidator<ReserveSaveVali
protected MessageUtil messageUtil; protected MessageUtil messageUtil;
private String message; private String message;
private boolean fieldValid;
@Override @Override
@@ -59,24 +57,28 @@ public class ReserveSaveValidator implements ConstraintValidator<ReserveSaveVali
@SneakyThrows @SneakyThrows
@Override @Override
public boolean isValid(Object value, ConstraintValidatorContext context) { public boolean isValid(Object value, ConstraintValidatorContext context) {
boolean fieldValid = true; fieldValid = true;
String categoryId = String.valueOf(getFieldValue(value, "categoryId")); String categoryId = String.valueOf(getFieldValue(value, "categoryId"));
if ("education".equals(categoryId)) { if ("education".equals(categoryId)) {
//교육인 경우 //교육인 경우
//신청인원 //신청인원
fieldValid = checkReserveQty(value, context); return checkReserveQty(value, context);
}
}else if ("equipment".equals(categoryId)) { if ("equipment".equals(categoryId)) {
//장비인 경우 //장비인 경우
//신청일자(기간), 신청수량 //신청일자(기간), 신청수량
fieldValid = checkReserveDate(value, context); fieldValid = checkReserveDate(value, context);
fieldValid = checkReserveQty(value, context); fieldValid = checkReserveQty(value, context);
}else if ("place".equals(categoryId)) { return fieldValid;
}
if ("place".equals(categoryId)) {
//공간인 경우 //공간인 경우
//신청일자(기간) //신청일자(기간)
fieldValid = checkReserveDate(value, context); return checkReserveDate(value, context);
} }
return fieldValid; return fieldValid;
@@ -94,12 +96,14 @@ public class ReserveSaveValidator implements ConstraintValidator<ReserveSaveVali
if (isNull(value, "reserveQty")) { if (isNull(value, "reserveQty")) {
context.disableDefaultConstraintViolation(); context.disableDefaultConstraintViolation();
//예약 수량 값은 필수 입니다. //예약 수량 값은 필수 입니다.
context.buildConstraintViolationWithTemplate(messageUtil.getMessage("reserve")+" "+messageUtil.getMessage("reserve.count") + messageUtil.getMessage("valid.required")) context.buildConstraintViolationWithTemplate(
messageUtil.getMessage("reserve") + " " + messageUtil.getMessage("reserve.count")
+ messageUtil.getMessage("valid.required"))
.addPropertyNode("reserveQty") .addPropertyNode("reserveQty")
.addConstraintViolation(); .addConstraintViolation();
return false; return false;
} }
return true; return fieldValid;
} }
/** /**
@@ -115,31 +119,41 @@ public class ReserveSaveValidator implements ConstraintValidator<ReserveSaveVali
if (isNull(value, "reserveStartDate")) { if (isNull(value, "reserveStartDate")) {
context.disableDefaultConstraintViolation(); context.disableDefaultConstraintViolation();
// 예약 신청 시작일 값은 필수 입니다. // 예약 신청 시작일 값은 필수 입니다.
context.buildConstraintViolationWithTemplate(messageUtil.getMessage("reserve_item.request")+" "+messageUtil.getMessage("common.start_datetime") + messageUtil.getMessage("valid.required")) context.buildConstraintViolationWithTemplate(
messageUtil.getMessage("reserve_item.request") + " " + messageUtil
.getMessage("common.start_datetime") + messageUtil.getMessage("valid.required"))
.addPropertyNode("reserveStartDate") .addPropertyNode("reserveStartDate")
.addConstraintViolation(); .addConstraintViolation();
return false; return false;
} else if (isNull(value, "reserveEndDate")) { }
if (isNull(value, "reserveEndDate")) {
context.disableDefaultConstraintViolation(); context.disableDefaultConstraintViolation();
// 예약 신청 종료일 값은 필수 입니다. // 예약 신청 종료일 값은 필수 입니다.
context.buildConstraintViolationWithTemplate(messageUtil.getMessage("reserve_item.request")+" "+messageUtil.getMessage("common.end_datetime") + messageUtil.getMessage("valid.required")) context.buildConstraintViolationWithTemplate(
messageUtil.getMessage("reserve_item.request") + " " + messageUtil
.getMessage("common.end_datetime") + messageUtil.getMessage("valid.required"))
.addPropertyNode("reserveEndDate") .addPropertyNode("reserveEndDate")
.addConstraintViolation(); .addConstraintViolation();
return false; return false;
}else { }
// 예약 시작일, 종료일 체크 // 예약 시작일, 종료일 체크
LocalDateTime reserveStartDate = (LocalDateTime) getFieldValue(value, "reserveStartDate"); LocalDateTime reserveStartDate = (LocalDateTime) getFieldValue(value, "reserveStartDate");
LocalDateTime reserveEndDate = (LocalDateTime) getFieldValue(value, "reserveEndDate"); LocalDateTime reserveEndDate = (LocalDateTime) getFieldValue(value, "reserveEndDate");
if (reserveStartDate.isAfter(reserveEndDate)) { if (reserveStartDate.isAfter(reserveEndDate)) {
context.disableDefaultConstraintViolation(); context.disableDefaultConstraintViolation();
//시작일, 종료일, {0}이 {1}보다 늦습니다. //시작일, 종료일, {0}이 {1}보다 늦습니다.
context.buildConstraintViolationWithTemplate(messageUtil.getMessage("valid.to_be_slow.format", new Object[]{messageUtil.getMessage("common.start_date"), messageUtil.getMessage("common.end_date")})) context.buildConstraintViolationWithTemplate(messageUtil
.getMessage("valid.to_be_slow.format",
new Object[]{messageUtil.getMessage("common.start_date"),
messageUtil.getMessage("common.end_date")}))
.addPropertyNode("reserveStartDate") .addPropertyNode("reserveStartDate")
.addConstraintViolation(); .addConstraintViolation();
return false; return false;
} }
}
return true; return fieldValid;
} }
/** /**
@@ -151,7 +165,8 @@ public class ReserveSaveValidator implements ConstraintValidator<ReserveSaveVali
* @throws NoSuchFieldException * @throws NoSuchFieldException
* @throws IllegalAccessException * @throws IllegalAccessException
*/ */
private Object getFieldValue(Object object, String fieldName) throws NoSuchFieldException, IllegalAccessException { private Object getFieldValue(Object object, String fieldName)
throws NoSuchFieldException, IllegalAccessException {
Class<?> clazz = object.getClass(); Class<?> clazz = object.getClass();
Field field = clazz.getDeclaredField(fieldName); Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true); field.setAccessible(true);
@@ -167,10 +182,12 @@ public class ReserveSaveValidator implements ConstraintValidator<ReserveSaveVali
* @throws NoSuchFieldException * @throws NoSuchFieldException
* @throws IllegalAccessException * @throws IllegalAccessException
*/ */
private boolean isNull(Object object, String fieldName) throws NoSuchFieldException, IllegalAccessException { private boolean isNull(Object object, String fieldName)
throws NoSuchFieldException, IllegalAccessException {
Class<?> clazz = object.getClass(); Class<?> clazz = object.getClass();
Field field = clazz.getDeclaredField(fieldName); Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true); 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)));
} }
} }

View File

@@ -1,27 +1,26 @@
package org.egovframe.cloud.reservechecksevice.api; package org.egovframe.cloud.reservechecksevice.api;
import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import org.egovframe.cloud.common.domain.Role; import org.egovframe.cloud.common.domain.Role;
import org.egovframe.cloud.common.exception.dto.ErrorCode; import org.egovframe.cloud.common.exception.dto.ErrorCode;
import org.egovframe.cloud.common.exception.dto.ErrorResponse; import org.egovframe.cloud.common.exception.dto.ErrorResponse;
import org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveCancelRequestDto; import org.egovframe.cloud.reservechecksevice.api.dto.ReserveCancelRequestDto;
import org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveListResponseDto; import org.egovframe.cloud.reservechecksevice.api.dto.ReserveListResponseDto;
import org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveSaveRequestDto; import org.egovframe.cloud.reservechecksevice.api.dto.ReserveSaveRequestDto;
import org.egovframe.cloud.reservechecksevice.api.reserve.dto.ReserveUpdateRequestDto; import org.egovframe.cloud.reservechecksevice.api.dto.ReserveUpdateRequestDto;
import org.egovframe.cloud.reservechecksevice.client.ReserveItemServiceClient; import org.egovframe.cloud.reservechecksevice.client.ReserveItemServiceClient;
import org.egovframe.cloud.reservechecksevice.client.UserServiceClient; import org.egovframe.cloud.reservechecksevice.client.UserServiceClient;
import org.egovframe.cloud.reservechecksevice.client.dto.ReserveItemRelationResponseDto; import org.egovframe.cloud.reservechecksevice.client.dto.ReserveItemRelationResponseDto;
import org.egovframe.cloud.reservechecksevice.client.dto.ReserveItemResponseDto; import org.egovframe.cloud.reservechecksevice.client.dto.ReserveItemResponseDto;
import org.egovframe.cloud.reservechecksevice.client.dto.UserResponseDto; 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.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.RestResponsePage;
import org.egovframe.cloud.reservechecksevice.util.WithCustomMockUser; import org.egovframe.cloud.reservechecksevice.util.WithCustomMockUser;
import org.junit.jupiter.api.AfterEach; 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.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.ParameterizedTypeReference;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.test.web.reactive.server.WebTestClient;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@@ -59,8 +56,6 @@ public class ReserveApiControllerTest {
@Autowired @Autowired
private WebTestClient webTestClient; private WebTestClient webTestClient;
@Autowired
private R2dbcEntityTemplate entityTemplate;
private static final String API_URL = "/api/v1/reserves"; private static final String API_URL = "/api/v1/reserves";
@@ -118,7 +113,7 @@ public class ReserveApiControllerTest {
} }
@Test @Test
public void 예약신청관리_목록_조회_성공() throws Exception { public void 예약신청관리_목록_조회_성공() {
//given //given
BDDMockito.when(userServiceClient.findByUserId(ArgumentMatchers.anyString())) BDDMockito.when(userServiceClient.findByUserId(ArgumentMatchers.anyString()))
.thenReturn(Mono.just(user)); .thenReturn(Mono.just(user));
@@ -146,7 +141,7 @@ public class ReserveApiControllerTest {
@Test @Test
@WithCustomMockUser(userId = "admin", role = Role.ADMIN) @WithCustomMockUser(userId = "admin", role = Role.ADMIN)
public void 관리자_취소_성공() throws Exception { public void 관리자_취소_성공() {
BDDMockito.when(reserveItemServiceClient.updateInventory(ArgumentMatchers.anyLong(), ArgumentMatchers.anyInt())) BDDMockito.when(reserveItemServiceClient.updateInventory(ArgumentMatchers.anyLong(), ArgumentMatchers.anyInt()))
.thenReturn(Mono.just(true)); .thenReturn(Mono.just(true));
@@ -189,7 +184,7 @@ public class ReserveApiControllerTest {
@Test @Test
@WithCustomMockUser(userId = "test", role = Role.USER) @WithCustomMockUser(userId = "test", role = Role.USER)
public void 다른사용자_예약_취소_실패() throws Exception { public void 다른사용자_예약_취소_실패() {
BDDMockito.when(reserveItemServiceClient.updateInventory(ArgumentMatchers.anyLong(), ArgumentMatchers.anyInt())) BDDMockito.when(reserveItemServiceClient.updateInventory(ArgumentMatchers.anyLong(), ArgumentMatchers.anyInt()))
.thenReturn(Mono.just(true)); .thenReturn(Mono.just(true));
Reserve saved = reserveRepository.insert(reserve).block(); Reserve saved = reserveRepository.insert(reserve).block();
@@ -209,7 +204,7 @@ public class ReserveApiControllerTest {
@Test @Test
@WithCustomMockUser(userId = "user", role = Role.USER) @WithCustomMockUser(userId = "user", role = Role.USER)
public void 예약상태_완료_취소_실패() throws Exception { public void 예약상태_완료_취소_실패() {
Reserve done = reserve.updateStatus(ReserveStatus.DONE.getKey()); Reserve done = reserve.updateStatus(ReserveStatus.DONE.getKey());
Reserve saved = reserveRepository.insert(done).block(); Reserve saved = reserveRepository.insert(done).block();
assertNotNull(saved); assertNotNull(saved);
@@ -231,7 +226,7 @@ public class ReserveApiControllerTest {
@Test @Test
@WithCustomMockUser(userId = "user", role = Role.USER) @WithCustomMockUser(userId = "user", role = Role.USER)
public void 관리자가_아닌_경우_승인_실패() throws Exception { public void 관리자가_아닌_경우_승인_실패() {
Reserve saved = reserveRepository.insert(reserve).block(); Reserve saved = reserveRepository.insert(reserve).block();
assertNotNull(saved); assertNotNull(saved);
@@ -252,7 +247,7 @@ public class ReserveApiControllerTest {
@Test @Test
@WithCustomMockUser(userId = "admin", role = Role.ADMIN) @WithCustomMockUser(userId = "admin", role = Role.ADMIN)
public void 예약승인_성공() throws Exception { public void 예약승인_성공() {
Reserve saved = reserveRepository.insert(reserve).block(); Reserve saved = reserveRepository.insert(reserve).block();
assertNotNull(saved); assertNotNull(saved);
@@ -274,7 +269,7 @@ public class ReserveApiControllerTest {
@Test @Test
@WithCustomMockUser(userId = "admin", role = Role.ADMIN) @WithCustomMockUser(userId = "admin", role = Role.ADMIN)
public void 예약승인_실패_재고부족() throws Exception { public void 예약승인_실패_재고부족() {
ReserveItem failReserveItem = ReserveItem.builder() ReserveItem failReserveItem = ReserveItem.builder()
.reserveItemId(1L) .reserveItemId(1L)
.reserveItemName("test") .reserveItemName("test")
@@ -313,7 +308,7 @@ public class ReserveApiControllerTest {
@Test @Test
@WithCustomMockUser(userId = "admin", role = Role.ADMIN) @WithCustomMockUser(userId = "admin", role = Role.ADMIN)
public void 관리자_예약정보_수정_성공() throws Exception { public void 관리자_예약정보_수정_성공() {
Reserve saved = reserveRepository.insert(reserve).block(); Reserve saved = reserveRepository.insert(reserve).block();
assertNotNull(saved); assertNotNull(saved);
@@ -351,7 +346,7 @@ public class ReserveApiControllerTest {
@Test @Test
@WithCustomMockUser(userId = "test", role = Role.USER) @WithCustomMockUser(userId = "test", role = Role.USER)
public void 다른사용자_예약정보_수정_실패() throws Exception { public void 다른사용자_예약정보_수정_실패() {
Reserve saved = reserveRepository.insert(reserve).block(); Reserve saved = reserveRepository.insert(reserve).block();
assertNotNull(saved); assertNotNull(saved);
@@ -387,7 +382,7 @@ public class ReserveApiControllerTest {
@Test @Test
@WithCustomMockUser(userId = "user", role = Role.USER) @WithCustomMockUser(userId = "user", role = Role.USER)
public void 사용자_예약정보_수정_성공() throws Exception { public void 사용자_예약정보_수정_성공() {
Reserve saved = reserveRepository.insert(reserve).block(); Reserve saved = reserveRepository.insert(reserve).block();
assertNotNull(saved); assertNotNull(saved);
@@ -426,7 +421,7 @@ public class ReserveApiControllerTest {
@Test @Test
@WithCustomMockUser(userId = "user", role = Role.USER) @WithCustomMockUser(userId = "user", role = Role.USER)
public void 사용자_상태승인인예약정보_수정_실패() throws Exception { public void 사용자_상태승인인예약정보_수정_실패() {
Reserve failedReserve = reserve.withReserveStatusId(ReserveStatus.APPROVE.getKey()); Reserve failedReserve = reserve.withReserveStatusId(ReserveStatus.APPROVE.getKey());
Reserve saved = reserveRepository.insert(failedReserve).block(); Reserve saved = reserveRepository.insert(failedReserve).block();
assertNotNull(saved); assertNotNull(saved);
@@ -463,7 +458,7 @@ public class ReserveApiControllerTest {
} }
@Test @Test
public void 관리자_예약_성공() throws Exception { public void 관리자_예약_성공() {
BDDMockito.when(reserveItemServiceClient.findById(ArgumentMatchers.anyLong())) BDDMockito.when(reserveItemServiceClient.findById(ArgumentMatchers.anyLong()))
.thenReturn(Mono.just(ReserveItemResponseDto.builder().reserveItem(reserveItem).build())); .thenReturn(Mono.just(ReserveItemResponseDto.builder().reserveItem(reserveItem).build()));
BDDMockito.when(reserveItemServiceClient.updateInventory(ArgumentMatchers.anyLong(), ArgumentMatchers.anyInt())) BDDMockito.when(reserveItemServiceClient.updateInventory(ArgumentMatchers.anyLong(), ArgumentMatchers.anyInt()))
@@ -495,7 +490,7 @@ public class ReserveApiControllerTest {
} }
@Test @Test
public void 예약신청_valid_실패() throws Exception { public void 예약신청_valid_실패() {
ReserveItem validReserveItem = ReserveItem.builder() ReserveItem validReserveItem = ReserveItem.builder()
.reserveItemId(1L) .reserveItemId(1L)
.reserveItemName("test") .reserveItemName("test")
@@ -532,7 +527,7 @@ public class ReserveApiControllerTest {
} }
@Test @Test
public void 물품재고조회_성공() throws Exception { public void 물품재고조회_성공() {
BDDMockito.when(reserveItemServiceClient.findById(ArgumentMatchers.anyLong())) BDDMockito.when(reserveItemServiceClient.findById(ArgumentMatchers.anyLong()))
.thenReturn(Mono.just(ReserveItemResponseDto.builder().reserveItem(reserveItem).build())); .thenReturn(Mono.just(ReserveItemResponseDto.builder().reserveItem(reserveItem).build()));

View File

@@ -1,15 +1,11 @@
package org.egovframe.cloud.reserveitemservice; package org.egovframe.cloud.reserveitemservice;
import java.security.Security;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import reactivefeign.spring.config.EnableReactiveFeignClients; 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 포함하기 위해 @ComponentScan({"org.egovframe.cloud.common", "org.egovframe.cloud.reactive", "org.egovframe.cloud.reserveitemservice"}) // org.egovframe.cloud.common package 포함하기 위해
@EnableDiscoveryClient @EnableDiscoveryClient
@@ -25,5 +21,4 @@ public class ReserveItemServiceApplication {
SpringApplication.run(ReserveItemServiceApplication.class, args); SpringApplication.run(ReserveItemServiceApplication.class, args);
} }
} }

View File

@@ -1,6 +1,7 @@
package org.egovframe.cloud.reserveitemservice.api.location; package org.egovframe.cloud.reserveitemservice.api.location;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import javax.validation.Valid;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.egovframe.cloud.common.dto.RequestDto; 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.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.http.HttpStatus; 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.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import javax.validation.Valid;
/** /**
* org.egovframe.cloud.reserveitemservice.api.location.LocationApiController * org.egovframe.cloud.reserveitemservice.api.location.LocationApiController

View File

@@ -1,9 +1,11 @@
package org.egovframe.cloud.reserveitemservice.api.location.dto; package org.egovframe.cloud.reserveitemservice.api.location.dto;
import lombok.*;
import org.egovframe.cloud.reserveitemservice.domain.location.Location;
import java.time.LocalDateTime; 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 * org.egovframe.cloud.reserveitemservice.api.location.dto.LocationResponseDto

View File

@@ -1,13 +1,12 @@
package org.egovframe.cloud.reserveitemservice.api.location.dto; package org.egovframe.cloud.reserveitemservice.api.location.dto;
import javax.validation.constraints.NotNull;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import org.egovframe.cloud.reserveitemservice.domain.location.Location; import org.egovframe.cloud.reserveitemservice.domain.location.Location;
import javax.validation.constraints.NotNull;
/** /**
* org.egovframe.cloud.reserveitemservice.api.location.dto.LocationSaveRequestDto * org.egovframe.cloud.reserveitemservice.api.location.dto.LocationSaveRequestDto
* <p> * <p>

View File

@@ -1,12 +1,11 @@
package org.egovframe.cloud.reserveitemservice.api.location.dto; package org.egovframe.cloud.reserveitemservice.api.location.dto;
import javax.validation.constraints.NotNull;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import org.egovframe.cloud.reserveitemservice.domain.location.Location; import org.egovframe.cloud.reserveitemservice.domain.location.Location;
import javax.validation.constraints.NotNull;
/** /**
* org.egovframe.cloud.reserveitemservice.api.location.dto.LocationUpdateRequestDto * org.egovframe.cloud.reserveitemservice.api.location.dto.LocationUpdateRequestDto
* <p> * <p>

View File

@@ -1,11 +1,10 @@
package org.egovframe.cloud.reserveitemservice.api.reserveItem; package org.egovframe.cloud.reserveitemservice.api.reserveItem;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.validation.Valid; 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.ReserveItemListResponseDto;
import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemMainResponseDto; import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemMainResponseDto;
import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemRelationResponseDto; 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.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController; 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; import reactor.core.publisher.Mono;
/** /**

View File

@@ -1,14 +1,11 @@
package org.egovframe.cloud.reserveitemservice.api.reserveItem.dto; package org.egovframe.cloud.reserveitemservice.api.reserveItem.dto;
import java.time.LocalDateTime;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem; import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem;
import org.springframework.util.NumberUtils;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
/** /**

View File

@@ -1,13 +1,11 @@
package org.egovframe.cloud.reserveitemservice.api.reserveItem.dto; package org.egovframe.cloud.reserveitemservice.api.reserveItem.dto;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem;
@Getter @Getter
@NoArgsConstructor @NoArgsConstructor

View File

@@ -1,16 +1,14 @@
package org.egovframe.cloud.reserveitemservice.api.reserveItem.dto; package org.egovframe.cloud.reserveitemservice.api.reserveItem.dto;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import org.egovframe.cloud.reserveitemservice.domain.location.Location; import org.egovframe.cloud.reserveitemservice.domain.location.Location;
import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem; import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/** /**
* org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemRelationResponseDto * org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemRelationResponseDto
* <p> * <p>

View File

@@ -1,6 +1,10 @@
package org.egovframe.cloud.reserveitemservice.api.reserveItem.dto; 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; import org.egovframe.cloud.common.dto.RequestDto;
/** /**
@@ -28,4 +32,16 @@ public class ReserveItemRequestDto extends RequestDto {
private Long locationId; private Long locationId;
private String categoryId; private String categoryId;
private Boolean isUse; 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);
}
} }

View File

@@ -1,12 +1,12 @@
package org.egovframe.cloud.reserveitemservice.api.reserveItem.dto; 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.math.BigDecimal;
import java.time.LocalDateTime; 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 * org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemResponseDto

View File

@@ -1,5 +1,11 @@
package org.egovframe.cloud.reserveitemservice.api.reserveItem.dto; 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.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@@ -7,10 +13,6 @@ import lombok.ToString;
import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem; import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem;
import org.egovframe.cloud.reserveitemservice.validator.annotation.ReserveItemSaveValid; 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 * org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemSaveRequestDto
* <p> * <p>

View File

@@ -1,16 +1,17 @@
package org.egovframe.cloud.reserveitemservice.api.reserveItem.dto; 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.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem; import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem;
import org.egovframe.cloud.reserveitemservice.validator.annotation.ReserveItemSaveValid; 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 * org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemUpdateRequestDto

View File

@@ -1,9 +1,12 @@
package org.egovframe.cloud.reserveitemservice.api.reserveItem.dto; package org.egovframe.cloud.reserveitemservice.api.reserveItem.dto;
import lombok.*;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime; 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 * org.egovframe.cloud.reserverequestservice.api.dto.ReserveSaveRequestDto

View File

@@ -1,25 +1,13 @@
package org.egovframe.cloud.reserveitemservice.config; package org.egovframe.cloud.reserveitemservice.config;
import java.util.function.Consumer;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveSaveRequestDto; import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveSaveRequestDto;
import org.egovframe.cloud.reserveitemservice.service.reserveItem.ReserveItemService; 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.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; 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 * org.egovframe.cloud.reserverequestservice.config.ReserveEventConfig

View File

@@ -1,12 +1,10 @@
package org.egovframe.cloud.reserveitemservice.config; 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.CircuitBreakerConfig;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; 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 * org.egovframe.cloud.portalservice.config.Resilience4JConfig

View File

@@ -1,13 +1,15 @@
package org.egovframe.cloud.reserveitemservice.domain.location; 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.egovframe.cloud.reactive.domain.BaseEntity;
import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table; import org.springframework.data.relational.core.mapping.Table;
import javax.validation.constraints.Size;
/** /**
* org.egovframe.cloud.reserveitemservice.domain.location.Location * org.egovframe.cloud.reserveitemservice.domain.location.Location
* *

View File

@@ -1,8 +1,14 @@
package org.egovframe.cloud.reserveitemservice.domain.reserveItem; package org.egovframe.cloud.reserveitemservice.domain.reserveItem;
import lombok.*; import java.math.BigDecimal;
import lombok.experimental.Accessors; 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.reactive.domain.BaseEntity;
import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemUpdateRequestDto; import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemUpdateRequestDto;
import org.egovframe.cloud.reserveitemservice.domain.location.Location; 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.Column;
import org.springframework.data.relational.core.mapping.Table; 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 * org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItem
* *
@@ -312,11 +313,11 @@ public class ReserveItem extends BaseEntity {
/** /**
* 재고 변경 * 재고 변경
* *
* @param inventoryQty * @param reserveQty
* @return * @return
*/ */
public ReserveItem updateInventoryQty(Integer inventoryQty) { public ReserveItem updateInventoryQty(Integer reserveQty) {
this.inventoryQty = inventoryQty; this.inventoryQty = calcInventoryQty(reserveQty);
return this; 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;
}
} }

View File

@@ -1,7 +1,6 @@
package org.egovframe.cloud.reserveitemservice.domain.reserveItem; package org.egovframe.cloud.reserveitemservice.domain.reserveItem;
import org.springframework.data.r2dbc.repository.R2dbcRepository; import org.springframework.data.r2dbc.repository.R2dbcRepository;
import reactor.core.publisher.Flux;
/** /**
* org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItemRepository * org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItemRepository

View File

@@ -1,12 +1,8 @@
package org.egovframe.cloud.reserveitemservice.domain.reserveItem; 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.api.reserveItem.dto.ReserveItemRequestDto;
import org.egovframe.cloud.reserveitemservice.domain.code.Code; import org.egovframe.cloud.reserveitemservice.domain.code.Code;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;

View File

@@ -1,10 +1,11 @@
package org.egovframe.cloud.reserveitemservice.domain.reserveItem; 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.ArrayList;
import java.util.List; 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.api.reserveItem.dto.ReserveItemRequestDto;
import org.egovframe.cloud.reserveitemservice.domain.code.Code; import org.egovframe.cloud.reserveitemservice.domain.code.Code;
import org.egovframe.cloud.reserveitemservice.domain.location.Location; 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.Criteria;
import org.springframework.data.relational.core.query.Query; import org.springframework.data.relational.core.query.Query;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@@ -40,7 +38,7 @@ import reactor.core.publisher.Mono;
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
public class ReserveItemRepositoryImpl implements ReserveItemRepositoryCustom{ public class ReserveItemRepositoryImpl implements ReserveItemRepositoryCustom{
private static final String SORT_COLUMN = "create_date";
private final R2dbcEntityTemplate entityTemplate; private final R2dbcEntityTemplate entityTemplate;
/** /**
@@ -54,7 +52,7 @@ public class ReserveItemRepositoryImpl implements ReserveItemRepositoryCustom{
public Flux<ReserveItem> search(ReserveItemRequestDto requestDto, Pageable pageable) { public Flux<ReserveItem> search(ReserveItemRequestDto requestDto, Pageable pageable) {
return entityTemplate.select(ReserveItem.class) return entityTemplate.select(ReserveItem.class)
.matching(Query.query(Criteria.from(whereQuery(requestDto))) .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)) .with(pageable))
.all() .all()
.flatMap(this::loadRelations) .flatMap(this::loadRelations)
@@ -72,7 +70,7 @@ public class ReserveItemRepositoryImpl implements ReserveItemRepositoryCustom{
public Mono<Long> searchCount(ReserveItemRequestDto requestDto, Pageable pageable) { public Mono<Long> searchCount(ReserveItemRequestDto requestDto, Pageable pageable) {
return entityTemplate.select(ReserveItem.class) return entityTemplate.select(ReserveItem.class)
.matching(Query.query(Criteria.from(whereQuery(requestDto))) .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)) .with(pageable))
.count(); .count();
} }
@@ -101,7 +99,7 @@ public class ReserveItemRepositoryImpl implements ReserveItemRepositoryCustom{
@Override @Override
public Flux<ReserveItem> findLatestByCategory(Integer count, String categoryId) { public Flux<ReserveItem> findLatestByCategory(Integer count, String categoryId) {
Query query =Query.query(where("category_id").is(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) { if (count > 0) {
query.limit(count); query.limit(count);
@@ -216,17 +214,15 @@ public class ReserveItemRepositoryImpl implements ReserveItemRepositoryCustom{
List<Criteria> whereCriteria = new ArrayList<>(); List<Criteria> whereCriteria = new ArrayList<>();
if (StringUtils.hasText(keyword)) { if (StringUtils.hasText(keyword) && "item".equals(keywordType)) {
if ("item".equals(keywordType)) {
whereCriteria.add(where("reserve_item_name").like(likeText(keyword))); 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())); 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())); whereCriteria.add(where("category_id").in(requestDto.getCategoryId()));
} }

View File

@@ -4,9 +4,6 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.egovframe.cloud.common.dto.RequestDto; import org.egovframe.cloud.common.dto.RequestDto;
import org.egovframe.cloud.common.exception.BusinessMessageException; 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.reactive.service.ReactiveAbstractService;
import org.egovframe.cloud.reserveitemservice.api.location.dto.LocationResponseDto; import org.egovframe.cloud.reserveitemservice.api.location.dto.LocationResponseDto;
import org.egovframe.cloud.reserveitemservice.api.location.dto.LocationSaveRequestDto; import org.egovframe.cloud.reserveitemservice.api.location.dto.LocationSaveRequestDto;

View File

@@ -1,12 +1,10 @@
package org.egovframe.cloud.reserveitemservice.service.reserveItem; package org.egovframe.cloud.reserveitemservice.service.reserveItem;
import java.time.Duration; import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map; import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.egovframe.cloud.common.exception.BusinessMessageException; import org.egovframe.cloud.common.exception.BusinessMessageException;
import org.egovframe.cloud.reactive.service.ReactiveAbstractService; import org.egovframe.cloud.reactive.service.ReactiveAbstractService;
import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemListResponseDto; 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.ReserveItemSaveRequestDto;
import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemUpdateRequestDto; import org.egovframe.cloud.reserveitemservice.api.reserveItem.dto.ReserveItemUpdateRequestDto;
import org.egovframe.cloud.reserveitemservice.config.RequestMessage; 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.ReserveItem;
import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItemRepository; import org.egovframe.cloud.reserveitemservice.domain.reserveItem.ReserveItemRepository;
import org.springframework.cloud.stream.function.StreamBridge; 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.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; 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.publisher.Mono;
import reactor.core.scheduler.Schedulers; import reactor.core.scheduler.Schedulers;
@@ -58,31 +51,14 @@ import reactor.core.scheduler.Schedulers;
public class ReserveItemService extends ReactiveAbstractService { public class ReserveItemService extends ReactiveAbstractService {
private static final String RESERVE_CATEGORY_CODE = "reserve-category"; 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 ReserveItemRepository reserveItemRepository;
private final StreamBridge streamBridge; private final StreamBridge streamBridge;
/**
* entity -> dto 변환
*
* @param reserveItem
* @return
*/
private Mono<ReserveItemResponseDto> convertReserveItemResponseDto(ReserveItem reserveItem) {
return Mono.just(ReserveItemResponseDto.builder().reserveItem(reserveItem).build());
}
/**
* entity -> dto 변환
*
* @param reserveItem
* @return
*/
private Mono<ReserveItemListResponseDto> convertReserveItemListResponseDto(ReserveItem reserveItem) {
return Mono.just(ReserveItemListResponseDto.builder().entity(reserveItem).build());
}
/** /**
* 목록 조회 * 목록 조회
* *
@@ -109,7 +85,7 @@ public class ReserveItemService extends ReactiveAbstractService {
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Mono<Page<ReserveItemListResponseDto>> searchForUser(String categoryId, ReserveItemRequestDto requestDto, Pageable pageable) { public Mono<Page<ReserveItemListResponseDto>> searchForUser(String categoryId, ReserveItemRequestDto requestDto, Pageable pageable) {
if (!"all".equals(categoryId)) { if (!RESERVE_CATEGORY_CODE_ALL.equals(categoryId)) {
requestDto.setCategoryId(categoryId); requestDto.setCategoryId(categoryId);
} }
return reserveItemRepository.search(requestDto, pageable) return reserveItemRepository.search(requestDto, pageable)
@@ -203,24 +179,11 @@ public class ReserveItemService extends ReactiveAbstractService {
return reserveItemRepository.findById(reserveItemId) return reserveItemRepository.findById(reserveItemId)
.switchIfEmpty(monoResponseStatusEntityNotFoundException(reserveItemId)) .switchIfEmpty(monoResponseStatusEntityNotFoundException(reserveItemId))
.flatMap(reserveItem -> { .flatMap(reserveItem -> {
if (!Category.EDUCATION.isEquals(reserveItem.getCategoryId())) { String validate = reserveItem.validate(reserveQty);
//해당 예약은 수정할 수 없습니다. if (!"valid".equals(validate)) {
return Mono.error(new BusinessMessageException(getMessage("valid.reserve_not_update"))); return Mono.error(new BusinessMessageException(getMessage(validate)));
} }
return Mono.just(reserveItem.updateInventoryQty(reserveQty));
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));
}) })
.flatMap(reserveItemRepository::save) .flatMap(reserveItemRepository::save)
.delayElement(Duration.ofSeconds(5)) .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) * 한건 조회 - 연관된 데이터도 같이 조회 (e.g. codename, location)
* *
@@ -274,13 +220,48 @@ public class ReserveItemService extends ReactiveAbstractService {
* @return * @return
*/ */
public Mono<Map<String, Collection<ReserveItemMainResponseDto>>> findLatest(Integer count) { public Mono<Map<String, Collection<ReserveItemMainResponseDto>>> findLatest(Integer count) {
return reserveItemRepository.findCodeDetail( return reserveItemRepository.findCodeDetail(RESERVE_CATEGORY_CODE)
RESERVE_CATEGORY_CODE)
.flatMap(code -> reserveItemRepository.findLatestByCategory(count, code.getCodeId())) .flatMap(code -> reserveItemRepository.findLatestByCategory(count, code.getCodeId()))
.map(reserveItem -> ReserveItemMainResponseDto.builder().entity(reserveItem).build()) .map(reserveItem -> ReserveItemMainResponseDto.builder().entity(reserveItem).build())
.collectMultimap(reserveItem -> reserveItem.getCategoryName()); .collectMultimap(ReserveItemMainResponseDto::getCategoryName);
} }
/**
* entity -> dto 변환
*
* @param reserveItem
* @return
*/
private Mono<ReserveItemResponseDto> convertReserveItemResponseDto(ReserveItem reserveItem) {
return Mono.just(ReserveItemResponseDto.builder().reserveItem(reserveItem).build());
}
/**
* entity -> dto 변환
*
* @param reserveItem
* @return
*/
private Mono<ReserveItemListResponseDto> 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());
}
} }

View File

@@ -1,21 +1,19 @@
package org.egovframe.cloud.reserveitemservice.validator; 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.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.egovframe.cloud.common.util.MessageUtil; import org.egovframe.cloud.common.util.MessageUtil;
import org.egovframe.cloud.reserveitemservice.validator.annotation.ReserveItemSaveValid; import org.egovframe.cloud.reserveitemservice.validator.annotation.ReserveItemSaveValid;
import org.springframework.util.StringUtils; 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 * org.egovframe.cloud.reserveitemservice.validator.ReserveItemSaveValidator
* * <p>
* 예약 물품 저장 시 validation check를 하기 위한 custom validator * 예약 물품 저장 시 validation check를 하기 위한 custom validator
* *
* @author 표준프레임워크센터 shinmj * @author 표준프레임워크센터 shinmj
@@ -39,6 +37,7 @@ public class ReserveItemSaveValidator implements ConstraintValidator<ReserveItem
protected MessageUtil messageUtil; protected MessageUtil messageUtil;
private String message; private String message;
private boolean fieldValid;
@Override @Override
public void initialize(ReserveItemSaveValid constraintAnnotation) { public void initialize(ReserveItemSaveValid constraintAnnotation) {
@@ -55,127 +54,281 @@ public class ReserveItemSaveValidator implements ConstraintValidator<ReserveItem
@SneakyThrows @SneakyThrows
@Override @Override
public boolean isValid(Object value, ConstraintValidatorContext context) { public boolean isValid(Object value, ConstraintValidatorContext context) {
boolean fieldValid = true; fieldValid = true;
// 운영 시작일, 종료일 체크 // 운영 시작일, 종료일 체크
LocalDateTime operationStartDate = (LocalDateTime) getFieldValue(value, "operationStartDate"); validateOperationDate(value, context);
LocalDateTime operationEndDate = (LocalDateTime) getFieldValue(value, "operationEndDate");
if (operationStartDate.isAfter(operationEndDate)) { // 유료인 경우 이용 요금 필수
context.disableDefaultConstraintViolation(); validatePaid(value, context);
//시작일, 종료일, {0}이 {1}보다 늦습니다.
context.buildConstraintViolationWithTemplate(messageUtil.getMessage("valid.to_be_slow.format", new Object[]{messageUtil.getMessage("common.start_date"), messageUtil.getMessage("common.end_date")}))
.addPropertyNode("operationStartDate")
.addConstraintViolation();
fieldValid = false;
}
String reserveMethodId = String.valueOf(getFieldValue(value, "reserveMethodId")); String reserveMethodId = String.valueOf(getFieldValue(value, "reserveMethodId"));
//예약 방법이 '인터넷' 인경우 //예약 방법이 '인터넷' 인경우
if ("internet".equals(reserveMethodId)) { if ("internet".equals(reserveMethodId)) {
return validateInternet(value, context);
}
if ("telephone".equals(reserveMethodId)) {
return validateTelephone(value, context);
}
if ("visit".equals(reserveMethodId)) {
return validateVisit(value, context);
}
return fieldValid;
}
/**
* 운영 시작일, 종료일 체크
*
* @param value
* @param context
* @return
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
private void validateOperationDate(Object value, ConstraintValidatorContext context)
throws NoSuchFieldException, IllegalAccessException {
LocalDateTime operationStartDate = (LocalDateTime) getFieldValue(value,
"operationStartDate");
LocalDateTime operationEndDate = (LocalDateTime) getFieldValue(value, "operationEndDate");
if (operationStartDate.isAfter(operationEndDate)) {
context.disableDefaultConstraintViolation();
//시작일, 종료일, {0}이 {1}보다 늦습니다.
context.buildConstraintViolationWithTemplate(messageUtil
.getMessage("valid.to_be_slow.format",
new Object[]{messageUtil.getMessage("common.start_date"),
messageUtil.getMessage("common.end_date")}))
.addPropertyNode("operationStartDate")
.addConstraintViolation();
fieldValid = false;
}
}
/**
* 유료인 경우 이용 요금 필수
*
* @param value
* @param context
* @return
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
private void validatePaid(Object value, ConstraintValidatorContext context)
throws NoSuchFieldException, IllegalAccessException {
Boolean isPaid = Boolean.valueOf(String.valueOf(getFieldValue(value, "isPaid")));
if (isPaid && isNull(value, "usageCost")) {
context.disableDefaultConstraintViolation();
//이용요금 값은 필수입니다.
context.buildConstraintViolationWithTemplate(
messageUtil.getMessage("reserve_item.usage_fee") + messageUtil
.getMessage("valid.required"))
.addPropertyNode("usageCost")
.addConstraintViolation();
fieldValid = false;
}
}
/**
* 예약 방법인 '방문'인 경우 주소 필수
*
* @param value
* @param context
* @return
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
private boolean validateVisit(Object value, ConstraintValidatorContext context)
throws NoSuchFieldException, IllegalAccessException {
if (isNull(value, "address")) {
context.disableDefaultConstraintViolation();
//주소 값은 필수 입니다.
context.buildConstraintViolationWithTemplate(
messageUtil.getMessage("common.address") + messageUtil.getMessage("valid.required"))
.addPropertyNode("address")
.addConstraintViolation();
return false;
}
return fieldValid;
}
/**
* 예약 방법인 '전화'인 경우 contact 필수
*
* @param value
* @param context
* @return
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
private boolean validateTelephone(Object value, ConstraintValidatorContext context)
throws NoSuchFieldException, IllegalAccessException {
if (isNull(value, "contact")) {
context.disableDefaultConstraintViolation();
//문의처 값은 필수입니다.
context.buildConstraintViolationWithTemplate(
messageUtil.getMessage("reserve_item.contact") + messageUtil
.getMessage("valid.required"))
.addPropertyNode("contact")
.addConstraintViolation();
return false;
}
return fieldValid;
}
/**
* 인터넷 예약인 경우 정합성 체크
*
* @param value
* @param context
* @return
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
private boolean validateInternet(Object value, ConstraintValidatorContext context)
throws NoSuchFieldException, IllegalAccessException {
// 예약 구분 필수 // 예약 구분 필수
if (isNull(value, "reserveMeansId")) { if (isNull(value, "reserveMeansId")) {
context.disableDefaultConstraintViolation(); context.disableDefaultConstraintViolation();
//인터넷 예약 구분 값은 필수 입니다. //인터넷 예약 구분 값은 필수 입니다.
context.buildConstraintViolationWithTemplate(messageUtil.getMessage("reserve_item.reserve_means")+ messageUtil.getMessage("valid.required")) context.buildConstraintViolationWithTemplate(
messageUtil.getMessage("reserve_item.reserve_means") + messageUtil
.getMessage("valid.required"))
.addPropertyNode("reserveMeansId") .addPropertyNode("reserveMeansId")
.addConstraintViolation(); .addConstraintViolation();
fieldValid = false; return false;
}else { }
String reserveMeansId = String.valueOf(getFieldValue(value, "reserveMeansId")); String reserveMeansId = String.valueOf(getFieldValue(value, "reserveMeansId"));
//예약 구분이 실시간 인 경우 //예약 구분이 실시간 인 경우
if ("realtime".equals(reserveMeansId)) { if ("realtime".equals(reserveMeansId)) {
// 예약 신청 기간 필수 return validateRealTime(value, context);
if (isNull(value, "requestStartDate")) { }
context.disableDefaultConstraintViolation();
// 예약 신청 시작일 값은 필수 입니다.
context.buildConstraintViolationWithTemplate(messageUtil.getMessage("reserve_item.request")+" "+messageUtil.getMessage("common.start_datetime") + messageUtil.getMessage("valid.required"))
.addPropertyNode("requestStartDate")
.addConstraintViolation();
fieldValid = false; if ("external".equals(reserveMeansId)) {
} else if (isNull(value, "requestEndDate")) { //예약 구분이 외부 링크인 경우 외부 링크 url 필수
if (isNull(value, "externalUrl")) {
context.disableDefaultConstraintViolation(); context.disableDefaultConstraintViolation();
// 예약 신청 종료일 값은 필수 입니다. //외부링크 URL 값은 필수 입니다.
context.buildConstraintViolationWithTemplate(messageUtil.getMessage("reserve_item.request")+" "+messageUtil.getMessage("common.end_datetime") + messageUtil.getMessage("valid.required")) context.buildConstraintViolationWithTemplate(
.addPropertyNode("requestEndDate") messageUtil.getMessage("reserve_item.external_url") + messageUtil
.getMessage("valid.required"))
.addPropertyNode("externalUrl")
.addConstraintViolation(); .addConstraintViolation();
fieldValid = false; return false;
}else {
LocalDateTime requestStartDate = (LocalDateTime) getFieldValue(value, "requestStartDate");
LocalDateTime requestEndDate = (LocalDateTime) getFieldValue(value, "requestEndDate");
if (requestStartDate.isAfter(requestEndDate)) {
context.disableDefaultConstraintViolation();
//시작일, 종료일, {0}이 {1}보다 늦습니다.
context.buildConstraintViolationWithTemplate(messageUtil.getMessage("valid.to_be_slow.format", new Object[]{messageUtil.getMessage("common.start_date"), messageUtil.getMessage("common.end_date")}))
.addPropertyNode("requestStartDate")
.addConstraintViolation();
fieldValid = false;
} }
} }
return fieldValid;
}
/**
* 실시간 인 경우 정합성 체크
*
* @param value
* @param context
* @return
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
private boolean validateRealTime(Object value, ConstraintValidatorContext context)
throws NoSuchFieldException, IllegalAccessException {
// 예약 신청 기간 필수
fieldValid = validateRequestDate(value, context);
//기간 지정 필수 //기간 지정 필수
fieldValid = validatePeriod(value, context);
return fieldValid;
}
/**
* 기간 지정 정합성 체크
*
* @param value
* @param context
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
private boolean validatePeriod(Object value, ConstraintValidatorContext context)
throws NoSuchFieldException, IllegalAccessException {
if (isNull(value, "isPeriod")) { if (isNull(value, "isPeriod")) {
context.disableDefaultConstraintViolation(); context.disableDefaultConstraintViolation();
//기간 지정 가능 여부 값은 필수 입니다. //기간 지정 가능 여부 값은 필수 입니다.
context.buildConstraintViolationWithTemplate(messageUtil.getMessage("reserve_item.period_possible")+ messageUtil.getMessage("valid.required")) context.buildConstraintViolationWithTemplate(
messageUtil.getMessage("reserve_item.period_possible") + messageUtil
.getMessage("valid.required"))
.addPropertyNode("requestEndDate") .addPropertyNode("requestEndDate")
.addConstraintViolation(); .addConstraintViolation();
fieldValid = false; return false;
}else { }
Boolean isPeriod = Boolean.valueOf(String.valueOf(getFieldValue(value, "isPeriod"))); Boolean isPeriod = Boolean.valueOf(String.valueOf(getFieldValue(value, "isPeriod")));
// 기간 지정 가능인 경우 최대 얘약일 수 필수 // 기간 지정 가능인 경우 최대 얘약일 수 필수
if (isPeriod && isNull(value, "periodMaxCount")) { if (isPeriod && isNull(value, "periodMaxCount")) {
context.disableDefaultConstraintViolation(); context.disableDefaultConstraintViolation();
//최대 예약 가능 일수 값은 필수 입니다. //최대 예약 가능 일수 값은 필수 입니다.
context.buildConstraintViolationWithTemplate(messageUtil.getMessage("reserve_item.max_period_days")+ messageUtil.getMessage("valid.required")) context.buildConstraintViolationWithTemplate(
messageUtil.getMessage("reserve_item.max_period_days") + messageUtil
.getMessage("valid.required"))
.addPropertyNode("periodMaxCount") .addPropertyNode("periodMaxCount")
.addConstraintViolation(); .addConstraintViolation();
fieldValid = false; return false;
}
}
}else if ("external".equals(reserveMeansId)) {
//예약 구분이 외부 링크인 경우 외부 링크 url 필수
if (isNull(value, "externalUrl")) {
context.disableDefaultConstraintViolation();
//외부링크 URL 값은 필수 입니다.
context.buildConstraintViolationWithTemplate(messageUtil.getMessage("reserve_item.external_url")+ messageUtil.getMessage("valid.required"))
.addPropertyNode("externalUrl")
.addConstraintViolation();
fieldValid = false;
}
}
}
} else if ("telephone".equals(reserveMethodId)) {
//예약 방법인 '전화'인 경우 contact 필수
if (isNull(value, "contact")) {
context.disableDefaultConstraintViolation();
//문의처 값은 필수입니다.
context.buildConstraintViolationWithTemplate(messageUtil.getMessage("reserve_item.contact")+ messageUtil.getMessage("valid.required"))
.addPropertyNode("contact")
.addConstraintViolation();
fieldValid = false;
}
}else if ("visit".equals(reserveMethodId)) {
//예약 방법인 '방문'인 경우 주소 필수
if (isNull(value, "address")) {
context.disableDefaultConstraintViolation();
//주소 값은 필수 입니다.
context.buildConstraintViolationWithTemplate(messageUtil.getMessage("common.address")+ messageUtil.getMessage("valid.required"))
.addPropertyNode("address")
.addConstraintViolation();
fieldValid = false;
}
} }
// 유료인 경우 이용 요금 필수 return fieldValid;
Boolean isPaid = Boolean.valueOf(String.valueOf(getFieldValue(value, "isPaid"))); }
if (isPaid && isNull(value, "usageCost")) {
/**
* 예약 신청 일자 정합성 체크
*
* @param value
* @param context
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
private boolean validateRequestDate(Object value, ConstraintValidatorContext context)
throws NoSuchFieldException, IllegalAccessException {
if (isNull(value, "requestStartDate")) {
context.disableDefaultConstraintViolation(); context.disableDefaultConstraintViolation();
//이용요금 값은 필수입니다. // 예약 신청 시작일 값은 필수 입니다.
context.buildConstraintViolationWithTemplate(messageUtil.getMessage("reserve_item.usage_fee")+ messageUtil.getMessage("valid.required")) context.buildConstraintViolationWithTemplate(
.addPropertyNode("usageCost") messageUtil.getMessage("reserve_item.request") + " " + messageUtil
.getMessage("common.start_datetime") + messageUtil.getMessage("valid.required"))
.addPropertyNode("requestStartDate")
.addConstraintViolation(); .addConstraintViolation();
fieldValid = false;
return false;
}
if (isNull(value, "requestEndDate")) {
context.disableDefaultConstraintViolation();
// 예약 신청 종료일 값은 필수 입니다.
context.buildConstraintViolationWithTemplate(
messageUtil.getMessage("reserve_item.request") + " " + messageUtil
.getMessage("common.end_datetime") + messageUtil.getMessage("valid.required"))
.addPropertyNode("requestEndDate")
.addConstraintViolation();
return false;
}
LocalDateTime requestStartDate = (LocalDateTime) getFieldValue(value, "requestStartDate");
LocalDateTime requestEndDate = (LocalDateTime) getFieldValue(value, "requestEndDate");
if (requestStartDate.isAfter(requestEndDate)) {
context.disableDefaultConstraintViolation();
//시작일, 종료일, {0}이 {1}보다 늦습니다.
context.buildConstraintViolationWithTemplate(messageUtil
.getMessage("valid.to_be_slow.format",
new Object[]{messageUtil.getMessage("common.start_date"),
messageUtil.getMessage("common.end_date")}))
.addPropertyNode("requestStartDate")
.addConstraintViolation();
return false;
} }
return fieldValid; return fieldValid;
@@ -190,7 +343,8 @@ public class ReserveItemSaveValidator implements ConstraintValidator<ReserveItem
* @throws NoSuchFieldException * @throws NoSuchFieldException
* @throws IllegalAccessException * @throws IllegalAccessException
*/ */
private Object getFieldValue(Object object, String fieldName) throws NoSuchFieldException, IllegalAccessException { private Object getFieldValue(Object object, String fieldName)
throws NoSuchFieldException, IllegalAccessException {
Class<?> clazz = object.getClass(); Class<?> clazz = object.getClass();
Field field = clazz.getDeclaredField(fieldName); Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true); field.setAccessible(true);
@@ -206,10 +360,12 @@ public class ReserveItemSaveValidator implements ConstraintValidator<ReserveItem
* @throws NoSuchFieldException * @throws NoSuchFieldException
* @throws IllegalAccessException * @throws IllegalAccessException
*/ */
private boolean isNull(Object object, String fieldName) throws NoSuchFieldException, IllegalAccessException { private boolean isNull(Object object, String fieldName)
throws NoSuchFieldException, IllegalAccessException {
Class<?> clazz = object.getClass(); Class<?> clazz = object.getClass();
Field field = clazz.getDeclaredField(fieldName); Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true); 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)));
} }
} }

View File

@@ -1,13 +1,12 @@
package org.egovframe.cloud.reserveitemservice.validator.annotation; 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.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; 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 * org.egovframe.cloud.reserveitemservice.validator.annotation.ReserveItemSaveValid

View File

@@ -108,7 +108,7 @@ class ReserveItemApiControllerTest {
@Test @Test
public void 사용자목록조회_성공() throws Exception { public void 사용자목록조회_성공() {
ReserveItem saved = reserveItemRepository.save(reserveItem).block(); ReserveItem saved = reserveItemRepository.save(reserveItem).block();
assertNotNull(saved); assertNotNull(saved);
@@ -123,7 +123,7 @@ class ReserveItemApiControllerTest {
} }
@Test @Test
public void 관리자목록조회_성공() throws Exception { public void 관리자목록조회_성공() {
ReserveItem saved = reserveItemRepository.save(reserveItem).block(); ReserveItem saved = reserveItemRepository.save(reserveItem).block();
assertNotNull(saved); assertNotNull(saved);
@@ -138,7 +138,7 @@ class ReserveItemApiControllerTest {
} }
@Test @Test
public void 한건조회_성공() throws Exception { public void 한건조회_성공() {
ReserveItem saved = reserveItemRepository.save(reserveItem).block(); ReserveItem saved = reserveItemRepository.save(reserveItem).block();
assertNotNull(saved); assertNotNull(saved);
@@ -155,7 +155,7 @@ class ReserveItemApiControllerTest {
} }
@Test @Test
public void 사용자_포털_메인_예약목록_조회_성공() throws Exception { public void 사용자_포털_메인_예약목록_조회_성공() {
ReserveItem saved = reserveItemRepository.save(reserveItem).block(); ReserveItem saved = reserveItemRepository.save(reserveItem).block();
assertNotNull(saved); assertNotNull(saved);
@@ -176,7 +176,7 @@ class ReserveItemApiControllerTest {
} }
@Test @Test
public void 한건_등록_성공() throws Exception { public void 한건_등록_성공() {
ReserveItemSaveRequestDto requestDto = ReserveItemSaveRequestDto.builder() ReserveItemSaveRequestDto requestDto = ReserveItemSaveRequestDto.builder()
.reserveItemName(reserveItem.getReserveItemName()) .reserveItemName(reserveItem.getReserveItemName())
.categoryId(reserveItem.getCategoryId()) .categoryId(reserveItem.getCategoryId())
@@ -208,7 +208,7 @@ class ReserveItemApiControllerTest {
} }
@Test @Test
public void 한건_수정_성공() throws Exception { public void 한건_수정_성공() {
ReserveItem saved = reserveItemRepository.save(reserveItem).block(); ReserveItem saved = reserveItemRepository.save(reserveItem).block();
assertNotNull(saved); assertNotNull(saved);
@@ -241,7 +241,7 @@ class ReserveItemApiControllerTest {
} }
@Test @Test
public void 사용여부_false_수정_성공() throws Exception { public void 사용여부_false_수정_성공() {
ReserveItem saved = reserveItemRepository.save(reserveItem).block(); ReserveItem saved = reserveItemRepository.save(reserveItem).block();
assertNotNull(saved); assertNotNull(saved);
@@ -255,7 +255,7 @@ class ReserveItemApiControllerTest {
} }
@Test @Test
public void 한건_저장_validation_실패() throws Exception { public void 한건_저장_validation_실패() {
ReserveItemSaveRequestDto requestDto = ReserveItemSaveRequestDto.builder() ReserveItemSaveRequestDto requestDto = ReserveItemSaveRequestDto.builder()
.reserveItemName(reserveItem.getReserveItemName()) .reserveItemName(reserveItem.getReserveItemName())
.categoryId(reserveItem.getCategoryId()) .categoryId(reserveItem.getCategoryId())
@@ -271,6 +271,8 @@ class ReserveItemApiControllerTest {
.selectionMeansId(reserveItem.getSelectionMeansId()) .selectionMeansId(reserveItem.getSelectionMeansId())
.build(); .build();
System.out.println(requestDto);
ErrorResponse responseBody = webTestClient.post() ErrorResponse responseBody = webTestClient.post()
.uri(API_URL) .uri(API_URL)
.bodyValue(requestDto) .bodyValue(requestDto)

View File

@@ -1,10 +1,12 @@
package org.egovframe.cloud.reserverequestservice.api.dto; package org.egovframe.cloud.reserverequestservice.api.dto;
import java.util.UUID;
import lombok.*; import lombok.*;
import org.egovframe.cloud.reserverequestservice.domain.Reserve; import org.egovframe.cloud.reserverequestservice.domain.Reserve;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import org.egovframe.cloud.reserverequestservice.domain.ReserveStatus;
/** /**
* org.egovframe.cloud.reserverequestservice.api.dto.ReserveSaveRequestDto * org.egovframe.cloud.reserverequestservice.api.dto.ReserveSaveRequestDto
@@ -90,6 +92,18 @@ public class ReserveSaveRequestDto {
this.userEmail = userEmail; 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() { public Reserve toEntity() {
return Reserve.builder() return Reserve.builder()
.reserveId(this.reserveId) .reserveId(this.reserveId)

View File

@@ -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<ReserveSaveRequestDto> 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<ReserveSaveRequestDto> 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<ReserveSaveRequestDto> 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<ReserveSaveRequestDto> 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<Integer> getMaxByReserveDate( Long reserveItemId, LocalDateTime startDate, LocalDateTime endDate) {
Flux<Reserve> 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);
}
}

View File

@@ -1,5 +1,6 @@
package org.egovframe.cloud.reserverequestservice.service; package org.egovframe.cloud.reserverequestservice.service;
import java.time.LocalDateTime;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.egovframe.cloud.common.config.GlobalConstant; 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.Reserve;
import org.egovframe.cloud.reserverequestservice.domain.ReserveRepository; import org.egovframe.cloud.reserverequestservice.domain.ReserveRepository;
import org.egovframe.cloud.reserverequestservice.domain.ReserveStatus; 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.cloud.stream.function.StreamBridge;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContext;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers; 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 * org.egovframe.cloud.reserverequestservice.service.ReserveService
* <p> * <p>
@@ -52,33 +53,10 @@ import java.util.stream.IntStream;
@Service @Service
public class ReserveService extends ReactiveAbstractService { public class ReserveService extends ReactiveAbstractService {
private final ReserveRepository reserveRepository; private final ReserveRepository reserveRepository;
private final ReserveValidator reserveValidator;
private final StreamBridge streamBridge; private final StreamBridge streamBridge;
private final AmqpAdmin amqpAdmin; private final AmqpAdmin amqpAdmin;
/**
* entity -> dto 변환
*
* @param reserve
* @return
*/
private Mono<ReserveResponseDto> convertReserveResponseDto(Reserve reserve) {
return Mono.just(ReserveResponseDto.builder()
.entity(reserve)
.build());
}
/**
* 현재 로그인 사용자 id
*
* @return
*/
private Mono<String> 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<ReserveResponseDto> create(ReserveSaveRequestDto saveRequestDto) { public Mono<ReserveResponseDto> create(ReserveSaveRequestDto saveRequestDto) {
return Mono.just(saveRequestDto) return Mono.just(saveRequestDto)
.flatMap(dto -> { .flatMap(dto -> Mono.just(dto.createRequestReserve()))
String uuid = UUID.randomUUID().toString();
dto.setReserveId(uuid);
dto.setReserveStatusId(ReserveStatus.REQUEST.getKey());
return Mono.just(dto.toEntity());
})
.zipWith(getUserId()) .zipWith(getUserId())
.flatMap(tuple -> { .flatMap(tuple -> {
tuple.getT1().setCreatedInfo(LocalDateTime.now(), tuple.getT2()); tuple.getT1().setCreatedInfo(LocalDateTime.now(), tuple.getT2());
@@ -152,162 +125,14 @@ public class ReserveService extends ReactiveAbstractService {
*/ */
public Mono<ReserveResponseDto> save(ReserveSaveRequestDto saveRequestDto) { public Mono<ReserveResponseDto> save(ReserveSaveRequestDto saveRequestDto) {
return Mono.just(saveRequestDto) return Mono.just(saveRequestDto)
.flatMap(this::checkValidation) .flatMap(this::validate)
.onErrorResume(throwable -> Mono.error(throwable)) .onErrorResume(throwable -> Mono.error(throwable))
.flatMap(dto -> { .flatMap(dto -> Mono.just(dto.createApproveReserve())).zipWith(getUserId())
String uuid = UUID.randomUUID().toString();
dto.setReserveId(uuid);
dto.setReserveStatusId(ReserveStatus.APPROVE.getKey());
return Mono.just(dto.toEntity());
}).zipWith(getUserId())
.flatMap(tuple -> Mono.just(tuple.getT1().setCreatedInfo(LocalDateTime.now(), tuple.getT2()))) .flatMap(tuple -> Mono.just(tuple.getT1().setCreatedInfo(LocalDateTime.now(), tuple.getT2())))
.flatMap(reserveRepository::insert) .flatMap(reserveRepository::insert)
.flatMap(this::convertReserveResponseDto); .flatMap(this::convertReserveResponseDto);
} }
private Mono<ReserveSaveRequestDto> 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<ReserveSaveRequestDto> 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<ReserveSaveRequestDto> 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<ReserveSaveRequestDto> 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<Integer> getMaxByReserveDate( Long reserveItemId, LocalDateTime startDate, LocalDateTime endDate) {
Flux<Reserve> 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(); .then();
} }
/**
* entity -> dto 변환
*
* @param reserve
* @return
*/
private Mono<ReserveResponseDto> convertReserveResponseDto(Reserve reserve) {
return Mono.just(ReserveResponseDto.builder()
.entity(reserve)
.build());
}
/**
* 현재 로그인 사용자 id
*
* @return
*/
private Mono<String> getUserId() {
return ReactiveSecurityContextHolder.getContext()
.map(SecurityContext::getAuthentication)
.filter(Authentication::isAuthenticated)
.map(Authentication::getPrincipal)
.map(String.class::cast);
}
/**
* 저장 시 정합성 체크
*
* @param saveRequestDto
* @return
*/
private Mono<ReserveSaveRequestDto> 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")));
}
} }

View File

@@ -55,7 +55,7 @@ class ReserveApiControllerTest {
@Test @Test
@WithCustomMockUser(userId = "user", role = Role.USER) @WithCustomMockUser(userId = "user", role = Role.USER)
public void 사용자_예약_성공() throws Exception { public void 사용자_예약_성공() {
ReserveSaveRequestDto saveRequestDto = ReserveSaveRequestDto saveRequestDto =
ReserveSaveRequestDto.builder() ReserveSaveRequestDto.builder()