Skip to content

Commit

Permalink
Merge pull request #14 from KwakDooChul/feat/10-create-apply
Browse files Browse the repository at this point in the history
[Feat] 수강신청 기능 구현
  • Loading branch information
SeonJuuuun authored Mar 31, 2024
2 parents 5463e3a + bf835d7 commit 024babe
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 10 deletions.
3 changes: 2 additions & 1 deletion doochul/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,17 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.boot:spring-boot-starter-batch'
implementation 'com.google.firebase:firebase-admin:9.2.0'

runtimeOnly 'mysql:mysql-connector-java:8.0.28'
runtimeOnly 'com.h2database:h2'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.batch:spring-batch-test'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.mockito:mockito-core:3.12.4'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.doochul.application;

import java.time.Duration;
import lombok.RequiredArgsConstructor;
import org.doochul.domain.membership.MemberShip;
import org.doochul.domain.membership.MemberShipRepository;
import org.doochul.domain.product.Product;
import org.doochul.domain.product.ProductRepository;
import org.doochul.domain.user.User;
import org.doochul.domain.user.UserRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional
public class MemberShipService {

private final MemberShipRepository memberShipRepository;
private final ProductRepository productRepository;
private final UserRepository userRepository;
private final RedisService redisService;

public Long save(final Long userId, final Long productId) {
final Product product = productRepository.findById(productId).orElseThrow();
final User user = userRepository.findById(userId).orElseThrow();

final String key = Long.toString(userId);
if (redisService.setNX(key, "apply", Duration.ofSeconds(5))) {
final Long id = memberShipRepository.save(MemberShip.of(user, product, product.getCount())).getId();
redisService.delete(key);
return id;
}
throw new IllegalArgumentException();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class MemberShip extends BaseEntity {
private Long id;

@ManyToOne
@JoinColumn(name = "student_id")
@JoinColumn(name = "user_id")
private User student;

@ManyToOne
Expand All @@ -32,12 +32,17 @@ public class MemberShip extends BaseEntity {

private Integer remainingCount;

public MemberShip(final User student, final Product product, final Integer remainingCount) {
public MemberShip(final Long id, final User student, final Product product, final Integer remainingCount) {
this.id = id;
this.student = student;
this.product = product;
this.remainingCount = remainingCount;
}

public static MemberShip of(final User student, final Product product, final Integer remainingCount) {
return new MemberShip(null, student, product, remainingCount);
}

public void decreasedCount() {
validateMinRemainingCount();
remainingCount -= 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ public class Product extends BaseEntity {

private Integer count;

public Product(final String name, final ProductType type, final User teacher, final Integer count) {
public Product(final Long id, final String name, final ProductType type, final User teacher, final Integer count) {
this.id = id;
this.name = name;
this.type = type;
this.teacher = teacher;
Expand Down
14 changes: 8 additions & 6 deletions doochul/src/main/java/org/doochul/domain/user/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.doochul.domain.BaseEntity;

@Entity
Expand All @@ -36,13 +37,14 @@ public class User extends BaseEntity {
@Enumerated(EnumType.STRING)
private Identity identity;

public User(
final String name,
final String deviceToken,
final String passWord,
final Gender gender,
final Identity identity
public User(final Long id,
final String name,
final String deviceToken,
final String passWord,
final Gender gender,
final Identity identity
) {
this.id = id;
this.name = name;
this.deviceToken = deviceToken;
this.passWord = passWord;
Expand Down
16 changes: 16 additions & 0 deletions doochul/src/main/java/org/doochul/ui/MemberShipController.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
package org.doochul.ui;

import java.net.URI;
import lombok.RequiredArgsConstructor;
import org.doochul.application.MemberShipService;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("/memberShip")
public class MemberShipController {

private final MemberShipService memberShipService;

@PostMapping("/apply/{productId}")
public ResponseEntity<Void> apply(@AuthenticationPrincipal final Long userId, @PathVariable final Long productId) {
Long memberShipId = memberShipService.save(userId, productId);
return ResponseEntity.created(URI.create("/memberShips" + memberShipId)).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.doochul.application;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.times;

import java.time.Duration;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.doochul.domain.membership.MemberShip;
import org.doochul.domain.membership.MemberShipRepository;
import org.doochul.domain.product.Product;
import org.doochul.domain.product.ProductRepository;
import org.doochul.domain.product.ProductType;
import org.doochul.domain.user.Gender;
import org.doochul.domain.user.Identity;
import org.doochul.domain.user.User;
import org.doochul.domain.user.UserRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;

@SpringBootTest
public class MemberShipServiceTest {

@MockBean
private MemberShipRepository memberShipRepository;

@Autowired
private ProductRepository productRepository;

@Autowired
private UserRepository userRepository;

@Autowired
private RedisService redisService;

@Test
void memberShip_save() throws InterruptedException {
// Given
User user = new User(2L, "JEON", "deviceToken1234", "password1234", Gender.MEN, Identity.GENERAL);
User teacher = new User(1L, "Faker", "deviceToken123", "password123", Gender.MEN, Identity.TEACHER);

userRepository.save(user);
userRepository.save(teacher);

Product product = new Product(1L, "페이커", ProductType.LOL, teacher, 10);
productRepository.save(product);

ExecutorService executorService = Executors.newFixedThreadPool(5);
CountDownLatch latch = new CountDownLatch(5);

given(memberShipRepository.save(any())).willReturn(new MemberShip(1L, user, product, 10));

for (int i = 0; i < 5; i++) {
executorService.submit(() -> {
try {
final String key = Long.toString(user.getId());
if (redisService.setNX(key, "apply", Duration.ofSeconds(5))) {
memberShipRepository.save(MemberShip.of(user, product, product.getCount()));
redisService.delete(key);
}
} finally {
latch.countDown();
}
});
}

latch.await();
executorService.shutdown();

then(memberShipRepository).should(times(1)).save(any());
}
}

0 comments on commit 024babe

Please sign in to comment.