Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
@@ -0,0 +1,51 @@
package com.threestar.trainus.domain.test.controller;

import com.threestar.trainus.domain.coupon.user.dto.CreateUserCouponResponseDto;
import com.threestar.trainus.domain.coupon.user.service.CouponService;
import com.threestar.trainus.domain.lesson.student.dto.LessonApplicationResponseDto;
import com.threestar.trainus.domain.lesson.student.service.StudentLessonService;
import com.threestar.trainus.domain.test.dto.TestRequestDto;
import com.threestar.trainus.domain.test.service.TestUserService;
import com.threestar.trainus.domain.user.entity.User;
import com.threestar.trainus.global.unit.BaseResponse;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@Tag(name = "동시성 테스트 API", description = "선착순 기능 테스트를 위한 API")
@RestController
@RequestMapping("/test")
@RequiredArgsConstructor
public class TestConcurrencyController {

private final CouponService couponService;
private final StudentLessonService studentLessonService;
private final TestUserService testUserService;

@PostMapping("/coupons/{couponId}")
@Operation(summary = "쿠폰 발급 동시성 테스트", description = "쿠폰을 발급받는 테스트 API")
public ResponseEntity<BaseResponse<CreateUserCouponResponseDto>> issueCouponForTest(
@PathVariable Long couponId,
@RequestBody TestRequestDto testRequestDto
) {
User user = testUserService.findOrCreateUser(testRequestDto.getUserId());
CreateUserCouponResponseDto responseDto = couponService.createUserCoupon(user.getId(), couponId);
return BaseResponse.ok("쿠폰 발급 완료", responseDto, HttpStatus.CREATED);
}

@PostMapping("/lessons/{lessonId}/application")
@Operation(summary = "레슨 신청 동시성 테스트", description = "레슨을 신청하는 테스트 API")
public ResponseEntity<BaseResponse<LessonApplicationResponseDto>> applyToLessonForTest(
@PathVariable Long lessonId,
@RequestBody TestRequestDto testRequestDto
) {
User user = testUserService.findOrCreateUser(testRequestDto.getUserId());
LessonApplicationResponseDto response = studentLessonService.applyToLessonWithLock(lessonId, user.getId());
return BaseResponse.ok("레슨 신청 완료", response, HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.threestar.trainus.domain.test.dto;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class TestRequestDto {
private Long userId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.threestar.trainus.domain.test.service;

import com.threestar.trainus.domain.profile.service.ProfileFacadeService;
import com.threestar.trainus.domain.user.entity.User;
import com.threestar.trainus.domain.user.entity.UserRole;
import com.threestar.trainus.domain.user.repository.UserRepository;

import lombok.RequiredArgsConstructor;

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;

@Service
@RequiredArgsConstructor
public class TestUserService {

private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final ProfileFacadeService profileFacadeService;

@Transactional
public User findOrCreateUser(Long userId) {
Optional<User> existingUser = userRepository.findById(userId);
if (existingUser.isPresent()) {
return existingUser.get();
}
String email = "testuser" + userId + "@example.com";
String nickname = "testuser" + userId;

if (userRepository.existsByEmail(email) || userRepository.existsByNickname(nickname)) {
return userRepository.findByEmail(email)
.orElseThrow(() -> new IllegalStateException("테스트 유저 생성 실패"));
}

String encodedPassword = passwordEncoder.encode("password");
User newUser = User.builder()
.email(email)
.password(encodedPassword)
.nickname(nickname)
.role(UserRole.USER)
.build();

User savedUser = userRepository.save(newUser);
profileFacadeService.createDefaultProfile(savedUser);

return savedUser;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/users/**", "/api/lessons/test-auth", "/swagger-ui/**", "/v3/api-docs/**",
"/api/v1/profiles/**", "/api/v1/lessons/**", "/api/v1/comments/**", "/api/v1/reviews/**",
"/api/v1/rankings/**", "/api/v1/payments/**")
"/api/v1/rankings/**", "/api/v1/payments/**", "/test/**")
.permitAll()
.requestMatchers("/api/v1/admin/**")
.hasRole("ADMIN")
Expand Down