diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/reservation/READEME.md b/reservation/READEME.md index e69de29..a271a1b 100644 --- a/reservation/READEME.md +++ b/reservation/READEME.md @@ -0,0 +1,15 @@ +# Reservation + + + + +## error + + +* An attempt was made to access a socket in a way forbidden by its access permissions + - 도커 컨테이너가 실행되지 않는 에러 + - `netsh interface ipv4 show excludedportrange protocol=tcp` 윈도우기준으로 확인해보면 사용할수없는 포트 목록이 나오는데 분명히 여기에 해당 컨테이너가 reserve 하려는 포트가 있을거임 + - 해결법은 재부팅 (PC) + +* UnknwonHostException + - docker-compose에서 따로 Hostname을 지정안해주면 지멋대로 hostname이 박아지는데 이거때문에 에러남 (컨테이너의 hosts 파일에 설정이 제대로 안되서 원인 발생) \ No newline at end of file diff --git a/reservation/build.gradle b/reservation/build.gradle index e31d110..dbbb1be 100644 --- a/reservation/build.gradle +++ b/reservation/build.gradle @@ -23,12 +23,18 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springdoc:springdoc-openapi-ui:1.6.11' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.kafka:spring-kafka' implementation 'org.springframework.boot:spring-boot-starter-logging:2.7.3' implementation 'org.apache.httpcomponents:httpclient' compileOnly 'org.projectlombok:lombok' runtimeOnly 'mysql:mysql-connector-java' annotationProcessor 'org.projectlombok:lombok' - testImplementation 'org.springframework.boot:spring-boot-starter-test' + + // JUnit Test + testImplementation'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.junit.jupiter:junit-jupiter' + testImplementation 'org.junit.jupiter:junit-jupiter-api' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' } // Set Environment @@ -54,5 +60,5 @@ jar { } tasks.named('test') { - //useJUnitPlatform() + useJUnitPlatform() } \ No newline at end of file diff --git a/reservation/src/main/java/com/potaty/reservation/reservation/ReservationApplication.java b/reservation/src/main/java/com/potato/reservation/ReservationApplication.java similarity index 86% rename from reservation/src/main/java/com/potaty/reservation/reservation/ReservationApplication.java rename to reservation/src/main/java/com/potato/reservation/ReservationApplication.java index b05d801..004d6a5 100644 --- a/reservation/src/main/java/com/potaty/reservation/reservation/ReservationApplication.java +++ b/reservation/src/main/java/com/potato/reservation/ReservationApplication.java @@ -1,4 +1,4 @@ -package com.potaty.reservation.reservation; +package com.potato.reservation; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/reservation/src/main/java/com/potato/reservation/config/DataSourceConfig.java b/reservation/src/main/java/com/potato/reservation/config/DataSourceConfig.java new file mode 100644 index 0000000..e794981 --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/config/DataSourceConfig.java @@ -0,0 +1,65 @@ +package com.potato.reservation.config; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.JpaVendorAdapter; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import javax.sql.DataSource; +import java.util.Properties; + +@Configuration +@EnableTransactionManagement +@EnableJpaRepositories( + basePackages = {"com.potato.reservation.module"}, + entityManagerFactoryRef = "mysqlEntityManagerFactory", + transactionManagerRef = "mysqlTransactionManager" +) +public class DataSourceConfig { + + @Bean + @ConfigurationProperties(prefix = "spring.datasource.mysql") + public DataSource mysqlDataSource() { + return DataSourceBuilder.create().build(); + } + + @Bean + public LocalContainerEntityManagerFactoryBean mysqlEntityManagerFactory( + @Qualifier("mysqlDataSource") DataSource mysqlDataSource) + { + LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); + Properties jpaProperties = new Properties(); + + // DDL-AUTO 구문은 jpaProperties에서 아래와 같이 씀 jpaProperties.put("hibernate.ddl-auto", true); + jpaProperties.put("hibernate.hbm2ddl.auto", "update"); + jpaProperties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect"); + + entityManagerFactoryBean.setDataSource(mysqlDataSource); + entityManagerFactoryBean.setPackagesToScan("com.potato.pay.model.entity"); + + // 여기서 Jpa 구현체 설정가능 (기본값 : Hibernate) + JpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter(); + entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter); + entityManagerFactoryBean.setJpaProperties(jpaProperties); + + return entityManagerFactoryBean; + } + + @Bean + public PlatformTransactionManager mysqlTransactionManager( + @Qualifier("mysqlEntityManagerFactory") LocalContainerEntityManagerFactoryBean mysqlEntityManagerFactory + ) { + JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(); + jpaTransactionManager.setEntityManagerFactory(mysqlEntityManagerFactory.getObject()); + return jpaTransactionManager; + } + +} diff --git a/reservation/src/main/java/com/potato/reservation/config/RestTemplateConfig.java b/reservation/src/main/java/com/potato/reservation/config/RestTemplateConfig.java new file mode 100644 index 0000000..3c8794c --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/config/RestTemplateConfig.java @@ -0,0 +1,31 @@ +package com.potato.reservation.config; + +import org.apache.http.client.HttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + + @Bean + @Qualifier("restTemplate") + public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) { + + HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); + factory.setReadTimeout(5000); // 읽기시간초과, ms + factory.setConnectTimeout(3000); // 연결시간초과, ms + HttpClient httpClient = HttpClientBuilder.create() + .setMaxConnTotal(100) // connection pool 적용 + .setMaxConnPerRoute(5) + .build(); + factory.setHttpClient(httpClient); // HttpClient 세팅 + + return new RestTemplate(factory); + } + +} diff --git a/reservation/src/main/java/com/potato/reservation/config/SwaggerConfig.java b/reservation/src/main/java/com/potato/reservation/config/SwaggerConfig.java new file mode 100644 index 0000000..1704135 --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/config/SwaggerConfig.java @@ -0,0 +1,31 @@ +package com.potato.reservation.config; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import org.springdoc.core.GroupedOpenApi; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SwaggerConfig { + + @Bean + public GroupedOpenApi testApi() { + return GroupedOpenApi.builder() + .group("v1-test") + .pathsToMatch("/test/**") + .build(); + } + + @Bean + public OpenAPI springOpenApi() { + return new OpenAPI() + .info( + new Info() + .title("Reservation Api") + .description("Reservation Desc") + .version("0.0.1") + ); + } + +} diff --git a/reservation/src/main/java/com/potato/reservation/config/constant/TransactionConstant.java b/reservation/src/main/java/com/potato/reservation/config/constant/TransactionConstant.java new file mode 100644 index 0000000..6f4dc48 --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/config/constant/TransactionConstant.java @@ -0,0 +1,7 @@ +package com.potato.reservation.config.constant; + +public class TransactionConstant { + + public static final String MYSQL_TRANSACTION_MANAGER = "mysqlTransactionManager"; + +} diff --git a/reservation/src/main/java/com/potato/reservation/constant/PayMethod.java b/reservation/src/main/java/com/potato/reservation/constant/PayMethod.java new file mode 100644 index 0000000..5583ca7 --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/constant/PayMethod.java @@ -0,0 +1,12 @@ +package com.potato.reservation.constant; + +public enum PayMethod { + + KAKAO("KAKAO"), + TOSS("TOSS"), + ; + + private String method; + PayMethod(String method) {this.method = method;} + +} diff --git a/reservation/src/main/java/com/potato/reservation/constant/PayStatus.java b/reservation/src/main/java/com/potato/reservation/constant/PayStatus.java new file mode 100644 index 0000000..fec705c --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/constant/PayStatus.java @@ -0,0 +1,13 @@ +package com.potato.reservation.constant; + +public enum PayStatus { + INIT("INIT"), + TRANSACTIONING("TRANSACTIONING"), + FAIL("FAIL"), + SUCCESS("SUCCESS"), + ; + + private String status; + + PayStatus(String status) {this.status = status;} +} \ No newline at end of file diff --git a/reservation/src/main/java/com/potato/reservation/model/entity/Ask.java b/reservation/src/main/java/com/potato/reservation/model/entity/Ask.java new file mode 100644 index 0000000..fa7506a --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/model/entity/Ask.java @@ -0,0 +1,32 @@ +package com.potato.reservation.model.entity; + +import com.potato.reservation.constant.PayStatus; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.experimental.SuperBuilder; + +import javax.persistence.*; +import java.util.List; +import java.util.UUID; + +@Entity(name = "ask") +@SuperBuilder +@Data +@RequiredArgsConstructor +public class Ask { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name = "user_id") + private Long userId; + @Column(name = "pay_id") + private Long payId; + @Column(name = "basket_uuid") + private UUID basketUuid; + @Column(name = "pay_status") + private PayStatus payStatus; + @Column(name = "total_price") + private Long totalPrice; +} + diff --git a/reservation/src/main/java/com/potato/reservation/model/entity/Basket.java b/reservation/src/main/java/com/potato/reservation/model/entity/Basket.java new file mode 100644 index 0000000..2201d40 --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/model/entity/Basket.java @@ -0,0 +1,31 @@ +package com.potato.reservation.model.entity; + +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.hibernate.annotations.GenericGenerator; + +import javax.persistence.*; +import java.util.UUID; + +@Entity(name = "basket") +@SuperBuilder +@Data +@RequiredArgsConstructor +public class Basket { + + @EmbeddedId + private BasketUUID basketUuid; + @Column(name = "ask_id") + private Long askId; + @Column(name = "user_id") + private Long userId; + + @Column(name = "product_id") + private Long productId; + + @Column(name = "count") + private Integer count; + + +} diff --git a/reservation/src/main/java/com/potato/reservation/model/entity/BasketUUID.java b/reservation/src/main/java/com/potato/reservation/model/entity/BasketUUID.java new file mode 100644 index 0000000..7cc154f --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/model/entity/BasketUUID.java @@ -0,0 +1,24 @@ +package com.potato.reservation.model.entity; + +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.hibernate.annotations.GenericGenerator; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.UUID; +@Entity(name = "basket_uuid") +@Embeddable +@SuperBuilder +@Data +@RequiredArgsConstructor +public class BasketUUID implements Serializable { + + @Id + @GeneratedValue(generator = "uuid2") + @GenericGenerator(name = "uuid2", strategy = "uuid2") + @Column(columnDefinition = "BINARY(16)") + private UUID uuid; + +} diff --git a/reservation/src/main/java/com/potato/reservation/model/entity/Pay.java b/reservation/src/main/java/com/potato/reservation/model/entity/Pay.java new file mode 100644 index 0000000..ad7eca2 --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/model/entity/Pay.java @@ -0,0 +1,36 @@ +package com.potato.reservation.model.entity; + +import com.potato.reservation.constant.PayMethod; +import com.potato.reservation.constant.PayStatus; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.experimental.SuperBuilder; + +import javax.persistence.*; + +@Entity(name = "pay") +@SuperBuilder +@Data +@RequiredArgsConstructor +public class Pay { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name = "user_id") + private Long userId; + @Column(name = "pay_mehtod") + private PayMethod payMethod; + @Column(name = "pay_status") + private PayStatus payStatus; + private Long amount; + @Column(name = "discount_amount") + private Long discountAmount; + @Column(name = "coupon_id") + private Long couponId; + @Column(name = "cash_receipt") + private String cashReceipt; + @Column(name = "point_ration") + private Float pointRation; + +} diff --git a/reservation/src/main/java/com/potato/reservation/model/entity/Product.java b/reservation/src/main/java/com/potato/reservation/model/entity/Product.java new file mode 100644 index 0000000..8513acf --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/model/entity/Product.java @@ -0,0 +1,37 @@ +package com.potato.reservation.model.entity; + +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.experimental.SuperBuilder; + +import javax.persistence.*; +import java.util.Date; + +@Entity(name = "product") +@SuperBuilder +@Data +@RequiredArgsConstructor +public class Product { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name = "seller_id") + private Long sellerId; + @Column(name = "hall_id") + private Long hallId; + @Column(name = "price") + private Long price; + @Column(name = "product_description") + private String productDescription; + @Column(name = "product_status") + private String productStatus; + @Column(name = "thumbnail_image_url") + private String thumbnail_image_url; + @Column(name = "casting") + private String casting; + @Column(name = "performance_date") + private Date performanceDate; + + +} diff --git a/reservation/src/main/java/com/potato/reservation/module/AskTransactionModule.java b/reservation/src/main/java/com/potato/reservation/module/AskTransactionModule.java new file mode 100644 index 0000000..c58e16a --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/module/AskTransactionModule.java @@ -0,0 +1,71 @@ +package com.potato.reservation.module; + +import com.potato.reservation.constant.PayStatus; +import com.potato.reservation.model.entity.Ask; +import com.potato.reservation.repository.AskRepository; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.stereotype.Component; + +import javax.transaction.Transactional; +import java.util.UUID; + +@Component +@RequiredArgsConstructor +public class AskTransactionModule { + + private final AskRepository askRepository; + private final BasketTransactionModule basketTransactionModule; + + public Ask getAsk(Long id) { + return askRepository.findById(id).get(); + } + + @Transactional + public void setComplete(Long askId) { + Ask ask = Ask.builder() + .id(askId) + .payStatus(PayStatus.SUCCESS) + .build(); + + askRepository.save(ask); + } + + @Transactional + public void updatePayId(Long askId, Long payId) { + Ask ask = Ask.builder() + .id(askId) + .payId(payId) + .build(); + + askRepository.save(ask); + } + + public boolean checkSuccessAsk(UUID basketUuid) { + Ask ask = askRepository.findByBasketUuidAndPayStatus( + basketUuid, + PayStatus.SUCCESS + ); + + return ObjectUtils.isEmpty(ask); + } + + @Transactional + public Long createInitAsk(Long userId, UUID basketUuid) { + + Ask ask = Ask.builder() + .userId(userId) + .basketUuid(basketUuid) + .totalPrice( + basketTransactionModule.getBasketPrice(basketUuid) + ) + .payStatus(PayStatus.INIT) + .build(); + + askRepository.save(ask); + + return ask.getId(); + } + + +} diff --git a/reservation/src/main/java/com/potato/reservation/module/BasketTransactionModule.java b/reservation/src/main/java/com/potato/reservation/module/BasketTransactionModule.java new file mode 100644 index 0000000..1931704 --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/module/BasketTransactionModule.java @@ -0,0 +1,34 @@ +package com.potato.reservation.module; + +import com.potato.reservation.model.entity.Basket; +import com.potato.reservation.model.entity.BasketUUID; +import com.potato.reservation.model.entity.Product; +import com.potato.reservation.repository.BasketRepository; +import com.potato.reservation.repository.ProductRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.UUID; + +@Component +@RequiredArgsConstructor +public class BasketTransactionModule { + + private final BasketRepository basketRepository; + private final ProductTransactionModule productTransactionModule; + + public Long getBasketPrice(UUID basketUuid) { + + BasketUUID basketUUID = BasketUUID.builder() + .uuid(basketUuid) + .build(); + + List basketList = basketRepository.findByBasketUuid(basketUUID); + + return (Long) basketList.stream() + .mapToLong(basket -> productTransactionModule.getProduct(basket.getProductId()).getPrice()) + .sum(); + } + +} diff --git a/reservation/src/main/java/com/potato/reservation/module/PayModule.java b/reservation/src/main/java/com/potato/reservation/module/PayModule.java new file mode 100644 index 0000000..5e2de91 --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/module/PayModule.java @@ -0,0 +1,50 @@ +package com.potato.reservation.module; + +import com.potato.reservation.model.entity.Pay; +import com.potato.reservation.module.dto.request.CreatePayRequest; +import com.potato.reservation.module.dto.response.PayResponse; +import com.potato.reservation.module.dto.response.ResponseCode; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +@Component +@RequiredArgsConstructor +public class PayModule { + + @Value("${spring.pay.url}") + private final String BASEURL; + + private final RestTemplate restTemplate; + + private ResponseEntity request(HttpMethod httpMethod, String url, Object body) { + + HttpHeaders headers = new HttpHeaders(); + headers.add("Content-Type", "application/json"); + + HttpEntity entity = new HttpEntity<>(body, headers); + + return restTemplate.exchange(BASEURL + url, httpMethod, entity, PayResponse.class); + } + + public Pay requestPay(CreatePayRequest createPayRequest) { + + ResponseEntity responseEntity = this.request(HttpMethod.POST, null, createPayRequest); + + PayResponse payResponse = responseEntity.getBody(); + + if (ResponseCode.FAIL.equals(payResponse.getResponseCode())) { + // throw new Exception(); + } + + return payResponse.getPay(); + } + + +} diff --git a/reservation/src/main/java/com/potato/reservation/module/ProductTransactionModule.java b/reservation/src/main/java/com/potato/reservation/module/ProductTransactionModule.java new file mode 100644 index 0000000..fe7a5c0 --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/module/ProductTransactionModule.java @@ -0,0 +1,22 @@ +package com.potato.reservation.module; + +import com.potato.reservation.model.entity.Product; +import com.potato.reservation.repository.ProductRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class ProductTransactionModule { + + private final ProductRepository productRepository; + + public Product getProduct(Long id) { + return productRepository.findById(id).get(); + } + + + + + +} diff --git a/reservation/src/main/java/com/potato/reservation/module/dto/request/CreatePayRequest.java b/reservation/src/main/java/com/potato/reservation/module/dto/request/CreatePayRequest.java new file mode 100644 index 0000000..99ee039 --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/module/dto/request/CreatePayRequest.java @@ -0,0 +1,21 @@ +package com.potato.reservation.module.dto.request; + +import com.potato.reservation.constant.PayMethod; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +@SuperBuilder +@Getter +@Setter +@RequiredArgsConstructor +@AllArgsConstructor +public class CreatePayRequest { + + private PayMethod payMethod; + private Long totalAmount; + private Long couponId; + +} diff --git a/reservation/src/main/java/com/potato/reservation/module/dto/response/PayResponse.java b/reservation/src/main/java/com/potato/reservation/module/dto/response/PayResponse.java new file mode 100644 index 0000000..6b2d69e --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/module/dto/response/PayResponse.java @@ -0,0 +1,20 @@ +package com.potato.reservation.module.dto.response; + +import com.potato.reservation.model.entity.Pay; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +@SuperBuilder +@Getter +@Setter +@RequiredArgsConstructor +@AllArgsConstructor +public class PayResponse { + + private Pay pay; + private ResponseCode responseCode; + +} \ No newline at end of file diff --git a/reservation/src/main/java/com/potato/reservation/module/dto/response/ResponseCode.java b/reservation/src/main/java/com/potato/reservation/module/dto/response/ResponseCode.java new file mode 100644 index 0000000..8a8f3ba --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/module/dto/response/ResponseCode.java @@ -0,0 +1,12 @@ +package com.potato.reservation.module.dto.response; + +public enum ResponseCode { + + SUCCESS("SUCCESS"), + FAIL("FAIL"), + ; + + private String code; + + ResponseCode(String code) {this.code = code;} +} \ No newline at end of file diff --git a/reservation/src/main/java/com/potato/reservation/repository/AskRepository.java b/reservation/src/main/java/com/potato/reservation/repository/AskRepository.java new file mode 100644 index 0000000..ca52cbe --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/repository/AskRepository.java @@ -0,0 +1,17 @@ +package com.potato.reservation.repository; + +import com.potato.reservation.constant.PayStatus; +import com.potato.reservation.model.entity.Ask; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.UUID; + +@Repository +public interface AskRepository extends JpaRepository { + + Ask findByBasketUuidAndPayStatus(UUID uuid, PayStatus payStatus); + +} diff --git a/reservation/src/main/java/com/potato/reservation/repository/BasketRepository.java b/reservation/src/main/java/com/potato/reservation/repository/BasketRepository.java new file mode 100644 index 0000000..aac13e0 --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/repository/BasketRepository.java @@ -0,0 +1,14 @@ +package com.potato.reservation.repository; + +import com.potato.reservation.model.entity.Basket; +import com.potato.reservation.model.entity.BasketUUID; +import org.springframework.data.repository.CrudRepository; + +import java.util.List; +import java.util.UUID; + +public interface BasketRepository extends CrudRepository { + + List findByBasketUuid(BasketUUID basketUuid); + +} diff --git a/reservation/src/main/java/com/potato/reservation/repository/PayRepository.java b/reservation/src/main/java/com/potato/reservation/repository/PayRepository.java new file mode 100644 index 0000000..40c7140 --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/repository/PayRepository.java @@ -0,0 +1,6 @@ +package com.potato.reservation.repository; + +import com.potato.reservation.model.entity.Pay; +import org.springframework.data.repository.CrudRepository; + +public interface PayRepository extends CrudRepository { } \ No newline at end of file diff --git a/reservation/src/main/java/com/potato/reservation/repository/ProductRepository.java b/reservation/src/main/java/com/potato/reservation/repository/ProductRepository.java new file mode 100644 index 0000000..1a3050a --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/repository/ProductRepository.java @@ -0,0 +1,7 @@ +package com.potato.reservation.repository; + +import com.potato.reservation.model.entity.Product; +import org.springframework.data.repository.CrudRepository; + +public interface ProductRepository extends CrudRepository { +} diff --git a/reservation/src/main/java/com/potato/reservation/service/ReservationConsumerService.java b/reservation/src/main/java/com/potato/reservation/service/ReservationConsumerService.java new file mode 100644 index 0000000..124cea4 --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/service/ReservationConsumerService.java @@ -0,0 +1,38 @@ +package com.potato.reservation.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.potato.reservation.stage.dto.ReservationParam; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +@RequiredArgsConstructor +public class ReservationConsumerService { + + private final ReservationStageHandler reservationStageHandler; + + @KafkaListener(topics = "reservation", groupId = "potato-id-test") + public void consume(String message) { + try { + log.info("Reservation Consumer Message : " + message); + } catch (Exception e) { + log.error(e.getMessage()); + } + + //DeSerialization + ObjectMapper objectMapper = new ObjectMapper(); + + try { + ReservationParam reservationParam = objectMapper.readValue(message, ReservationParam.class); + reservationStageHandler.reservationHandle(reservationParam); + } catch (JsonProcessingException e) { + log.error("Json Mapping Error : " + e.getMessage()); + } + + } + +} diff --git a/reservation/src/main/java/com/potato/reservation/service/ReservationStageCaller.java b/reservation/src/main/java/com/potato/reservation/service/ReservationStageCaller.java new file mode 100644 index 0000000..eaad20c --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/service/ReservationStageCaller.java @@ -0,0 +1,46 @@ +package com.potato.reservation.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.potato.reservation.stage.ReservationStage; +import com.potato.reservation.stage.constant.ReservationStep; +import com.potato.reservation.stage.dto.ReservationParam; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Component; + +@RequiredArgsConstructor +@Component +@Slf4j +public class ReservationStageCaller { + + private final ReservationStage reservationStage; + private final KafkaTemplate kafkaTemplate; + + public boolean stageCall(ReservationParam reservationParam) { + + ReservationStep reservationStep = reservationParam.getReservationStep(); + + return switch (reservationStep) { + case CHECK -> reservationStage.check(reservationParam); + case CREATE_ASK_ENTITY -> reservationStage.createAskEntity(reservationParam); + case REQUEST_PAY -> reservationStage.requestPay(reservationParam); + case SET_COMPLETE -> reservationStage.setComplete(reservationParam); + case STAGE_COMPLETE -> true; + default -> false; + }; + } + + public void nextStagePublish(ReservationParam reservationParam) { + ObjectMapper objectMapper = new ObjectMapper(); + try { + //Serialization + String message = objectMapper.writeValueAsString(reservationParam); + kafkaTemplate.send("reservation", message); + } catch (JsonProcessingException e) { + log.error(e.getMessage()); + } + } + +} diff --git a/reservation/src/main/java/com/potato/reservation/service/ReservationStageHandler.java b/reservation/src/main/java/com/potato/reservation/service/ReservationStageHandler.java new file mode 100644 index 0000000..287bcb7 --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/service/ReservationStageHandler.java @@ -0,0 +1,33 @@ +package com.potato.reservation.service; + +import com.potato.reservation.stage.constant.ReservationStep; +import com.potato.reservation.stage.dto.ReservationParam; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@RequiredArgsConstructor +@Component +@Slf4j +public class ReservationStageHandler { + + private final ReservationStageCaller reservationStageCaller; + + public boolean reservationHandle(ReservationParam reservationParam) { + + boolean result = reservationStageCaller.stageCall(reservationParam); + + if (ReservationStep.STAGE_COMPLETE.equals(reservationParam.getReservationStep())) { + return true; + } + + if (result) { + reservationStageCaller.nextStagePublish(reservationParam); + return true; + } else { + log.error("ERROR"); + return false; + } + } + +} diff --git a/reservation/src/main/java/com/potato/reservation/stage/ReservationStage.java b/reservation/src/main/java/com/potato/reservation/stage/ReservationStage.java new file mode 100644 index 0000000..6debade --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/stage/ReservationStage.java @@ -0,0 +1,72 @@ +package com.potato.reservation.stage; + +import com.potato.reservation.model.entity.Ask; +import com.potato.reservation.model.entity.Pay; +import com.potato.reservation.module.AskTransactionModule; +import com.potato.reservation.module.PayModule; +import com.potato.reservation.module.dto.request.CreatePayRequest; +import com.potato.reservation.stage.constant.ReservationStep; +import com.potato.reservation.stage.dto.ReservationParam; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +@RequiredArgsConstructor +public class ReservationStage { + + private final AskTransactionModule askTransactionModule; + private final PayModule payModule; + + public boolean check(ReservationParam reservationParam) { + + + if (!askTransactionModule.checkSuccessAsk(reservationParam.getBasketUuid()) + ) { + // throw new Exception(); + } + + reservationParam.setReservationStep(ReservationStep.CREATE_ASK_ENTITY); + return true; + } + + public boolean createAskEntity(ReservationParam reservationParam) { + + Long askId = askTransactionModule.createInitAsk( + reservationParam.getUserId(), + reservationParam.getBasketUuid() + ); + + reservationParam.setReservationStep(ReservationStep.REQUEST_PAY); + reservationParam.setAskId(askId); + return true; + } + + public boolean requestPay(ReservationParam reservationParam) { + + Ask ask = askTransactionModule.getAsk(reservationParam.getAskId()); + + Pay pay = payModule.requestPay( + CreatePayRequest.builder() + .payMethod(reservationParam.getPayMethod()) + .couponId(reservationParam.getCouponId()) + .totalAmount(ask.getTotalPrice()) + .build() + ); + + askTransactionModule.updatePayId(reservationParam.getAskId(), pay.getId()); + + reservationParam.setReservationStep(ReservationStep.SET_COMPLETE); + return true; + } + + public boolean setComplete(ReservationParam reservationParam) { + + askTransactionModule.setComplete(reservationParam.getAskId()); + + reservationParam.setReservationStep(ReservationStep.STAGE_COMPLETE); + return true; + } + +} diff --git a/reservation/src/main/java/com/potato/reservation/stage/constant/ReservationStep.java b/reservation/src/main/java/com/potato/reservation/stage/constant/ReservationStep.java new file mode 100644 index 0000000..866a08a --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/stage/constant/ReservationStep.java @@ -0,0 +1,20 @@ +package com.potato.reservation.stage.constant; + +import lombok.Getter; + +public enum ReservationStep { + + CHECK(100), + CREATE_ASK_ENTITY(101), + REQUEST_PAY(102), + SET_COMPLETE(103), + STAGE_COMPLETE(0), + ; + + @Getter + private Integer step; + + + ReservationStep(Integer step){this.step = step;} + +} diff --git a/reservation/src/main/java/com/potato/reservation/stage/dto/ReservationParam.java b/reservation/src/main/java/com/potato/reservation/stage/dto/ReservationParam.java new file mode 100644 index 0000000..7874eb5 --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/stage/dto/ReservationParam.java @@ -0,0 +1,27 @@ +package com.potato.reservation.stage.dto; + +import com.potato.reservation.constant.PayMethod; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +import java.util.List; +import java.util.UUID; + +@Getter +@Setter +@SuperBuilder +public class ReservationParam extends StepHead{ + + private Long userId; + private UUID basketUuid; + private Long couponId; + private PayMethod payMethod; + + /** + * Optional + */ + private Long askId; + +} diff --git a/reservation/src/main/java/com/potato/reservation/stage/dto/StepHead.java b/reservation/src/main/java/com/potato/reservation/stage/dto/StepHead.java new file mode 100644 index 0000000..56f5969 --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/stage/dto/StepHead.java @@ -0,0 +1,15 @@ +package com.potato.reservation.stage.dto; + +import com.potato.reservation.stage.constant.ReservationStep; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +@Getter +@Setter +@SuperBuilder +public class StepHead { + private ReservationStep reservationStep; + +} diff --git a/reservation/src/main/java/com/potato/reservation/test/TestConsumeService.java b/reservation/src/main/java/com/potato/reservation/test/TestConsumeService.java new file mode 100644 index 0000000..35d1adf --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/test/TestConsumeService.java @@ -0,0 +1,25 @@ +package com.potato.reservation.test; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Service; + +import java.io.IOException; + +@Service +@Slf4j +@RequiredArgsConstructor +public class TestConsumeService { + + @KafkaListener(topics = "lilac", groupId = "group-id-test") + public void consume(String message) { + try { + log.info("Message : " + message); + } catch (Exception e) { + log.error(e.getMessage()); + } + } + + +} diff --git a/reservation/src/main/java/com/potato/reservation/test/TestController.java b/reservation/src/main/java/com/potato/reservation/test/TestController.java new file mode 100644 index 0000000..1589f2b --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/test/TestController.java @@ -0,0 +1,23 @@ +package com.potato.reservation.test; + +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "Test", description = "테스트") +@RestController +@RequestMapping("/test") +@RequiredArgsConstructor +public class TestController { + + private final TestProducerService testProducerService; + + @PostMapping("/test") + public void test(@RequestBody String message) { + testProducerService.sendMessage(message); + } + +} diff --git a/reservation/src/main/java/com/potato/reservation/test/TestProducerService.java b/reservation/src/main/java/com/potato/reservation/test/TestProducerService.java new file mode 100644 index 0000000..87ff8f5 --- /dev/null +++ b/reservation/src/main/java/com/potato/reservation/test/TestProducerService.java @@ -0,0 +1,20 @@ +package com.potato.reservation.test; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class TestProducerService { + + private final KafkaTemplate kafkaTemplate; + + public void sendMessage(String message) { + log.info("Produce Message : " + message); + kafkaTemplate.send("lilac", message); + } + +} diff --git a/reservation/src/main/resources-env/local/application.yaml b/reservation/src/main/resources-env/local/application.yaml new file mode 100644 index 0000000..9707d6c --- /dev/null +++ b/reservation/src/main/resources-env/local/application.yaml @@ -0,0 +1,27 @@ +spring: + datasource: + mysql: + driver-class-name: com.mysql.cj.jdbc.Driver + username: root + password: password + jdbc-url: jdbc:mysql://127.0.0.1:3306/test_schema + pay: + url: 127.0.0.1:8081 + jpa: + show-sql: true + hibernate: + #??? ???? + ddl-auto: create + kafka: + consumer: + bootstrap-servers: 127.0.0.1:9092 + group-id: group-id-test + auto-offset-reset: earliest + key-deserializer: org.apache.kafka.common.serialization.StringDeserializer + value-deserializer: org.apache.kafka.common.serialization.StringDeserializer + producer: + bootstrap-servers: 127.0.0.1:9092 + key-serializer: org.apache.kafka.common.serialization.StringSerializer + value-serializer: org.apache.kafka.common.serialization.StringSerializer + profiles: + active: local \ No newline at end of file diff --git a/reservation/src/main/resources/application.properties b/reservation/src/main/resources/application.properties deleted file mode 100644 index 8b13789..0000000 --- a/reservation/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - diff --git a/reservation/src/test/java/com/potaty/reservation/reservation/ReservationApplicationTests.java b/reservation/src/test/java/com/potato/reservation/ReservationApplicationTests.java similarity index 80% rename from reservation/src/test/java/com/potaty/reservation/reservation/ReservationApplicationTests.java rename to reservation/src/test/java/com/potato/reservation/ReservationApplicationTests.java index 83b0c3d..052abd1 100644 --- a/reservation/src/test/java/com/potaty/reservation/reservation/ReservationApplicationTests.java +++ b/reservation/src/test/java/com/potato/reservation/ReservationApplicationTests.java @@ -1,4 +1,4 @@ -package com.potaty.reservation.reservation; +package com.potato.reservation; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; diff --git a/reservation/src/test/java/com/potato/reservation/repository/AskRepositoryTest.java b/reservation/src/test/java/com/potato/reservation/repository/AskRepositoryTest.java new file mode 100644 index 0000000..d50ffa0 --- /dev/null +++ b/reservation/src/test/java/com/potato/reservation/repository/AskRepositoryTest.java @@ -0,0 +1,43 @@ +package com.potato.reservation.repository; + +import com.potato.reservation.config.DataSourceConfig; +import com.potato.reservation.constant.PayStatus; +import com.potato.reservation.model.entity.Ask; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import static org.junit.jupiter.api.Assertions.*; + +import java.util.UUID; + +@ExtendWith(SpringExtension.class) +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +@DataJpaTest +class AskRepositoryTest { + + @Autowired + private AskRepository askRepository; + + + @Test + void save() { + + // given + Ask ask = Ask.builder() + .payId(123L) + .userId(123L) + .payStatus(PayStatus.SUCCESS) + .basketUuid(UUID.randomUUID()) + .build(); + + // when + final Ask saveAsk = askRepository.save(ask); + + // then + assertEquals(123L, saveAsk.getPayId()); + } + +}