Skip to content

Commit

Permalink
선착순 이벤트 종료 시, 응모를 시도하면 실패 모달정보를 보내주는 기능 구현 (#195)
Browse files Browse the repository at this point in the history
* [Infra] CI/CD test (#42)

* infra: 빌드 테스트 yml 작성

* infra: DB 정보 추가

* infra: ssh-agent 버전 변경

* infra: known_hosts 추가

* infra: db port 변경

* infra: database test 설정 변경

* infra: DB 환경변수 설정 및 application.yml 생성

* infra: application.yml 동적 생성 스크립트 수정

* infra: 레디스 설정 추가

* infra: redis test 추가

* infra: redis 버전 변경

* infra: redis cli 설치

* infra: application.yml 위치 및 내용 확인

* infra: Github Actions 환경변수에 REDIS_HOST, REDIS_PORT 추가

* infra: 환경변수 확인 추가

* infra: zip file 만들기 추가, AWS credentials 추가

* infra: 환경변수 이름 변경

- ARN -> AWS_ARN

* infra: s3 bucket에 업로드 추가

* infra: code deploy 추가

* infra: code deploy 수정

* infra: code deploy 수정

* infra: appspec.yml 작성

* infra: application.yml 생성 경로 변경

* infra: application.yml 확인 스크립트 삭제

* infra: application.yml 생성 스크립트 수정

* infra: application-prod.yml 추가

* infra: appspec.yml 수정, 배포를 위한 sh파일 추가

* infra: deploy.yml 이름 변경

- test_deploy -> deploy

* infra: body = null 설정

* infra: develop에 머지되었을 때만 발동하도록 수정

* feat: draw_rank column 이름 수정

* Infra: environment 삭제

* [Infra] CI CD test 3 (#45)

* infra: 빌드 테스트 yml 작성

* infra: DB 정보 추가

* infra: ssh-agent 버전 변경

* infra: known_hosts 추가

* infra: db port 변경

* infra: database test 설정 변경

* infra: DB 환경변수 설정 및 application.yml 생성

* infra: application.yml 동적 생성 스크립트 수정

* infra: 레디스 설정 추가

* infra: redis test 추가

* infra: redis 버전 변경

* infra: redis cli 설치

* infra: application.yml 위치 및 내용 확인

* infra: Github Actions 환경변수에 REDIS_HOST, REDIS_PORT 추가

* infra: 환경변수 확인 추가

* infra: zip file 만들기 추가, AWS credentials 추가

* infra: 환경변수 이름 변경

- ARN -> AWS_ARN

* infra: s3 bucket에 업로드 추가

* infra: code deploy 추가

* infra: code deploy 수정

* infra: code deploy 수정

* infra: appspec.yml 작성

* infra: application.yml 생성 경로 변경

* infra: application.yml 확인 스크립트 삭제

* infra: application.yml 생성 스크립트 수정

* infra: application-prod.yml 추가

* infra: appspec.yml 수정, 배포를 위한 sh파일 추가

* infra: deploy.yml 이름 변경

- test_deploy -> deploy

* infra: body = null 설정

* infra: develop에 머지되었을 때만 발동하도록 수정

* feat: draw_rank column 이름 수정

* Infra: environment 삭제

* Infra: environment 삭제

* config: jwt 속성을 yml에 설정

* rebase: 원본 develop 브랜치와 병합

* doc: jacoco 파일 생성

* feat: EventLockException 처리 메서드 구현

* fix: url 변경

* refactor: 변수명 변경

* feat: 중복 응모에 대한 처리 구현

* fix: 변수 바인딩 수정

* feat: post 요청에 대한 처리 구현

* feat: swagger 파리미터 안보이도록 설정

* rebase: 원본 repo develop 브랜치와 rebase

* feat: 정적 텍스트 상수 추가

* chore: jacoco 삭제

---------

Co-authored-by: DrRivaski <[email protected]>
Co-authored-by: hyeokson <[email protected]>
  • Loading branch information
3 people authored Aug 22, 2024
1 parent a6103a0 commit 2062c7c
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public ResponseDto<FcfsResultResponseDto> handleFcfs(@Parameter(hidden = true) H
return ResponseDto.onSuccess(fcfsResultResponseDto);
}

@GetMapping("/fcfs/history")
@GetMapping("/history")
public ResponseDto<FcfsHistoryResponseDto> getFcfsHistory(@Parameter(hidden = true) @AuthInfo Integer userId){

FcfsHistoryResponseDto fcfsHistoryResponseDto = fcfsService.getFcfsHistory(userId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
public class FcfsResultResponseDto {

@JsonProperty("isFcfsWinner")
private boolean isFcfsWinner;
private boolean fcfsWinner;

private FcfsResult fcfsResult;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.softeer.backend.fo_domain.fcfs.exception.FcfsException;
import com.softeer.backend.fo_domain.fcfs.service.FcfsSettingManager;
import com.softeer.backend.global.common.code.status.ErrorStatus;
import io.micrometer.core.ipc.http.HttpSender;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -39,8 +40,16 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons

if (!fcfsSettingManager.isFcfsEntryAvailable(now)) {

log.error("Cannot access the FCFS event");
throw new FcfsException(ErrorStatus._BAD_REQUEST);
if("GET".equalsIgnoreCase(request.getMethod())){
log.error("Cannot access the FCFS event");
throw new FcfsException(ErrorStatus._BAD_REQUEST);
}

else if("POST".equalsIgnoreCase(request.getMethod())){
log.error("Cannot participate FCFS event");
throw new FcfsException(ErrorStatus._FCFS_ALREADY_CLOSED);
}

}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ public FcfsResultResponseDto handleFcfsEvent(int userId, int round, FcfsRequestD
if (fcfsSettingManager.isFcfsClosed()) {
countFcfsParticipant(round);

return getFcfsResult(false, null);
return getFcfsResult(false, false, null);
}

// 선착순 등록을 처리하는 메서드 호출
Expand Down Expand Up @@ -156,10 +156,14 @@ public FcfsResultResponseDto saveFcfsWinners(int userId, int round) {
fcfsSettingManager.setFcfsClosed(true);
}

return getFcfsResult(true, code);
return getFcfsResult(true, false, code);
}
else if(numOfWinners < fcfsSettingManager.getFcfsWinnerNum()
&& fcfsRedisUtil.isValueInIntegerSet(RedisKeyPrefix.FCFS_USERID_PREFIX.getPrefix() + round, userId))
return getFcfsResult(false, true, null);

return getFcfsResult(false, null);

return getFcfsResult(false, false, null);

}

Expand All @@ -182,31 +186,26 @@ private void countFcfsParticipant(int round) {
/**
* 선착순 결과 모달 응답 Dto를 만들어서 반환하는 메서드
*/
public FcfsResultResponseDto getFcfsResult(boolean fcfsWin, String fcfsCode) {

Map<String, String> textContentMap = staticResourceUtil.getTextContentMap();
Map<String, String> s3ContentMap = staticResourceUtil.getS3ContentMap();
public FcfsResultResponseDto getFcfsResult(boolean fcfsWin, boolean isDuplicated, String fcfsCode) {

FcfsSettingDto firstFcfsSetting = fcfsSettingManager.getFcfsSettingByRound(1);

FcfsService fcfsService = fcfsServiceProvider.getObject();

if (fcfsWin) {
FcfsSuccessResult fcfsSuccessResult = fcfsService.getFcfsSuccessResult(
textContentMap, s3ContentMap, firstFcfsSetting
);
FcfsSuccessResult fcfsSuccessResult = fcfsService.getFcfsSuccessResult(firstFcfsSetting);
fcfsSuccessResult.setFcfsCode(fcfsCode);

return FcfsResultResponseDto.builder()
.isFcfsWinner(fcfsWin)
.fcfsWinner(fcfsWin)
.fcfsResult(fcfsSuccessResult)
.build();
}

FcfsFailResult fcfsFailResult = fcfsService.getFcfsFailResult(textContentMap);
FcfsFailResult fcfsFailResult = fcfsService.getFcfsFailResult(isDuplicated);

return FcfsResultResponseDto.builder()
.isFcfsWinner(fcfsWin)
.fcfsWinner(fcfsWin)
.fcfsResult(fcfsFailResult)
.build();
}
Expand All @@ -215,8 +214,10 @@ public FcfsResultResponseDto getFcfsResult(boolean fcfsWin, String fcfsCode) {
* 선착순 당첨 모달 정보 중, 정적 정보를 반환하는 메서드
*/
@Cacheable(value = "staticResources", key = "'fcfsSuccess'")
public FcfsSuccessResult getFcfsSuccessResult(Map<String, String> textContentMap, Map<String, String> s3ContentMap,
FcfsSettingDto firstFcfsSetting) {
public FcfsSuccessResult getFcfsSuccessResult(FcfsSettingDto firstFcfsSetting) {

Map<String, String> textContentMap = staticResourceUtil.getTextContentMap();
Map<String, String> s3ContentMap = staticResourceUtil.getS3ContentMap();

return FcfsSuccessResult.builder()
.title(staticResourceUtil.format(textContentMap.get(StaticTextName.FCFS_WINNER_TITLE.name()),
Expand All @@ -235,9 +236,17 @@ public FcfsSuccessResult getFcfsSuccessResult(Map<String, String> textContentMap
/**
* 선착순 실패 모달 정보 중, 정적 정보를 반환하는 메서드
*/
@Cacheable(value = "staticResources", key = "'fcfsFail'")
public FcfsFailResult getFcfsFailResult(Map<String, String> textContentMap) {
@Cacheable(value = "staticResources", key = "'fcfsFail_' + #isDuplicated")
public FcfsFailResult getFcfsFailResult(boolean isDuplicated) {
Map<String, String> textContentMap = staticResourceUtil.getTextContentMap();

if(isDuplicated){
return FcfsFailResult.builder()
.title(textContentMap.get(StaticTextName.FCFS_DUPLICATED_TITLE.name()))
.subTitle(textContentMap.get(StaticTextName.FCFS_DUPLICATED_SUBTITLE.name()))
.caution(textContentMap.get(StaticTextName.FCFS_LOSER_CAUTION.name()))
.build();
}
return FcfsFailResult.builder()
.title(textContentMap.get(StaticTextName.FCFS_LOSER_TITLE.name()))
.subTitle(textContentMap.get(StaticTextName.FCFS_LOSER_SUBTITLE.name()))
Expand All @@ -250,11 +259,21 @@ public FcfsFailResult getFcfsFailResult(Map<String, String> textContentMap) {
*/
public FcfsHistoryResponseDto getFcfsHistory(int userId){
fcfsRepository.findByUserIdOrderByWinningDateAsc(userId);
List<FcfsHistoryResponseDto.FcfsHistory> fcfsHistoryList = new ArrayList<>();

Map<String, String> s3ContentMap = staticResourceUtil.getS3ContentMap();

LocalDate now = LocalDate.now();

List<Fcfs> fcfsList = fcfsRepository.findByUserIdOrderByWinningDateAsc(userId);
List<FcfsHistoryResponseDto.FcfsHistory> fcfsHistoryList = new ArrayList<>(fcfsList.stream()
.map((fcfs) ->
FcfsHistoryResponseDto.FcfsHistory.builder()
.barcode(s3ContentMap.get(S3FileName.BARCODE_IMAGE.name()))
.fcfsCode(fcfs.getCode())
.winningDate(fcfs.getWinningDate())
.build()
).toList());

Integer round = fcfsSettingManager.getFcfsRoundForHistory(now);
if(round == null)
round = fcfsSettingManager.getFcfsRoundForHistory(now.minusDays(1));
Expand All @@ -277,16 +296,6 @@ public FcfsHistoryResponseDto getFcfsHistory(int userId){
}
}

List<Fcfs> fcfsList = fcfsRepository.findByUserIdOrderByWinningDateAsc(userId);
fcfsHistoryList.addAll(fcfsList.stream()
.map((fcfs) ->
FcfsHistoryResponseDto.FcfsHistory.builder()
.barcode(s3ContentMap.get(S3FileName.BARCODE_IMAGE.name()))
.fcfsCode(fcfs.getCode())
.winningDate(fcfs.getWinningDate())
.build()
).toList());

return FcfsHistoryResponseDto.builder()
.isFcfsWin(!fcfsHistoryList.isEmpty())
.fcfsHistoryList(fcfsHistoryList)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@ public MainPageEventInfoResponseDto getEventPageInfo() {

String fcfsTime = "";
if(firstFcfsSetting.getStartTime().getMinute() != 0){
fcfsTime = firstFcfsSetting.getStartTime().format(fcfsTimeFormatter);
fcfsTime = firstFcfsSetting.getStartTime().format(fcfsTimeMinFormatter);
}
else
fcfsTime = firstFcfsSetting.getStartTime().format(fcfsTimeMinFormatter);
fcfsTime = firstFcfsSetting.getStartTime().format(fcfsTimeFormatter);

return MainPageEventInfoResponseDto.builder()
.startDate(drawSettingManager.getStartDate().format(eventTimeFormatter))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.softeer.backend.global.common.dto.JwtTokenResponseDto;
import com.softeer.backend.fo_domain.user.service.LoginService;
import com.softeer.backend.global.common.response.ResponseDto;
import io.swagger.v3.oas.annotations.Parameter;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
Expand All @@ -22,6 +23,7 @@ public class LoginController {
*/
@PostMapping("/login")
ResponseDto<JwtTokenResponseDto> handleLogin(@Valid @RequestBody LoginRequestDto loginRequestDto,
@Parameter(hidden = true)
@RequestHeader(value = "X-Share-Code", required = false) String shareCode) {
JwtTokenResponseDto jwtTokenResponseDto = loginService.handleLogin(loginRequestDto, shareCode);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ public enum ErrorStatus implements BaseErrorCode {
_AUTH_CODE_ISSUE_LIMIT_EXCEEDED(HttpStatus.BAD_REQUEST, "A403",
"인증 코드 발급 횟수를 초과하였습니다. 나중에 다시 시도하세요."),
_AUTH_CODE_NOT_VERIFIED(HttpStatus.BAD_REQUEST, "A404", "인증되지 않은 상태에서 로그인 할 수 없습니다."),
_AUTH_USERNAME_NOT_MATCH(HttpStatus.BAD_REQUEST, "A405", "이미 등록된 번호입니다.");
_AUTH_USERNAME_NOT_MATCH(HttpStatus.BAD_REQUEST, "A405", "이미 등록된 번호입니다."),

// FCFS ERROR
_FCFS_ALREADY_CLOSED(HttpStatus.BAD_REQUEST, "F400", "이미 선착순 이벤트가 마감되었습니다.");

// 예외의 Http 상태값
private final HttpStatus httpStatus;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package com.softeer.backend.global.common.exception;

import com.softeer.backend.fo_domain.fcfs.dto.result.FcfsFailResult;
import com.softeer.backend.fo_domain.fcfs.dto.result.FcfsResultResponseDto;
import com.softeer.backend.fo_domain.fcfs.exception.FcfsException;
import com.softeer.backend.global.common.code.status.ErrorStatus;
import com.softeer.backend.global.common.response.ResponseDto;
import com.softeer.backend.global.staticresources.constant.StaticTextName;
import com.softeer.backend.global.staticresources.util.StaticResourceUtil;
import jakarta.validation.ConstraintViolationException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpHeaders;
Expand All @@ -13,7 +19,6 @@
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import java.util.*;
Expand All @@ -24,8 +29,11 @@
*/
@Slf4j
@RestControllerAdvice
@RequiredArgsConstructor
public class ExceptionAdvice extends ResponseEntityExceptionHandler {

private final StaticResourceUtil staticResourceUtil;

/**
* GeneralException을 처리하는 메서드
*
Expand All @@ -39,11 +47,22 @@ public ResponseEntity<Object> handleGeneralException(GeneralException generalExc
return handleGeneralExceptionInternal(generalException, errorReasonHttpStatus, HttpHeaders.EMPTY, webRequest);
}

/**
* 분산락 예외를 처리하는 메서드
*/
@ExceptionHandler
public ModelAndView handleEventLockException(EventLockException eventLockException, WebRequest webRequest) {
public ResponseEntity<Object> handleEventLockException(EventLockException eventLockException, WebRequest webRequest) {
return handleEventLockExceptionInternal(eventLockException, HttpHeaders.EMPTY, webRequest);
}

/**
* 선착순 예외를 처리하는 메서드
*/
@ExceptionHandler
public ResponseEntity<Object> handleFcfsException(FcfsException FcfsException, WebRequest webRequest) {
return handleFcfsExceptionInternal(FcfsException, HttpHeaders.EMPTY, webRequest);
}

/**
* ConstraintViolationException을 처리하는 메서드
*
Expand Down Expand Up @@ -130,24 +149,66 @@ private ResponseEntity<Object> handleGeneralExceptionInternal(Exception e, Respo
}

// EventLockException에 대한 client 응답 객체를 생성하는 메서드
private ModelAndView handleEventLockExceptionInternal(EventLockException e, HttpHeaders headers, WebRequest webRequest) {
private ResponseEntity<Object> handleEventLockExceptionInternal(EventLockException e, HttpHeaders headers, WebRequest webRequest) {
Map<String, String> textContentMap = staticResourceUtil.getTextContentMap();

log.error("EventLockException captured in ExceptionAdvice", e);

String redissonKeyName = e.getRedissonKeyName();

ModelAndView modelAndView = new ModelAndView();
ResponseDto<Object> body = null;

if (redissonKeyName.contains("FCFS")) {

modelAndView.setViewName("redirect:/fcfs/result");
modelAndView.addObject("fcfsWin", false);
body = ResponseDto.onSuccess(FcfsResultResponseDto.builder()
.fcfsWinner(false)
.fcfsResult(FcfsFailResult.builder()
.title(textContentMap.get(StaticTextName.FCFS_LOSER_TITLE.name()))
.subTitle(textContentMap.get(StaticTextName.FCFS_LOSER_SUBTITLE.name()))
.caution(textContentMap.get(StaticTextName.FCFS_LOSER_CAUTION.name()))
.build())
.build());
}


//TODO
// DRAW 관련 예외일 경우, body 구성하는 코드 필요

return modelAndView;
return super.handleExceptionInternal(
e,
body,
headers,
HttpStatus.OK,
webRequest
);
}

// FcfsException 대한 client 응답 객체를 생성하는 메서드
private ResponseEntity<Object> handleFcfsExceptionInternal(FcfsException e, HttpHeaders headers, WebRequest webRequest) {
Map<String, String> textContentMap = staticResourceUtil.getTextContentMap();

ResponseDto<Object> body = null;

if (e.getCode() == ErrorStatus._FCFS_ALREADY_CLOSED) {
body = ResponseDto.onSuccess(FcfsResultResponseDto.builder()
.fcfsWinner(false)
.fcfsResult(FcfsFailResult.builder()
.title(textContentMap.get(StaticTextName.FCFS_CLOSED_TITLE.name()))
.subTitle(textContentMap.get(StaticTextName.FCFS_CLOSED_SUBTITLE.name()))
.caution(textContentMap.get(StaticTextName.FCFS_LOSER_CAUTION.name()))
.build())
.build());
} else {
body = ResponseDto.onFailure(e.getCode());
}


return super.handleExceptionInternal(
e,
body,
headers,
HttpStatus.OK,
webRequest
);
}

// ConstraintViolationException에 대한 client 응답 객체를 생성하는 메서드
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ public enum StaticTextName {

FCFS_LOSER_TITLE,
FCFS_LOSER_SUBTITLE,
FCFS_LOSER_CAUTION;
FCFS_LOSER_CAUTION,

// 선착순 이벤트 종료로 인한 응모 실패 모달
FCFS_CLOSED_TITLE,
FCFS_CLOSED_SUBTITLE,

// 선착순 이벤트 당첨된 상황에서 중복으로 응모할 때의 실패 모달
FCFS_DUPLICATED_TITLE,
FCFS_DUPLICATED_SUBTITLE;

}
Loading

0 comments on commit 2062c7c

Please sign in to comment.