Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] EventUserArgumentResolver 적용 및 FE 요청에 따른 추가 API 구현 (#35) #38

Merged
merged 26 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
59eaf16
[chore] ConstantUtil에 상수 문자열 추가 (#35)
win-luck Aug 7, 2024
e0fe948
[feat] EventUserArgumentResolver 구현 (#35)
win-luck Aug 7, 2024
b1fc018
[chore] WebConfig resolver 반영 및 생성자 주입으로 변경 (#35)
win-luck Aug 7, 2024
17c0ad2
[fix] JWT 필터링 및 추출 과정 에러 수정 (#35)
win-luck Aug 7, 2024
fd263e3
[refactor] 기대평 생성 기능 ArgumentResolver 기반 리팩토링 (#35)
win-luck Aug 7, 2024
4a095b9
[test] 기대평 작성 관련 테스트코드 재작성 (#35)
win-luck Aug 7, 2024
96903b0
[chore] 인증번호 관련 로직 일부 표현 수정 (#35)
win-luck Aug 7, 2024
7c50d87
[refactor] FcfsController ArgumentResolver 적용 (#35)
win-luck Aug 7, 2024
732743c
[test] ArgumentResolver 관련 단위테스트 표현 수정 (#35)
win-luck Aug 7, 2024
b4f81dc
[chore] 여러 매직넘버 상수로 관리 (#35)
win-luck Aug 7, 2024
5348547
[feat] 인증번호 5분 유효기간 설정 및 예외처리 추가 (#35)
win-luck Aug 7, 2024
9c856c1
[chore] JWT 토큰 payLoad에 userName 추가 (#35)
win-luck Aug 7, 2024
d52bd05
[feat] 선착순 이벤트 정보 조회 구현 (#35)
win-luck Aug 7, 2024
2063d85
[fix] AuthInterceptor 예외처리 추가 (#35)
win-luck Aug 7, 2024
5864bea
[feat] 기대평 참여 상태 조회 기능 구현 (#35)
win-luck Aug 7, 2024
7fba82c
[chore] 참여자 여부 판정을 위한 Redis Key Format 도입 (#35)
win-luck Aug 7, 2024
191b4ff
[feat] 유저의 선착순 이벤트 참여 상태 조회 기능 구현 (#35)
win-luck Aug 7, 2024
2f7cf59
[feat] 타 선착순 이벤트 구현방안에도 참여 여부 조회 기능 적용 (#35)
win-luck Aug 7, 2024
7fa66a1
[build] jackson 의존성 추가 (#35)
win-luck Aug 7, 2024
81b36a2
[test] 신규 기능 추가에 따른 테스트코드 재작성 (#35)
win-luck Aug 7, 2024
149dead
[fix] 불필요하거나 잘못된 일부 표현 수정 (#35)
win-luck Aug 7, 2024
bf64b81
[test] CommentControllerTest 보강 및 swagger 예외처리 추가 (#35)
win-luck Aug 7, 2024
3348767
[chore] Fcfs 신규 기능 관련 잘못된 표현 수정 (#35)
win-luck Aug 7, 2024
70bd40b
[build] 불필요한 것으로 판정된 jackson 라이브러리 삭제 및 관련 코드 제거 (#35)
win-luck Aug 7, 2024
c6296c0
[test] 테스트코드 일부 업데이트 (#35)
win-luck Aug 7, 2024
544357d
[chore] 누락된 매직넘버 상수화 (#35)
win-luck Aug 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
import hyundai.softeer.orange.comment.service.ApiService;
import hyundai.softeer.orange.comment.service.CommentService;
import hyundai.softeer.orange.common.ErrorResponse;
import hyundai.softeer.orange.core.auth.Auth;
import hyundai.softeer.orange.core.auth.AuthRole;
import hyundai.softeer.orange.eventuser.component.EventUserAnnotation;
import hyundai.softeer.orange.eventuser.dto.EventUserInfo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
Expand Down Expand Up @@ -36,18 +40,34 @@ public ResponseEntity<ResponseCommentsDto> getComments() {
return ResponseEntity.ok(commentService.getComments());
}

@Auth(AuthRole.event_user)
@Tag(name = "Comment")
@PostMapping
@Operation(summary = "기대평 등록", description = "유저가 신규 기대평을 등록한다.", responses = {
@ApiResponse(responseCode = "200", description = "기대평 등록 성공",
content = @Content(schema = @Schema(implementation = Boolean.class))),
@ApiResponse(responseCode = "400", description = "기대평 등록 실패, 지나치게 부정적인 표현으로 간주될 때",
content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(responseCode = "404", description = "해당 정보를 갖는 유저나 이벤트가 존재하지 않을 때",
content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(responseCode = "409", description = "하루에 여러 번의 기대평을 작성하려 할 때",
content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
})
public ResponseEntity<Boolean> createComment(@RequestBody @Valid CreateCommentDto dto) {
public ResponseEntity<Boolean> createComment(@EventUserAnnotation EventUserInfo userInfo, @RequestBody @Valid CreateCommentDto dto) {
boolean isPositive = apiService.analyzeComment(dto.getContent());
return ResponseEntity.ok(commentService.createComment(dto, isPositive));
return ResponseEntity.ok(commentService.createComment(userInfo.getUserId(), dto, isPositive));
}

@Auth(AuthRole.event_user)
@Tag(name = "Comment")
@GetMapping("/info")
@Operation(summary = "기대평 등록 가능 여부 조회", description = "오늘 기대평 등록 가능 여부를 조회한다.", responses = {
@ApiResponse(responseCode = "200", description = "기대평 작성 가능 여부를 true/false로 반환한다.",
content = @Content(schema = @Schema(implementation = Boolean.class))),
@ApiResponse(responseCode = "404", description = "해당 정보를 갖는 유저가 존재하지 않을 때",
content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
})
public ResponseEntity<Boolean> isCommentable(@EventUserAnnotation EventUserInfo userInfo) {
return ResponseEntity.ok(commentService.isCommentable(userInfo.getUserId()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,13 @@
@NoArgsConstructor
public class CreateCommentDto {

@NotNull(message = MessageUtil.BAD_INPUT)
private Long eventUserId;

@NotNull(message = MessageUtil.BAD_INPUT)
private Long eventFrameId;

@Size(min = 1, max = 100, message = MessageUtil.OUT_OF_SIZE)
private String content;

public CreateCommentDto(Long eventUserId, Long eventFrameId, String content) {
this.eventUserId = eventUserId;
public CreateCommentDto(Long eventFrameId, String content) {
this.eventFrameId = eventFrameId;
this.content = content;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,31 @@ public ResponseCommentsDto getComments() {

// 신규 기대평을 등록한다.
@Transactional
public Boolean createComment(CreateCommentDto dto, Boolean isPositive) {
public Boolean createComment(String userId, CreateCommentDto dto, Boolean isPositive) {
EventUser eventUser = eventUserRepository.findByUserId(userId)
.orElseThrow(() -> new CommentException(ErrorCode.EVENT_USER_NOT_FOUND));
EventFrame eventFrame = eventFrameRepository.findById(dto.getEventFrameId())
.orElseThrow(() -> new CommentException(ErrorCode.EVENT_FRAME_NOT_FOUND));

// 하루에 여러 번의 기대평을 작성하려 할 때 예외처리
if(commentRepository.existsByCreatedDateAndEventUser(dto.getEventUserId())) {
if(commentRepository.existsByCreatedDateAndEventUser(eventUser.getId())) {
throw new CommentException(ErrorCode.COMMENT_ALREADY_EXISTS);
}

EventFrame eventFrame = eventFrameRepository.findById(dto.getEventFrameId())
.orElseThrow(() -> new CommentException(ErrorCode.EVENT_FRAME_NOT_FOUND));
EventUser eventUser = eventUserRepository.findById(dto.getEventUserId())
.orElseThrow(() -> new CommentException(ErrorCode.EVENT_USER_NOT_FOUND));
// TODO: 점수정책와 연계하여 기대평 등록 시 점수를 부여 추가해야함
Comment comment = Comment.of(dto.getContent(), eventFrame, eventUser, isPositive);
commentRepository.save(comment);
return true;
}

// 오늘 이 유저가 기대평을 작성할 수 있는지 여부를 조회한다.
@Transactional(readOnly = true)
public Boolean isCommentable(String userId) {
EventUser eventUser = eventUserRepository.findByUserId(userId)
.orElseThrow(() -> new CommentException(ErrorCode.EVENT_USER_NOT_FOUND));
return !commentRepository.existsByCreatedDateAndEventUser(eventUser.getId());
}

// 기대평을 삭제한다. 이 동작을 실행하는 주체가 어드민임이 반드시 검증되어야 한다.
@Transactional
public Long deleteComment(Long commentId) {
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/hyundai/softeer/orange/common/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ public enum ErrorCode {
INVALID_EVENT_TIME(HttpStatus.BAD_REQUEST, false, "이벤트 시간이 아닙니다."),
INVALID_EVENT_TYPE(HttpStatus.BAD_REQUEST, false, "이벤트 타입이 지원되지 않습니다."),


// 401 Unauthorized
UNAUTHORIZED(HttpStatus.UNAUTHORIZED, false, "인증되지 않은 사용자입니다."),
AUTHENTICATION_FAILED(HttpStatus.UNAUTHORIZED, false, "아이디 또는 비밀번호가 일치하지 않습니다"),
INVALID_AUTH_CODE(HttpStatus.UNAUTHORIZED, false, "인증번호가 일치하지 않습니다."),
SESSION_EXPIRED(HttpStatus.UNAUTHORIZED, false, "세션이 만료되었습니다."),
AUTH_CODE_EXPIRED(HttpStatus.UNAUTHORIZED, false, "인증번호가 만료되었거나 존재하지 않습니다."),

// 403 Forbidden
FORBIDDEN(HttpStatus.FORBIDDEN, false, "권한이 없습니다."),
Expand All @@ -45,6 +45,7 @@ public enum ErrorCode {
COMMENT_ALREADY_EXISTS(HttpStatus.CONFLICT, false, "이미 등록된 기대평입니다."),
ADMIN_USER_ALREADY_EXISTS(HttpStatus.CONFLICT, false, "이미 존재하는 관리자입니다."),
ALREADY_WINNER(HttpStatus.CONFLICT, false, "이미 당첨된 사용자입니다."),
ALREADY_PARTICIPATED(HttpStatus.CONFLICT, false, "이미 참여한 사용자입니다."),
PHONE_NUMBER_ALREADY_EXISTS(HttpStatus.CONFLICT, false, "이미 존재하는 전화번호입니다."),

// 500 Internal Server Error
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,21 @@ public class ConstantUtil {
public static final String PHONE_NUMBER_REGEX = "010\\d{8}"; // 010 + 8자리 숫자
public static final String AUTH_CODE_REGEX = "\\d{6}"; // 6자리 숫자
public static final String AUTH_CODE_CREATE_REGEX = "%06d";
public static final String CLAIMS_KEY = "user";
public static final String CLAIMS_USER_KEY = "userId";
public static final String CLAIMS_ROLE_KEY = "role";
public static final String CLAIMS_USER_NAME_KEY = "userName";
public static final String JWT_USER_KEY = "eventUser";
public static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
public static final String LOCATION = "Location";

public static final double LIMIT_NEGATIVE_CONFIDENCE = 99.5;
public static final int COMMENTS_SIZE = 20;
public static final int SCHEDULED_TIME = 1000 * 60 * 60 * 2;
public static final int SHORT_URL_LENGTH = 10;
public static final int USER_ID_LENGTH = 8;
public static final int AUTH_CODE_LENGTH = 6;
public static final int JWT_LIFESPAN = 1; // 1시간
public static final int AUTH_CODE_EXPIRE_TIME = 5; // 5분
Comment on lines +15 to +29
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

파편화된 Key 문자열과 매직넘버를 상수로 관리하고자 했습니다.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋은 것 같습니다. 저도 여기에다 admin 부분을 등록해서 변경해둬야겠네요

public static final int FCFS_AVAILABLE_HOUR = 7;
public static final int FCFS_COUNTDOWN_HOUR = 3;
}
11 changes: 7 additions & 4 deletions src/main/java/hyundai/softeer/orange/config/WebConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import hyundai.softeer.orange.admin.component.AdminArgumentResolver;
import hyundai.softeer.orange.core.auth.AuthInterceptor;
import hyundai.softeer.orange.eventuser.component.EventUserArgumentResolver;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
Expand All @@ -10,13 +12,13 @@

import java.util.List;

@RequiredArgsConstructor
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private AuthInterceptor authInterceptor;

@Autowired
private AdminArgumentResolver adminArgumentResolver;
private final AuthInterceptor authInterceptor;
private final AdminArgumentResolver adminArgumentResolver;
private final EventUserArgumentResolver eventUserArgumentResolver;
Comment on lines +15 to +21
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

필드 주입이 3개 이상이라 생성자 주입으로 바꾸었습니다.


@Override
public void addInterceptors(InterceptorRegistry registry) {
Expand All @@ -28,5 +30,6 @@ public void addInterceptors(InterceptorRegistry registry) {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(adminArgumentResolver);
resolvers.add(eventUserArgumentResolver);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
import hyundai.softeer.orange.core.jwt.JWTManager;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import java.util.*;

@Slf4j
@Component // 문제 있으면 변경
public class AuthInterceptor implements HandlerInterceptor {
private final JWTManager jwtManager;
Expand All @@ -21,6 +23,11 @@ public AuthInterceptor(JWTManager jwtManager) {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 유효하지 않은 요청은 정적 리소스로 간주하여 ResourceHttpRequestHandler가 대신 처리하기에, HandlerMethod가 아닌 경우는 무시
if (!(handler instanceof HandlerMethod)) {
return true;
}

Comment on lines +26 to +30
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

404가 발생해야 하는데 요청을 가로채는 과정에서 500이 뜨던 버그가 있어 수정했습니다.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

내부 경로가 아니면 HandlerMethod가 아닌 handler이 올 수도 있다는 것을 새롭게 알게 되었네요. 기존에 swagger-ui 경로에 접근하지 못하던 문제도 이걸로 처리할 수 있었겠네요

HandlerMethod handlerMethod = (HandlerMethod) handler;

Auth classAnnotation = handlerMethod.getClass().getAnnotation(Auth.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package hyundai.softeer.orange.event.fcfs.controller;

import hyundai.softeer.orange.common.ErrorResponse;
import hyundai.softeer.orange.core.auth.Auth;
import hyundai.softeer.orange.core.auth.AuthRole;
import hyundai.softeer.orange.event.fcfs.dto.ResponseFcfsInfoDto;
import hyundai.softeer.orange.event.fcfs.dto.ResponseFcfsResultDto;
import hyundai.softeer.orange.event.fcfs.service.FcfsAnswerService;
import hyundai.softeer.orange.event.fcfs.service.FcfsManageService;
import hyundai.softeer.orange.event.fcfs.service.FcfsService;
import hyundai.softeer.orange.eventuser.component.EventUserAnnotation;
import hyundai.softeer.orange.eventuser.dto.EventUserInfo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
Expand All @@ -21,7 +27,9 @@ public class FcfsController {

private final FcfsService fcfsService;
private final FcfsAnswerService fcfsAnswerService;
private final FcfsManageService fcfsManageService;

@Auth(AuthRole.event_user)
@Tag(name = "fcfs")
@PostMapping
@Operation(summary = "선착순 이벤트 참여", description = "선착순 이벤트에 참여한 결과(boolean)를 반환한다.", responses = {
Expand All @@ -30,9 +38,34 @@ public class FcfsController {
@ApiResponse(responseCode = "400", description = "선착순 이벤트 시간이 아니거나, 요청 형식이 잘못된 경우",
content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
})
public ResponseEntity<ResponseFcfsResultDto> participate(@RequestParam Long eventSequence, @RequestParam String userId, @RequestParam String eventAnswer) {
public ResponseEntity<ResponseFcfsResultDto> participate(@EventUserAnnotation EventUserInfo userInfo, @RequestParam Long eventSequence, @RequestParam String eventAnswer) {
boolean answerResult = fcfsAnswerService.judgeAnswer(eventSequence, eventAnswer);
boolean isWin = answerResult && fcfsService.participate(eventSequence, userId);
boolean isWin = answerResult && fcfsService.participate(eventSequence, userInfo.getUserId());
return ResponseEntity.ok(new ResponseFcfsResultDto(answerResult, isWin));
}

@Tag(name = "fcfs")
@GetMapping("/{eventSequence}/info")
@Operation(summary = "특정 선착순 이벤트의 정보 조회", description = "특정 선착순 이벤트에 대한 정보(서버 기준 시각, 이벤트의 상태)를 반환한다.", responses = {
@ApiResponse(responseCode = "200", description = "선착순 이벤트에 대한 상태 정보",
content = @Content(schema = @Schema(implementation = ResponseFcfsInfoDto.class))),
@ApiResponse(responseCode = "404", description = "선착순 이벤트를 찾을 수 없는 경우",
content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
})
public ResponseEntity<ResponseFcfsInfoDto> getFcfsInfo(@PathVariable Long eventSequence) {
return ResponseEntity.ok(fcfsManageService.getFcfsInfo(eventSequence));
}

@Auth(AuthRole.event_user)
@Tag(name = "fcfs")
@GetMapping("/participated")
@Operation(summary = "선착순 이벤트 참여 여부 조회", description = "정답을 맞혀서 선착순 이벤트에 참여했는지 여부를 조회한다. (당첨은 별도)", responses = {
@ApiResponse(responseCode = "200", description = "선착순 이벤트의 정답을 맞혀서 참여했는지에 대한 결과",
content = @Content(schema = @Schema(implementation = ResponseFcfsResultDto.class))),
@ApiResponse(responseCode = "404", description = "선착순 이벤트를 찾을 수 없는 경우",
content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
})
public ResponseEntity<Boolean> isParticipated(@EventUserAnnotation EventUserInfo userInfo, @RequestParam Long eventSequence) {
return ResponseEntity.ok(fcfsManageService.isParticipated(eventSequence, userInfo.getUserId()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package hyundai.softeer.orange.event.fcfs.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class ResponseFcfsInfoDto {

private LocalDateTime nowDateTime;

private String eventStatus;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package hyundai.softeer.orange.event.fcfs.service;

import hyundai.softeer.orange.common.ErrorCode;
import hyundai.softeer.orange.common.util.ConstantUtil;
import hyundai.softeer.orange.event.fcfs.dto.ResponseFcfsInfoDto;
import hyundai.softeer.orange.event.fcfs.dto.ResponseFcfsWinnerDto;
import hyundai.softeer.orange.event.fcfs.entity.FcfsEvent;
import hyundai.softeer.orange.event.fcfs.entity.FcfsEventWinningInfo;
Expand Down Expand Up @@ -69,6 +71,38 @@ public void registerWinners() {
}
}

// 특정 선착순 이벤트의 정보 조회
public ResponseFcfsInfoDto getFcfsInfo(Long eventSequence) {
String startTime = stringRedisTemplate.opsForValue().get(FcfsUtil.startTimeFormatting(eventSequence.toString()));
// 선착순 이벤트가 존재하지 않는 경우
if (startTime == null) {
throw new FcfsEventException(ErrorCode.FCFS_EVENT_NOT_FOUND);
}

LocalDateTime nowDateTime = LocalDateTime.now();
LocalDateTime eventStartTime = LocalDateTime.parse(startTime);

// 서버시간 < 이벤트시작시간 < 서버시간+3시간 -> countdown
// 이벤트시작시간 < 서버시간 < 이벤트시작시간+7시간 -> progress
// 그 외 -> waiting
if(nowDateTime.isBefore(eventStartTime) && nowDateTime.plusHours(ConstantUtil.FCFS_COUNTDOWN_HOUR).isAfter(eventStartTime)) {
return new ResponseFcfsInfoDto(nowDateTime, "countdown");
} else if(eventStartTime.isBefore(nowDateTime) && eventStartTime.plusHours(ConstantUtil.FCFS_AVAILABLE_HOUR).isAfter(nowDateTime)) {
return new ResponseFcfsInfoDto(nowDateTime, "progress");
} else {
return new ResponseFcfsInfoDto(nowDateTime, "waiting");
}
}

// 특정 유저가 선착순 이벤트의 참여자인지 조회 (정답을 맞힌 경우 참여자로 간주)
@Transactional(readOnly = true)
public Boolean isParticipated(Long eventSequence, String userId) {
if(!fcfsEventRepository.existsById(eventSequence)) {
throw new FcfsEventException(ErrorCode.FCFS_EVENT_NOT_FOUND);
}
return Boolean.TRUE.equals(stringRedisTemplate.opsForSet().isMember(FcfsUtil.participantFormatting(eventSequence.toString()), userId));
}

// 특정 선착순 이벤트의 당첨자 조회 - 어드민에서 사용
@Transactional(readOnly = true)
public List<ResponseFcfsWinnerDto> getFcfsWinnersInfo(Long eventSequence) {
Expand All @@ -94,6 +128,8 @@ private void prepareEventInfo(FcfsEvent event) {

public void deleteEventInfo(String eventId) {
stringRedisTemplate.delete(FcfsUtil.startTimeFormatting(eventId));
stringRedisTemplate.delete(FcfsUtil.answerFormatting(eventId));
stringRedisTemplate.delete(FcfsUtil.participantFormatting(eventId));
stringRedisTemplate.delete(FcfsUtil.winnerFormatting(eventId));
numberRedisTemplate.delete(FcfsUtil.keyFormatting(eventId));
booleanRedisTemplate.delete(FcfsUtil.endFlagFormatting(eventId));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@ public boolean participate(Long eventSequence, String userId) {
String fcfsId = FcfsUtil.keyFormatting(eventSequence.toString());
// 불필요한 Lock 접근을 막기 위한 종료 flag 확인
if (isEventEnded(fcfsId)) {
stringRedisTemplate.opsForSet().add(FcfsUtil.participantFormatting(eventSequence.toString()), userId);
return false;
}

// 이미 당첨된 사용자인지 확인
if(stringRedisTemplate.opsForZSet().rank(FcfsUtil.winnerFormatting(eventSequence.toString()), userId) != null){
if(stringRedisTemplate.opsForSet().isMember(FcfsUtil.winnerFormatting(eventSequence.toString()), userId) != null) {
throw new FcfsEventException(ErrorCode.ALREADY_WINNER);
}

Expand Down Expand Up @@ -65,6 +66,7 @@ public boolean participate(Long eventSequence, String userId) {

numberRedisTemplate.opsForValue().decrement(fcfsId);
stringRedisTemplate.opsForZSet().add(FcfsUtil.winnerFormatting(eventSequence.toString()), userId, System.currentTimeMillis());
stringRedisTemplate.opsForSet().add(FcfsUtil.participantFormatting(eventSequence.toString()), userId);
log.info("{} - 이벤트 참여 성공, 잔여 쿠폰: {}", userId, availableCoupons(fcfsId));
return true;
} catch (InterruptedException e) {
Expand Down
Loading
Loading