Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…into dev
  • Loading branch information
Z1Park committed Aug 21, 2023
2 parents 18aaf3a + 4ec10d7 commit cebb63e
Show file tree
Hide file tree
Showing 16 changed files with 455 additions and 233 deletions.
10 changes: 5 additions & 5 deletions backend/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ services:
build: ./
restart: always
environment:
MARIADB_DATABASE_HOST: ${HOST}
MARIADB_DATABASE: ${DATABASE}
MARIADB_USER: ${DB_USER}
MARIADB_PASSWORD: ${DB_PASSWORD}
MARIADB_ROOT_PASSWORD: ${ROOT_PASSWORD}
MARIADB_DATABASE_HOST: localhost
MARIADB_DATABASE: cabi_local
MARIADB_USER: root
MARIADB_PASSWORD: YourPassword
MARIADB_ROOT_PASSWORD: YourPassword
ports:
- '3307:3306'
tty: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
import org.ftclub.cabinet.user.domain.User;

/**
* 유저의 식별자, 이름, 이메일, 블랙홀 날짜를 반환하는 DTO입니다.
Expand All @@ -22,4 +23,7 @@ public static UserBlackholeInfoDto of(Long userId, String name, String email,
LocalDateTime blackHoledAt) {
return new UserBlackholeInfoDto(userId, name, email, blackHoledAt);
}
public static UserBlackholeInfoDto of(User user) {
return new UserBlackholeInfoDto(user.getUserId(), user.getName(), user.getEmail(), user.getBlackholedAt());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.ftclub.cabinet.event;

import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.ftclub.cabinet.dto.UserBlackholeInfoDto;
import org.ftclub.cabinet.utils.blackhole.manager.BlackholeManager;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Log4j2
@Component
//@EnableAsync
@RequiredArgsConstructor
public class BlackholedUserLentEventListener {

private final BlackholeManager blackholeManager;

// @Async
@EventListener
public void handleBlackholedUserLentAttemptingEvent(UserBlackholeInfoDto userBlackholeInfoDto) {
log.info("Called handleBlackholedUserLentAttemptingEvent");
blackholeManager.handleBlackhole(userBlackholeInfoDto);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.ftclub.cabinet.exception;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;

/**
* {@link CustomServiceException}을 위한 exception 클래스. 생성할 exception에 대한 정보를 담고있다.
*/
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
@RequiredArgsConstructor
@Getter
public class CustomExceptionStatus {
private final int statusCode;
private final String message;
private final String error;

public CustomExceptionStatus(HttpStatus status, String message) {
this.statusCode = status.value();
this.message = message;
this.error = status.getReasonPhrase();
}

public CustomExceptionStatus(ExceptionStatus status, String message) {
this.statusCode = status.getStatusCode();
this.message = status.getMessage() + "\n" + message;
this.error = status.getError();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.ftclub.cabinet.exception;

/**
* Service에서 throw 하는 Exception 중 오류메세지를 커스텀 가능한 Exception
* @see CustomExceptionStatus
*/
public class CustomServiceException extends RuntimeException {

final CustomExceptionStatus status;

/**
* @param status exception에 대한 정보에 대한 enum
*/
public CustomServiceException(CustomExceptionStatus status) {
this.status = status;
}

public CustomExceptionStatus getStatus() {
return status;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ public ResponseEntity<?> serviceExceptionHandler(ServiceException e) {
.body(e.status);
}

@ExceptionHandler(CustomServiceException.class)
public ResponseEntity<?> customServiceExceptionHandler(CustomServiceException e) {
log.info("[CustomServiceException] {} : {}", e.status.getError(), e.status.getMessage());
return ResponseEntity
.status(e.status.getStatusCode())
.body(e.status);
}

@ExceptionHandler(DomainException.class)
public ResponseEntity<?> domainExceptionHandler(DomainException e) {
log.warn("[DomainException] {} : {}", e.status.getError(), e.status.getMessage());
Expand All @@ -33,4 +41,12 @@ public ResponseEntity<?> domainExceptionHandler(DomainException e) {
.body(e.status);
}

@ExceptionHandler(UtilException.class)
public ResponseEntity<?> utilExceptionHandler(UtilException e) {
log.warn("[UtilException] {} : {}", e.status.getError(), e.status.getMessage());
return ResponseEntity
.status(e.status.getStatusCode())
.body(e.status);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ public enum ExceptionStatus {
SHARE_BANNED_USER(HttpStatus.BAD_REQUEST, "SHARE 밴 상태의 유저입니다."),
NOT_FOUND_BAN_HISTORY(HttpStatus.NOT_FOUND, "현재 정지 상태인 유저가 아닙니다."),
BLACKHOLED_USER(HttpStatus.BAD_REQUEST, "블랙홀 상태의 유저입니다."),
BLACKHOLE_REFRESHING(HttpStatus.BAD_REQUEST, "블랙홀 갱신 중 입니다.\n잠시 후에 다시 시도해주세요."),
UNAUTHORIZED_ADMIN(HttpStatus.UNAUTHORIZED, "관리자 로그인 정보가 유효하지 않습니다\n다시 로그인해주세요"),
UNAUTHORIZED_USER(HttpStatus.UNAUTHORIZED, "사용자 로그인 정보가 유효하지 않습니다\n다시 로그인해주세요"),
EXTERNAL_API_EXCEPTION(HttpStatus.BAD_REQUEST, "외부 API와 통신 중 에러가 발생했습니다"),
EXISTED_CLUB_USER(HttpStatus.CONFLICT, "이미 존재하는 동아리 유저입니다"),
CLUB_HAS_LENT_CABINET(HttpStatus.NOT_ACCEPTABLE, "대여 중인 사물함을 반납 후 삭제할 수 있습니다."),
;


final private int statusCode;
final private String message;
final private String error;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.ftclub.cabinet.exception;

/**
* Util에서 throw하는 exception들을 위한 exception 사용 예시:
* <pre>
* {@code throw new UtilException(ExceptionStatus.NOT_FOUND_USER);}
*</pre>
* 만약 새로운 exception을 만들 필요가 있다면 {@link ExceptionStatus}에서 새로운 enum값을 추가하면 된다.
* @see org.ftclub.cabinet.exception.ExceptionStatus
*/

public class UtilException extends RuntimeException {

final ExceptionStatus status;

/**
* @param status exception에 대한 정보에 대한 enum
*/
public UtilException(ExceptionStatus status) {
this.status = status;
}

}
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
package org.ftclub.cabinet.lent.domain;

import java.time.LocalDate;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.ftclub.cabinet.cabinet.domain.Cabinet;
import org.ftclub.cabinet.cabinet.domain.CabinetStatus;
import org.ftclub.cabinet.cabinet.domain.LentType;
import org.ftclub.cabinet.config.CabinetProperties;
import org.ftclub.cabinet.dto.UserBlackholeInfoDto;
import org.ftclub.cabinet.exception.DomainException;
import org.ftclub.cabinet.exception.ExceptionStatus;
import org.ftclub.cabinet.user.domain.BanHistory;
import org.ftclub.cabinet.user.domain.User;
import org.ftclub.cabinet.user.domain.UserRole;
import org.ftclub.cabinet.utils.DateUtil;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.List;

@Component
@RequiredArgsConstructor
@Log4j2
public class LentPolicyImpl implements LentPolicy {

private final CabinetProperties cabinetProperties;
private final ApplicationEventPublisher publisher;


private LocalDateTime generateSharedCabinetExpirationDate(LocalDateTime now,
CabinetStatus cabinetStatus, LentHistory activeLentHistory) {
Expand Down Expand Up @@ -78,10 +79,11 @@ public LocalDateTime generateExpirationDate(LocalDateTime now, Cabinet cabinet,
@Override
public void applyExpirationDate(LentHistory curHistory, List<LentHistory> beforeActiveHistories,
LocalDateTime expiredAt) {
log.info("Called applyExpirationDate curHistory: {}, beforeActiveHistories: {}, expiredAt: {}",
log.info(
"Called applyExpirationDate curHistory: {}, beforeActiveHistories: {}, expiredAt: {}",
curHistory, beforeActiveHistories, expiredAt);

if (expiredAt == null){
if (expiredAt == null) {
throw new DomainException(ExceptionStatus.INVALID_ARGUMENT);
}

Expand All @@ -105,17 +107,15 @@ public LentPolicyStatus verifyUserForLent(User user, Cabinet cabinet, int userAc
if (userActiveLentCount >= 1) {
return LentPolicyStatus.ALREADY_LENT_USER;
}
// TODO: 현재 구조에서는 DB 정합성 문제를 일으키는 코드입니다.
// 블랙홀에 빠진 유저 대여 로직을 막는다고 하면, BlackholeManager.handleBlackhole()을 통해
// 실제 DB에 반영되기 전에 블랙홀에 빠진 유저를 걸러낼 수 있습니다.
// 하지만, 현재는 BlackholeManager <-> LentService 간의 순환 참조가 발생하는데,
// BlackholeManager는 스케줄러에 의해 빈에 등록되는 컴포넌트이므로
// 현재 구조상으로는 @Lazy 어노테이션을 통해 순환 참조 문제를 해결할 수 없습니다.
// 추후 다른 방식으로 구조적인 리팩토링이 필요한 부분입니다..!
// if (user.getBlackholedAt() != null && user.getBlackholedAt()
// .isBefore(LocalDateTime.now())) {
// return LentPolicyStatus.BLACKHOLED_USER;
// }
if (user.getBlackholedAt() != null && user.getBlackholedAt()
.isBefore(LocalDateTime.now())) {
publisher.publishEvent(UserBlackholeInfoDto.of(user));
if (user.getBlackholedAt() != null && user.getBlackholedAt()
.isBefore(LocalDateTime.now())) {
return LentPolicyStatus.BLACKHOLED_USER;
}
}

// 유저가 페널티 2 종류 이상 받을 수 있나? <- 실제로 그럴리 없지만 lentPolicy 객체는 그런 사실을 모르고, 유연하게 구현?
if (userActiveBanList == null || userActiveBanList.size() == 0) {
return LentPolicyStatus.FINE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import org.ftclub.cabinet.exception.ExceptionStatus;
import org.ftclub.cabinet.exception.ServiceException;
import org.ftclub.cabinet.lent.domain.LentHistory;
import org.ftclub.cabinet.lent.domain.LentPolicyStatus;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
Expand Down Expand Up @@ -91,41 +90,6 @@ public LentHistory getActiveLentHistoryWithUserId(Long userId) {
.orElseThrow(() -> new ServiceException(ExceptionStatus.NO_LENT_CABINET));
}

/**
* 정책에 대한 결과 상태({@link LentPolicyStatus})에 맞는 적절한 {@link ServiceException}을 throw합니다.
*
* @param status 정책에 대한 결과 상태
* @throws ServiceException 정책에 따라 다양한 exception이 throw될 수 있습니다.
*/
public void handlePolicyStatus(LentPolicyStatus status) {
log.info("Called handlePolicyStatus status: {}", status);
switch (status) {
case FINE:
break;
case BROKEN_CABINET:
throw new ServiceException(ExceptionStatus.LENT_BROKEN);
case FULL_CABINET:
throw new ServiceException(ExceptionStatus.LENT_FULL);
case OVERDUE_CABINET:
throw new ServiceException(ExceptionStatus.LENT_EXPIRED);
case LENT_CLUB:
throw new ServiceException(ExceptionStatus.LENT_CLUB);
case IMMINENT_EXPIRATION:
throw new ServiceException(ExceptionStatus.LENT_EXPIRE_IMMINENT);
case ALREADY_LENT_USER:
throw new ServiceException(ExceptionStatus.LENT_ALREADY_EXISTED);
case ALL_BANNED_USER:
throw new ServiceException(ExceptionStatus.ALL_BANNED_USER);
case SHARE_BANNED_USER:
throw new ServiceException(ExceptionStatus.SHARE_BANNED_USER);
case BLACKHOLED_USER:
throw new ServiceException(ExceptionStatus.BLACKHOLED_USER);
case NOT_USER:
case INTERNAL_ERROR:
default:
throw new ServiceException(ExceptionStatus.INTERNAL_SERVER_ERROR);
}
}

/**
* 사물함에 남은 자리가 있는 지 확인합니다. 남은 자리가 없으면 throw합니다.
Expand Down
Loading

0 comments on commit cebb63e

Please sign in to comment.