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

엔티티 필드명에 명시적 @colunm 어노테이션 사용 #97

Merged
merged 3 commits into from
Oct 31, 2023
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
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ dependencies {
asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'

// RestTemplate for PATCH method
implementation 'org.apache.httpcomponents.client5:httpclient5'
// Webclient
implementation 'org.springframework.boot:spring-boot-starter-webflux'
}

tasks.named('test') {
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/ayuconpon/CouponApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.scheduling.annotation.EnableAsync;


@SpringBootApplication
@ConfigurationPropertiesScan(basePackages = "com.ayuconpon")
@EnableAsync
public class CouponApplication {

public static void main(String[] args) {
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/ayuconpon/common/BaseEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
public class BaseEntity {

@CreatedDate
@Column(updatable = false)
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
@Column(name = "updated_at")
private LocalDateTime updatedAt;

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
public class CouponWarmupProperties {

private final Integer warmupCount;
private final String couponUrl;
private final String baseUrl;

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
public class UserCouponWarmupProperties {

private final Integer warmupCount;
private final Long dummyUserId;
private final Long dummyIssueCouponId;
private final String url;
private final String useUserCouponApiBody;
private final String issueUserCouponApiBody;
private final Long userid;
private final Long useApiPathSegment;
private final String baseUrl;
private final String useApiBody;
private final String issueApiBody;

}
59 changes: 53 additions & 6 deletions src/main/java/com/ayuconpon/common/config/warmup/WarmupConfig.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.ayuconpon.common.config.warmup;

import com.ayuconpon.warmup.CouponWarmupRunner;
import com.ayuconpon.warmup.UserCouponWarmupRunner;
import com.ayuconpon.warmup.WarmupRegistry;
import com.ayuconpon.warmup.WarmupRunner;
import com.ayuconpon.warmup.WarmupStarter;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;

@Configuration
@EnableConfigurationProperties({CouponWarmupProperties.class, UserCouponWarmupProperties.class})
Expand All @@ -16,13 +19,57 @@ public class WarmupConfig {
private final UserCouponWarmupProperties userCouponWarmupProperties;

@Bean
public CouponWarmupRunner couponWarmupRunner() {
return new CouponWarmupRunner(couponWarmupProperties);
public WarmupRunner showCouponsWarmupRunner() {
return WarmupRunner
.create(HttpMethod.GET, couponWarmupProperties.getBaseUrl(), couponWarmupProperties.getWarmupCount());
}

@Bean
public UserCouponWarmupRunner userCouponWarmupRunner() {
return new UserCouponWarmupRunner(userCouponWarmupProperties);
public WarmupRunner showUserCouponsWarmupRunner() {
return WarmupRunner
.create(HttpMethod.GET, userCouponWarmupProperties.getBaseUrl(), userCouponWarmupProperties.getWarmupCount())
.headers(getHeaders());
}

@Bean
public WarmupRunner issueCouponWarmupRunner() {
return WarmupRunner
.create(HttpMethod.POST, userCouponWarmupProperties.getBaseUrl(), userCouponWarmupProperties.getWarmupCount())
.headers(getHeaders())
.bodyValue(userCouponWarmupProperties.getIssueApiBody());
}

@Bean
public WarmupRunner useCouponWarmupRunner() {
return WarmupRunner
.create(HttpMethod.PATCH, userCouponWarmupProperties.getBaseUrl(), userCouponWarmupProperties.getWarmupCount())
.headers(getHeaders())
.pathSegment(userCouponWarmupProperties.getUseApiPathSegment().toString())
.bodyValue(userCouponWarmupProperties.getUseApiBody());
}

@Bean
public WarmupRegistry warmupRegistry() {
WarmupRegistry registry = new WarmupRegistry();
registry.addWarmupRunner(showCouponsWarmupRunner())
.addWarmupRunner(showUserCouponsWarmupRunner())
.addWarmupRunner(issueCouponWarmupRunner())
.addWarmupRunner(useCouponWarmupRunner());
return registry;
}

@Bean
public WarmupStarter warmupStarter() {
return new WarmupStarter(warmupRegistry());
}

private HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json");
headers.add("Accept", "application/json");
headers.add("Accept-Encoding", "gzip, deflate, br");
headers.add("User-Id", userCouponWarmupProperties.getUserid().toString());
return headers;
}

}
11 changes: 11 additions & 0 deletions src/main/java/com/ayuconpon/coupon/domain/CouponRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
Expand All @@ -20,4 +22,13 @@ public interface CouponRepository extends JpaRepository<Coupon, Long> {
"where c.issuePeriod.startedAt <= :currentTime and :currentTime <= c.issuePeriod.finishedAt")
List<Coupon> findCouponsInProgress(LocalDateTime currentTime, Pageable pageable);

@Transactional
@Modifying(clearAutomatically = true)
@Query(value = "update coupon " +
"set left_quantity = left_quantity - 1 " +
"where coupon_id = :couponId and left_quantity > 0 " +
"and :currentTime BETWEEN started_at and finished_at"
, nativeQuery = true)
int decrease(Long couponId, LocalDateTime currentTime);

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class Coupon extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "coupon_id")
private Long couponId;

@Column(name = "name")
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/ayuconpon/user/domain/entity/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class User extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private Long userId;

@Column(name = "name")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import com.ayuconpon.usercoupon.controller.response.UseUserCouponResponse;
import com.ayuconpon.usercoupon.service.*;
import com.ayuconpon.common.resolver.UserId;
import com.ayuconpon.usercoupon.service.issue.IssueUserCouponCommand;
import com.ayuconpon.usercoupon.service.issue.IssueUserCouponFacade;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
Expand All @@ -22,7 +24,7 @@
@RestController
public class UserCouponController {

private final IssueUserCouponService issueUserCouponService;
private final IssueUserCouponFacade issueUserCouponfacade;
private final UseUserCouponService useUserCouponService;
private final ShowUserCouponService showUserCouponService;

Expand All @@ -41,7 +43,7 @@ public ResponseEntity<IssueUserCouponResponse> issueCoupon(
@Valid @RequestBody IssueUserCouponRequest issueUserCouponRequest) {

IssueUserCouponCommand command = new IssueUserCouponCommand(userId, issueUserCouponRequest.getCouponId());
Long issuedUserCouponId = issueUserCouponService.issue(command);
Long issuedUserCouponId = issueUserCouponfacade.issue(command);

return ResponseEntity.ok(new IssueUserCouponResponse(issuedUserCouponId));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class UserCoupon extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_coupon_id")
private Long userCouponId;

@Column(name = "user_id")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.ayuconpon.usercoupon.service.issue;

import com.ayuconpon.coupon.domain.CouponRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;

@Service
@RequiredArgsConstructor
public class CouponQuantityService {

private final CouponRepository couponRepository;

public void decrease(IssueUserCouponCommand command, LocalDateTime currentTime) {
int result = couponRepository.decrease(command.couponId(), currentTime);
if (result != 1) throw new IllegalStateException("쿠폰의 재고가 없습니다.");
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.ayuconpon.usercoupon.service;
package com.ayuconpon.usercoupon.service.issue;

import org.springframework.util.Assert;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.ayuconpon.usercoupon.service.issue;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;

@Service
@RequiredArgsConstructor
public class IssueUserCouponFacade {

private final IssueUserCouponValidator validator;
private final CouponQuantityService couponQuantityService;
private final IssueUserCouponService issueUserCouponService;

public Long issue(IssueUserCouponCommand command) {
validator.validate(command);

LocalDateTime currentTime = LocalDateTime.now();
couponQuantityService.decrease(command, currentTime);
return issueUserCouponService.issue(command, currentTime);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.ayuconpon.usercoupon.service.issue;

import com.ayuconpon.common.exception.NotFoundCouponException;
import com.ayuconpon.coupon.domain.CouponRepository;
import com.ayuconpon.coupon.domain.entity.Coupon;
import com.ayuconpon.usercoupon.domain.UserCouponRepository;
import com.ayuconpon.usercoupon.domain.entity.UserCoupon;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;

@Service
@RequiredArgsConstructor
public class IssueUserCouponService {

private final CouponRepository couponRepository;
private final UserCouponRepository userCouponRepository;

public Long issue(IssueUserCouponCommand command, LocalDateTime currentTime) {
Coupon coupon = couponRepository.findById(command.couponId())
.orElseThrow(NotFoundCouponException::new);
return saveCoupon(new UserCoupon(command.userId(), coupon, currentTime));
}

private Long saveCoupon(UserCoupon issuedCoupon) {
return userCouponRepository.save(issuedCoupon).getUserCouponId();
}

}
Original file line number Diff line number Diff line change
@@ -1,54 +1,33 @@
package com.ayuconpon.usercoupon.service;
package com.ayuconpon.usercoupon.service.issue;

import com.ayuconpon.coupon.domain.entity.Coupon;
import com.ayuconpon.usercoupon.domain.entity.UserCoupon;
import com.ayuconpon.coupon.domain.CouponRepository;
import com.ayuconpon.usercoupon.domain.UserCouponRepository;
import com.ayuconpon.common.exception.DuplicatedCouponException;
import com.ayuconpon.common.exception.NotFoundCouponException;
import com.ayuconpon.common.exception.RequireRegistrationException;
import com.ayuconpon.coupon.domain.CouponRepository;
import com.ayuconpon.user.domain.UserRepository;
import com.ayuconpon.usercoupon.domain.UserCouponRepository;
import com.ayuconpon.usercoupon.domain.entity.UserCoupon;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Optional;

@RequiredArgsConstructor
@Transactional
@Service
public class IssueUserCouponService {
@RequiredArgsConstructor
public class IssueUserCouponValidator {

private final CouponRepository couponRepository;
private final UserCouponRepository userCouponRepository;
private final UserRepository userRepository;

public Long issue(IssueUserCouponCommand command) {
validate(command);
UserCoupon issuedUserCoupon = issueUserCoupon(command);
return saveCoupon(issuedUserCoupon);
}

private void validate(IssueUserCouponCommand command) {
@Transactional(readOnly = true)
public void validate(IssueUserCouponCommand command) {
validateRegisteredCoupon(command);
validateRegisteredUser(command);
validateDuplicatedCoupon(command);
}

private UserCoupon issueUserCoupon(IssueUserCouponCommand command) {
Coupon coupon = couponRepository.findByIdWithPessimisticLock(command.couponId());

LocalDateTime currentTime = LocalDateTime.now();

coupon.decrease(currentTime);
return new UserCoupon(command.userId(), coupon, currentTime);
}

private Long saveCoupon(UserCoupon issuedCoupon) {
return userCouponRepository.save(issuedCoupon).getUserCouponId();
}

private void validateRegisteredCoupon(IssueUserCouponCommand command) {
boolean isExist = couponRepository.existsById(command.couponId());
if (!isExist) throw new NotFoundCouponException();
Expand Down
37 changes: 0 additions & 37 deletions src/main/java/com/ayuconpon/warmup/CouponWarmupRunner.java

This file was deleted.

Loading