From c33f43bad307e70d4e62676abb16dd1f1a471be5 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Sun, 18 Feb 2024 01:43:14 +0700 Subject: [PATCH 01/54] feat(docker) : init docker-compose.yml and Dockerfile.multi --- posttest/Dockerfile.multi | 11 +++++++++++ posttest/docker-compose.yml | 17 +++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 posttest/Dockerfile.multi create mode 100644 posttest/docker-compose.yml diff --git a/posttest/Dockerfile.multi b/posttest/Dockerfile.multi new file mode 100644 index 00000000..78e8c798 --- /dev/null +++ b/posttest/Dockerfile.multi @@ -0,0 +1,11 @@ +# stage-1 build artifact +FROM amazoncorretto:17.0.9-alpine3.18 as builder +WORKDIR /app +ADD . . +RUN ["./gradlew","bootJar"] + +# stage-2 running image +FROM gcr.io/distroless/java17-debian12:latest +WORKDIR /app +COPY --from=builder /app/build/libs/lottery-0.0.1-SNAPSHOT.jar lottery-0.0.1-SNAPSHOT.jar +ENTRYPOINT ["java", "-jar", "lottery-0.0.1-SNAPSHOT.jar"] \ No newline at end of file diff --git a/posttest/docker-compose.yml b/posttest/docker-compose.yml new file mode 100644 index 00000000..b179e0d5 --- /dev/null +++ b/posttest/docker-compose.yml @@ -0,0 +1,17 @@ +version: '3' + +services: + postgres: + image: postgres:latest + restart: always + environment: + POSTGRES_DB: lottery + POSTGRES_USER: lottery + POSTGRES_PASSWORD: Plottery001!@# + volumes: + # - pgdata:/var/lib/postgresql/data + - ./database/init.sql:/docker-entrypoint-initdb.d/init.sql + ports: + - "5432:5432" +volumes: + pgdata: \ No newline at end of file From f93dea95eb2ddcbf35b699a8ca6fe531e57f0dd6 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Sun, 18 Feb 2024 01:44:06 +0700 Subject: [PATCH 02/54] feat(Lottery) : init lottery application and env name --- .../Application.java} | 10 ++++------ .../src/main/resources/application.properties | 17 +++++++++++------ 2 files changed, 15 insertions(+), 12 deletions(-) rename posttest/src/main/java/com/kbtg/bootcamp/{posttest/PosttestApplication.java => lottery/Application.java} (58%) diff --git a/posttest/src/main/java/com/kbtg/bootcamp/posttest/PosttestApplication.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/Application.java similarity index 58% rename from posttest/src/main/java/com/kbtg/bootcamp/posttest/PosttestApplication.java rename to posttest/src/main/java/com/kbtg/bootcamp/lottery/Application.java index 630c3b8d..8826641f 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/posttest/PosttestApplication.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/Application.java @@ -1,13 +1,11 @@ -package com.kbtg.bootcamp.posttest; +package com.kbtg.bootcamp.lottery; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class PosttestApplication { - +public class Application { public static void main(String[] args) { - SpringApplication.run(PosttestApplication.class, args); + SpringApplication.run(Application.class, args); } - -} +} \ No newline at end of file diff --git a/posttest/src/main/resources/application.properties b/posttest/src/main/resources/application.properties index 8d3eb44e..06cf0ed0 100644 --- a/posttest/src/main/resources/application.properties +++ b/posttest/src/main/resources/application.properties @@ -1,6 +1,11 @@ -server.port=8081 -spring.datasource.url=jdbc:postgresql://localhost:5432/ -spring.datasource.username= -spring.datasource.password= -spring.jpa.hibernate.ddl-auto= -spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect \ No newline at end of file +server.port=8888 +spring.datasource.url=${DATABASE_URL} +spring.datasource.username=lottery +spring.datasource.password=Plottery001!@# +#spring.jpa.hibernate.ddl-auto= +spring.jpa.hibernate.ddl-auto=validate +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect + +spring.security.user.name=kamusername +spring.security.user.password=kampassword +logging.level.org.springframework.security=DEBUG \ No newline at end of file From 0230101d481783e7a2697a42be2495596805abe8 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Sun, 18 Feb 2024 01:44:22 +0700 Subject: [PATCH 03/54] feat(Lottery) : implement dependencies in build.gradle --- posttest/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/posttest/build.gradle b/posttest/build.gradle index 6d9dbbe2..6ed593e7 100644 --- a/posttest/build.gradle +++ b/posttest/build.gradle @@ -22,11 +22,11 @@ repositories { } dependencies { - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' +// implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.postgresql:postgresql' - compileOnly 'org.projectlombok:lombok' - annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' } From 47a7198463908f5752a9d8dc808392ea21a07566 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Mon, 19 Feb 2024 10:28:32 +0700 Subject: [PATCH 04/54] feat(Lottery) : implement api exception handler code status --- .../lottery/exception/ApiErrorResponse.java | 31 +++++++++++++++++++ .../exception/ApiExceptionHandler.java | 26 ++++++++++++++++ .../exception/BadRequestException.java | 8 +++++ .../lottery/exception/NotFoundException.java | 8 +++++ 4 files changed, 73 insertions(+) create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/ApiErrorResponse.java create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/ApiExceptionHandler.java create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/BadRequestException.java create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/NotFoundException.java diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/ApiErrorResponse.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/ApiErrorResponse.java new file mode 100644 index 00000000..c4fe1116 --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/ApiErrorResponse.java @@ -0,0 +1,31 @@ +package com.kbtg.bootcamp.lottery.exception; + +import org.springframework.http.HttpStatus; + +import java.time.ZonedDateTime; + +public class ApiErrorResponse { + private final String message; + + private final HttpStatus httpStatus; + + private final ZonedDateTime dateTime; + + public ApiErrorResponse(String message, HttpStatus httpStatus, ZonedDateTime dateTime) { + this.message = message; + this.httpStatus = httpStatus; + this.dateTime = dateTime; + } + + public String getMessage() { + return message; + } + + public HttpStatus getHttpStatus() { + return httpStatus; + } + + public ZonedDateTime getDateTime() { + return dateTime; + } +} diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/ApiExceptionHandler.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/ApiExceptionHandler.java new file mode 100644 index 00000000..755466d0 --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/ApiExceptionHandler.java @@ -0,0 +1,26 @@ +package com.kbtg.bootcamp.lottery.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.time.ZonedDateTime; + +@RestControllerAdvice +public class ApiExceptionHandler { + + @ExceptionHandler({ NotFoundException.class }) + public ResponseEntity handleNotFoundException(NotFoundException e) { + ApiErrorResponse errorResponse = + new ApiErrorResponse(e.getMessage(), HttpStatus.NOT_FOUND, ZonedDateTime.now()); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + } + + @ExceptionHandler({ BadRequestException.class }) + public ResponseEntity handleNotFoundException(BadRequestException e) { + ApiErrorResponse errorResponse = + new ApiErrorResponse(e.getMessage(), HttpStatus.BAD_REQUEST, ZonedDateTime.now()); + return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); + } +} diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/BadRequestException.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/BadRequestException.java new file mode 100644 index 00000000..60836d58 --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/BadRequestException.java @@ -0,0 +1,8 @@ +package com.kbtg.bootcamp.lottery.exception; + +public class BadRequestException extends RuntimeException { + + public BadRequestException(String message) { + super(message); + } +} diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/NotFoundException.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/NotFoundException.java new file mode 100644 index 00000000..b287d0f9 --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/NotFoundException.java @@ -0,0 +1,8 @@ +package com.kbtg.bootcamp.lottery.exception; + +public class NotFoundException extends RuntimeException { + + public NotFoundException(String message) { + super(message); + } +} From 69a2579a4758143d6e947a2096a50276768f14d0 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Mon, 19 Feb 2024 10:29:01 +0700 Subject: [PATCH 05/54] feat(Lottery) : implement config security --- .../lottery/config/SecurityConfig.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/config/SecurityConfig.java diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/config/SecurityConfig.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/config/SecurityConfig.java new file mode 100644 index 00000000..d8f3af6f --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/config/SecurityConfig.java @@ -0,0 +1,42 @@ +package com.kbtg.bootcamp.lottery.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.factory.PasswordEncoderFactories; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + @Bean + SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { + http.csrf(AbstractHttpConfigurer::disable).authorizeHttpRequests(authorizeRequests -> authorizeRequests + .requestMatchers("/**").permitAll() + .requestMatchers("/admin/**") + .hasRole("ADMIN") + .anyRequest() + .authenticated()); + http.httpBasic(Customizer.withDefaults()); + return http.build(); + } + + + @Bean + public UserDetailsService userDetailsService() { + PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); + UserDetails adminUser = User.withUsername("admin") + .password(encoder.encode("password")) + .roles("ADMIN") + .build(); + return new InMemoryUserDetailsManager(adminUser); + } +} From 877999b6b97c740053e66186d9dbe8ab62de821e Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Mon, 19 Feb 2024 10:29:40 +0700 Subject: [PATCH 06/54] feat(Lottery) : change java config --- posttest/build.gradle | 3 ++- posttest/src/main/resources/application.properties | 14 +++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/posttest/build.gradle b/posttest/build.gradle index 6ed593e7..ff760156 100644 --- a/posttest/build.gradle +++ b/posttest/build.gradle @@ -22,8 +22,9 @@ repositories { } 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-validation' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.postgresql:postgresql' diff --git a/posttest/src/main/resources/application.properties b/posttest/src/main/resources/application.properties index 06cf0ed0..2a6c3d84 100644 --- a/posttest/src/main/resources/application.properties +++ b/posttest/src/main/resources/application.properties @@ -1,11 +1,15 @@ server.port=8888 -spring.datasource.url=${DATABASE_URL} +# DataSource Configuration +spring.datasource.url=jdbc:postgresql://localhost:5432/lottery spring.datasource.username=lottery spring.datasource.password=Plottery001!@# -#spring.jpa.hibernate.ddl-auto= +spring.datasource.driver-class-name=org.postgresql.Driver + +# JPA & Hibernate Configuration +spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect spring.jpa.hibernate.ddl-auto=validate -spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect -spring.security.user.name=kamusername -spring.security.user.password=kampassword +# Security Configuration +spring.security.user.name=admin +spring.security.user.password=password logging.level.org.springframework.security=DEBUG \ No newline at end of file From 981a9b6446227eb7ae5dcad761083417e014f8ea Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Mon, 19 Feb 2024 10:29:46 +0700 Subject: [PATCH 07/54] feat(Lottery) : change java config --- posttest/settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posttest/settings.gradle b/posttest/settings.gradle index 5fb54ad4..36b5943e 100644 --- a/posttest/settings.gradle +++ b/posttest/settings.gradle @@ -1 +1 @@ -rootProject.name = 'posttest' +rootProject.name = 'lottery' From e47f23f42e58185ae2efa91137493691fa8d0c04 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Mon, 19 Feb 2024 10:30:07 +0700 Subject: [PATCH 08/54] feat(Lottery) : init first sql design --- posttest/database/init.sql | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 posttest/database/init.sql diff --git a/posttest/database/init.sql b/posttest/database/init.sql new file mode 100644 index 00000000..046f45cb --- /dev/null +++ b/posttest/database/init.sql @@ -0,0 +1,21 @@ +DROP TABLE IF EXISTS lottery; +DROP TABLE IF EXISTS user_ticket; + +CREATE TABLE lottery ( + ticket_id SERIAL PRIMARY KEY, + ticket_number VARCHAR(6) NOT NULL, + price DECIMAL NOT NULL, + amount_available INT NOT NULL, + created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT (NOW()) +); + +CREATE TABLE user_ticket ( + purchase_id SERIAL PRIMARY KEY, + user_id VARCHAR(10) NOT NULL, + ticket_id INT NOT NULL, + purchase_date TIMESTAMP WITHOUT TIME ZONE DEFAULT (NOW()), + FOREIGN KEY (ticket_id) REFERENCES lottery(ticket_id) +); + +CREATE INDEX idx_ticket_number ON lottery(ticket_number); +CREATE INDEX idx_user_id ON user_ticket(user_id); From 7fb38eb238c449b2917de7dc20e77ae224a05149 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Mon, 19 Feb 2024 10:30:47 +0700 Subject: [PATCH 09/54] feat(Lottery) : add controllerAdmin --- .../lottery/controller/AdminController.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/AdminController.java diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/AdminController.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/AdminController.java new file mode 100644 index 00000000..8f4fc400 --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/AdminController.java @@ -0,0 +1,36 @@ +package com.kbtg.bootcamp.lottery.controller; + +import com.kbtg.bootcamp.lottery.request.LotteryRequestDto; +import com.kbtg.bootcamp.lottery.response.LotteryTicketResponse; +import com.kbtg.bootcamp.lottery.service.LotteryService; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +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; + +@RestController +@RequestMapping("/admin") +public class AdminController { + + private final LotteryService lotteryService; + + @Autowired + public AdminController(LotteryService lotteryService) { + this.lotteryService = lotteryService; + } + + + @PostMapping("/lotteries") + @PreAuthorize("hasRole('ADMIN')") + public LotteryTicketResponse createLottery(@Valid @RequestBody LotteryRequestDto lotteryDTO) throws Exception { + return lotteryService.createLottery(lotteryDTO); + } + +// public ResponseEntity createLottery(@RequestBody @Valid LotteryRequestDto lotteryDTO) throws Exception { +// Lottery createdLottery = lotteryService.createLottery(lotteryDTO); +// return ResponseEntity.ok(createdLottery); +// } +} From 79077beb51dfba65ac9596f3635d56be08769efb Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Mon, 19 Feb 2024 10:30:54 +0700 Subject: [PATCH 10/54] feat(Lottery) : add Lottery Entity --- .../kbtg/bootcamp/lottery/entity/Lottery.java | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java new file mode 100644 index 00000000..f69c0468 --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java @@ -0,0 +1,61 @@ +package com.kbtg.bootcamp.lottery.entity; + +import jakarta.persistence.*; + +import java.math.BigDecimal; +import java.util.Collections; +import java.util.List; + + +@Entity +@Table(name = "lottery") +public class Lottery { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer ticketId; + @Column(name = "ticket_number") + private String ticketNumber; + @Column(name = "price") + private BigDecimal ticketPrice; + + @Column(name = "amount_available") + private Integer ticketAmount; + + public Integer getTicketAmount() { + return ticketAmount; + } + + public void setTicketAmount(Integer ticketAmount) { + this.ticketAmount = ticketAmount; + } + + public Integer getTicketId() { + return ticketId; + } + + public void setTicketId(Integer ticketId) { + this.ticketId = ticketId; + } + + public String getTicketNumber() { + return ticketNumber; + } + + public List getTicketNumbers() { + return Collections.singletonList(ticketNumber); + } + + public void setTicketNumber(String ticketNumber) { + this.ticketNumber = ticketNumber; + } + + public BigDecimal getTicketPrice() { + return ticketPrice; + } + + public void setTicketPrice(BigDecimal ticketPrice) { + this.ticketPrice = ticketPrice; + } + + +} From 8cf657a10ff9287d708633cb3b677245bac33bf0 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Mon, 19 Feb 2024 10:31:03 +0700 Subject: [PATCH 11/54] feat(Lottery) : add LotteryController --- .../lottery/controller/LotteryController.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/LotteryController.java diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/LotteryController.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/LotteryController.java new file mode 100644 index 00000000..861b1c62 --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/LotteryController.java @@ -0,0 +1,25 @@ +package com.kbtg.bootcamp.lottery.controller; + + +import com.kbtg.bootcamp.lottery.response.LotteryTicketResponse; +import com.kbtg.bootcamp.lottery.service.LotteryService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/") +public class LotteryController { + private final LotteryService lotteryService; + + @Autowired + public LotteryController(LotteryService lotteryService) { + this.lotteryService = lotteryService; + } + + @GetMapping("/lotteries") + public LotteryTicketResponse getLotteries() throws Exception { + return lotteryService.getAllLotteryTickets(); + } +} From aad3748e24ae1de704be8215e9dea6b6ff446758 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Mon, 19 Feb 2024 10:31:09 +0700 Subject: [PATCH 12/54] feat(Lottery) : add LotteryRepository --- .../bootcamp/lottery/repository/LotteryRepository.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/LotteryRepository.java diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/LotteryRepository.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/LotteryRepository.java new file mode 100644 index 00000000..02aea6eb --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/LotteryRepository.java @@ -0,0 +1,8 @@ +package com.kbtg.bootcamp.lottery.repository; + +import com.kbtg.bootcamp.lottery.entity.Lottery; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface LotteryRepository extends JpaRepository { + +} From 015d9e525ca18e084c6184cb9dafe5390cde7482 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Mon, 19 Feb 2024 10:31:19 +0700 Subject: [PATCH 13/54] feat(Lottery) : add LotteryRequestDto --- .../lottery/request/LotteryRequestDto.java | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/request/LotteryRequestDto.java diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/request/LotteryRequestDto.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/request/LotteryRequestDto.java new file mode 100644 index 00000000..04534729 --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/request/LotteryRequestDto.java @@ -0,0 +1,49 @@ +package com.kbtg.bootcamp.lottery.request; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; + +import java.math.BigDecimal; + +public class LotteryRequestDto { + @NotNull + @NotBlank(message = "Please enter 6 digits for lottery number only.") + @Size(min = 6, max = 6, message = "Please enter 6 digits for lottery number only.") + @Pattern(regexp = "^[0-9]*$", message = "Please enter 6 digits for lottery number only.") + @JsonProperty("ticket") + private String ticketNumber; + + @JsonProperty("price") + private BigDecimal ticketPrice; + + @JsonProperty("amount") + private Integer ticketAmount; + + public Integer getTicketAmount() { + return ticketAmount; + } + + public void setTicketAmount(Integer ticketAmount) { + this.ticketAmount = ticketAmount; + } + + public String getTicketNumber() { + return ticketNumber; + } + + public BigDecimal getTicketPrice() { + return ticketPrice; + } + + public void setTicketPrice(BigDecimal ticketPrice) { + this.ticketPrice = ticketPrice; + } + + public void setTicketNumber(String ticketNumber) { + this.ticketNumber = ticketNumber; + } +} + From 44a87a1a76cc4b9d0a8dcf819f19e3fc0e8a3662 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Mon, 19 Feb 2024 10:31:24 +0700 Subject: [PATCH 14/54] feat(Lottery) : add LotteryService --- .../kbtg/bootcamp/lottery/service/LotteryService.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/service/LotteryService.java diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/LotteryService.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/LotteryService.java new file mode 100644 index 00000000..699dee67 --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/LotteryService.java @@ -0,0 +1,10 @@ +package com.kbtg.bootcamp.lottery.service; + +import com.kbtg.bootcamp.lottery.request.LotteryRequestDto; +import com.kbtg.bootcamp.lottery.response.LotteryTicketResponse; + + +public interface LotteryService { + LotteryTicketResponse createLottery(LotteryRequestDto lotteryDTO); + LotteryTicketResponse getAllLotteryTickets() throws Exception; +} From 01052f0df95f6aab4f1a32b5a560f0f0c0f96731 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Mon, 19 Feb 2024 10:31:34 +0700 Subject: [PATCH 15/54] feat(Lottery) : add LotteryService --- .../service/impl/LotteryServiceImpl.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java new file mode 100644 index 00000000..61f07876 --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java @@ -0,0 +1,40 @@ +package com.kbtg.bootcamp.lottery.service.impl; + +import com.kbtg.bootcamp.lottery.entity.Lottery; +import com.kbtg.bootcamp.lottery.repository.LotteryRepository; +import com.kbtg.bootcamp.lottery.request.LotteryRequestDto; +import com.kbtg.bootcamp.lottery.response.LotteryTicketResponse; +import com.kbtg.bootcamp.lottery.service.LotteryService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class LotteryServiceImpl implements LotteryService { + private final LotteryRepository lotteryRepository; + + @Autowired + public LotteryServiceImpl(LotteryRepository lotteryRepository) throws Exception { + this.lotteryRepository = lotteryRepository; + } + + @Override + public LotteryTicketResponse createLottery(LotteryRequestDto lotteryDTO) { + Lottery lottery = new Lottery(); + lottery.setTicketNumber(lotteryDTO.getTicketNumber()); + lottery.setTicketPrice(lotteryDTO.getTicketPrice()); + lottery.setTicketAmount(lotteryDTO.getTicketAmount()); + lotteryRepository.save(lottery); + return new LotteryTicketResponse(lottery.getTicketNumber()); + } + + @Override + public LotteryTicketResponse getAllLotteryTickets() throws Exception { + List ticketNumbers = lotteryRepository.findAll().stream() + .map(Lottery::getTicketNumber) + .distinct() + .toList(); + return new LotteryTicketResponse(ticketNumbers); + } +} From 7460fc50976cb1e95f8a223804a7a4fd4c7dbed5 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Mon, 19 Feb 2024 10:31:41 +0700 Subject: [PATCH 16/54] feat(Lottery) : add LotteryTicketResponse --- .../response/LotteryTicketResponse.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryTicketResponse.java diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryTicketResponse.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryTicketResponse.java new file mode 100644 index 00000000..073910dd --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryTicketResponse.java @@ -0,0 +1,40 @@ +package com.kbtg.bootcamp.lottery.response; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.util.List; + +public class LotteryTicketResponse { + + @JsonInclude(JsonInclude.Include.NON_NULL) + private String ticket; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private List tickets; + + + public LotteryTicketResponse(List tickets) { + this.tickets = tickets; + } + + public LotteryTicketResponse(String ticket) { + this.ticket = ticket; + } + + // Getter + public List getTickets() { + return tickets; + } + + public void setTickets(List tickets) { + this.tickets = tickets; + } + + public String getTicket() { + return ticket; + } + + public void setTicket(String ticket) { + this.ticket = ticket; + } +} From e18ab99eca40512f932b161145f2a3196c551d32 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Mon, 19 Feb 2024 10:32:26 +0700 Subject: [PATCH 17/54] feat(Lottery) : refactor class name LotteryTicketResponse to LotteryTicket --- .../lottery/controller/AdminController.java | 4 ++-- .../lottery/controller/LotteryController.java | 4 ++-- ...cketResponse.java => LotteryResponse.java} | 6 +++--- .../lottery/service/LotteryService.java | 6 +++--- .../service/impl/LotteryServiceImpl.java | 10 +++++----- .../ApplicationTests.java} | 4 ++-- .../lottery/EnvironmentVariableTest.java | 20 +++++++++++++++++++ 7 files changed, 37 insertions(+), 17 deletions(-) rename posttest/src/main/java/com/kbtg/bootcamp/lottery/response/{LotteryTicketResponse.java => LotteryResponse.java} (82%) rename posttest/src/test/java/com/kbtg/bootcamp/{posttest/PosttestApplicationTests.java => lottery/ApplicationTests.java} (68%) create mode 100644 posttest/src/test/java/com/kbtg/bootcamp/lottery/EnvironmentVariableTest.java diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/AdminController.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/AdminController.java index 8f4fc400..641e46fb 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/AdminController.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/AdminController.java @@ -1,7 +1,7 @@ package com.kbtg.bootcamp.lottery.controller; import com.kbtg.bootcamp.lottery.request.LotteryRequestDto; -import com.kbtg.bootcamp.lottery.response.LotteryTicketResponse; +import com.kbtg.bootcamp.lottery.response.LotteryResponse; import com.kbtg.bootcamp.lottery.service.LotteryService; import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; @@ -25,7 +25,7 @@ public AdminController(LotteryService lotteryService) { @PostMapping("/lotteries") @PreAuthorize("hasRole('ADMIN')") - public LotteryTicketResponse createLottery(@Valid @RequestBody LotteryRequestDto lotteryDTO) throws Exception { + public LotteryResponse createLottery(@Valid @RequestBody LotteryRequestDto lotteryDTO) throws Exception { return lotteryService.createLottery(lotteryDTO); } diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/LotteryController.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/LotteryController.java index 861b1c62..8fdfab7f 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/LotteryController.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/LotteryController.java @@ -1,7 +1,7 @@ package com.kbtg.bootcamp.lottery.controller; -import com.kbtg.bootcamp.lottery.response.LotteryTicketResponse; +import com.kbtg.bootcamp.lottery.response.LotteryResponse; import com.kbtg.bootcamp.lottery.service.LotteryService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; @@ -19,7 +19,7 @@ public LotteryController(LotteryService lotteryService) { } @GetMapping("/lotteries") - public LotteryTicketResponse getLotteries() throws Exception { + public LotteryResponse getLotteries() throws Exception { return lotteryService.getAllLotteryTickets(); } } diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryTicketResponse.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryResponse.java similarity index 82% rename from posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryTicketResponse.java rename to posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryResponse.java index 073910dd..7ef85607 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryTicketResponse.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryResponse.java @@ -4,7 +4,7 @@ import java.util.List; -public class LotteryTicketResponse { +public class LotteryResponse { @JsonInclude(JsonInclude.Include.NON_NULL) private String ticket; @@ -13,11 +13,11 @@ public class LotteryTicketResponse { private List tickets; - public LotteryTicketResponse(List tickets) { + public LotteryResponse(List tickets) { this.tickets = tickets; } - public LotteryTicketResponse(String ticket) { + public LotteryResponse(String ticket) { this.ticket = ticket; } diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/LotteryService.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/LotteryService.java index 699dee67..a6b2cca6 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/LotteryService.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/LotteryService.java @@ -1,10 +1,10 @@ package com.kbtg.bootcamp.lottery.service; import com.kbtg.bootcamp.lottery.request.LotteryRequestDto; -import com.kbtg.bootcamp.lottery.response.LotteryTicketResponse; +import com.kbtg.bootcamp.lottery.response.LotteryResponse; public interface LotteryService { - LotteryTicketResponse createLottery(LotteryRequestDto lotteryDTO); - LotteryTicketResponse getAllLotteryTickets() throws Exception; + LotteryResponse createLottery(LotteryRequestDto lotteryDTO); + LotteryResponse getAllLotteryTickets() throws Exception; } diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java index 61f07876..5f99759e 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java @@ -3,7 +3,7 @@ import com.kbtg.bootcamp.lottery.entity.Lottery; import com.kbtg.bootcamp.lottery.repository.LotteryRepository; import com.kbtg.bootcamp.lottery.request.LotteryRequestDto; -import com.kbtg.bootcamp.lottery.response.LotteryTicketResponse; +import com.kbtg.bootcamp.lottery.response.LotteryResponse; import com.kbtg.bootcamp.lottery.service.LotteryService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -20,21 +20,21 @@ public LotteryServiceImpl(LotteryRepository lotteryRepository) throws Exception } @Override - public LotteryTicketResponse createLottery(LotteryRequestDto lotteryDTO) { + public LotteryResponse createLottery(LotteryRequestDto lotteryDTO) { Lottery lottery = new Lottery(); lottery.setTicketNumber(lotteryDTO.getTicketNumber()); lottery.setTicketPrice(lotteryDTO.getTicketPrice()); lottery.setTicketAmount(lotteryDTO.getTicketAmount()); lotteryRepository.save(lottery); - return new LotteryTicketResponse(lottery.getTicketNumber()); + return new LotteryResponse(lottery.getTicketNumber()); } @Override - public LotteryTicketResponse getAllLotteryTickets() throws Exception { + public LotteryResponse getAllLotteryTickets() throws Exception { List ticketNumbers = lotteryRepository.findAll().stream() .map(Lottery::getTicketNumber) .distinct() .toList(); - return new LotteryTicketResponse(ticketNumbers); + return new LotteryResponse(ticketNumbers); } } diff --git a/posttest/src/test/java/com/kbtg/bootcamp/posttest/PosttestApplicationTests.java b/posttest/src/test/java/com/kbtg/bootcamp/lottery/ApplicationTests.java similarity index 68% rename from posttest/src/test/java/com/kbtg/bootcamp/posttest/PosttestApplicationTests.java rename to posttest/src/test/java/com/kbtg/bootcamp/lottery/ApplicationTests.java index 8349fc7a..f3b4f7df 100644 --- a/posttest/src/test/java/com/kbtg/bootcamp/posttest/PosttestApplicationTests.java +++ b/posttest/src/test/java/com/kbtg/bootcamp/lottery/ApplicationTests.java @@ -1,10 +1,10 @@ -package com.kbtg.bootcamp.posttest; +package com.kbtg.bootcamp.lottery; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest -class PosttestApplicationTests { +class ApplicationTests { @Test void contextLoads() { diff --git a/posttest/src/test/java/com/kbtg/bootcamp/lottery/EnvironmentVariableTest.java b/posttest/src/test/java/com/kbtg/bootcamp/lottery/EnvironmentVariableTest.java new file mode 100644 index 00000000..38fe796d --- /dev/null +++ b/posttest/src/test/java/com/kbtg/bootcamp/lottery/EnvironmentVariableTest.java @@ -0,0 +1,20 @@ +package com.kbtg.bootcamp.lottery; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +public class EnvironmentVariableTest +{ + @Value("${spring.datasource.url}") + private String datasourceUrl; + + @Test + public void testDataSourceUrl() { + assertThat(datasourceUrl).isNotNull(); + System.out.println("DataSource URL: " + datasourceUrl); + // You can add more assertions here to validate the format or the content of the datasourceUrl + } +} From 162eeaae300e122fda9636b4b6d96c2e8f0fc008 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Mon, 19 Feb 2024 18:27:07 +0700 Subject: [PATCH 18/54] feat(Lottery) : re-design database and Lottery Class --- posttest/database/init.sql | 6 +++--- .../java/com/kbtg/bootcamp/lottery/entity/Lottery.java | 10 +--------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/posttest/database/init.sql b/posttest/database/init.sql index 046f45cb..4c259e3b 100644 --- a/posttest/database/init.sql +++ b/posttest/database/init.sql @@ -2,8 +2,7 @@ DROP TABLE IF EXISTS lottery; DROP TABLE IF EXISTS user_ticket; CREATE TABLE lottery ( - ticket_id SERIAL PRIMARY KEY, - ticket_number VARCHAR(6) NOT NULL, + ticket_number VARCHAR(6) NOT NULL PRIMARY KEY, price DECIMAL NOT NULL, amount_available INT NOT NULL, created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT (NOW()) @@ -11,10 +10,11 @@ CREATE TABLE lottery ( CREATE TABLE user_ticket ( purchase_id SERIAL PRIMARY KEY, + ticket_number VARCHAR(6) NOT NULL, user_id VARCHAR(10) NOT NULL, ticket_id INT NOT NULL, purchase_date TIMESTAMP WITHOUT TIME ZONE DEFAULT (NOW()), - FOREIGN KEY (ticket_id) REFERENCES lottery(ticket_id) + FOREIGN KEY (ticket_number) REFERENCES lottery(ticket_number) ); CREATE INDEX idx_ticket_number ON lottery(ticket_number); diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java index f69c0468..479be3a9 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java @@ -10,9 +10,8 @@ @Entity @Table(name = "lottery") public class Lottery { + @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Integer ticketId; @Column(name = "ticket_number") private String ticketNumber; @Column(name = "price") @@ -29,13 +28,6 @@ public void setTicketAmount(Integer ticketAmount) { this.ticketAmount = ticketAmount; } - public Integer getTicketId() { - return ticketId; - } - - public void setTicketId(Integer ticketId) { - this.ticketId = ticketId; - } public String getTicketNumber() { return ticketNumber; From 958767c7e38a3c8162a9eb685abe66dfa5e9e7b4 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Tue, 20 Feb 2024 15:13:22 +0700 Subject: [PATCH 19/54] feat(Lottery) : add constant variable file --- .../src/main/java/com/kbtg/bootcamp/lottery/lib/Const.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/lib/Const.java diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/lib/Const.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/lib/Const.java new file mode 100644 index 00000000..6f1978b0 --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/lib/Const.java @@ -0,0 +1,7 @@ +package com.kbtg.bootcamp.lottery.lib; + +public final class Const { + public static final String LOTTERY_NOT_SOLD_BACK_FLAG = "N"; + public static final String LOTTERY_SOLD_OUT_MESSAGE_FORMAT = "Lottery number: %s sold out"; + public static final String LOTTERY_NOT_FOUND = "Lottery number: %s not found."; +} From 221a95a35e6f3efd9fac63f008ac6fd5194ce34a Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Tue, 20 Feb 2024 15:13:30 +0700 Subject: [PATCH 20/54] feat(Lottery) : change design DB --- posttest/database/init.sql | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/posttest/database/init.sql b/posttest/database/init.sql index 4c259e3b..01d2609c 100644 --- a/posttest/database/init.sql +++ b/posttest/database/init.sql @@ -5,16 +5,26 @@ CREATE TABLE lottery ( ticket_number VARCHAR(6) NOT NULL PRIMARY KEY, price DECIMAL NOT NULL, amount_available INT NOT NULL, + last_updated TIMESTAMP WITHOUT TIME ZONE DEFAULT (NOW()), created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT (NOW()) ); +CREATE TABLE users ( + user_id VARCHAR(100) NOT NULL PRIMARY KEY, + created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() +); + CREATE TABLE user_ticket ( - purchase_id SERIAL PRIMARY KEY, + ticket_id SERIAL PRIMARY KEY, ticket_number VARCHAR(6) NOT NULL, - user_id VARCHAR(10) NOT NULL, - ticket_id INT NOT NULL, + user_id VARCHAR(100) NOT NULL, + price_paid DECIMAL NOT NULL, + is_sold_back_flag VARCHAR(1) DEFAULT 'N', purchase_date TIMESTAMP WITHOUT TIME ZONE DEFAULT (NOW()), - FOREIGN KEY (ticket_number) REFERENCES lottery(ticket_number) + created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW(), + last_updated TIMESTAMP WITHOUT TIME ZONE DEFAULT (NOW()), + FOREIGN KEY (ticket_number) REFERENCES lottery(ticket_number), + FOREIGN KEY (user_id) REFERENCES users(user_id) ); CREATE INDEX idx_ticket_number ON lottery(ticket_number); From 981b819ef797572163a11384bfa5fffb2666898e Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Tue, 20 Feb 2024 15:13:41 +0700 Subject: [PATCH 21/54] feat(Lottery) : change design DB --- .../kbtg/bootcamp/lottery/entity/Lottery.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java index 479be3a9..4ab6f67c 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java @@ -1,8 +1,12 @@ package com.kbtg.bootcamp.lottery.entity; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; import java.math.BigDecimal; +import java.time.LocalDateTime; import java.util.Collections; import java.util.List; @@ -24,6 +28,9 @@ public Integer getTicketAmount() { return ticketAmount; } + @Column(name = "last_updated") + private LocalDateTime lastUpdate; + public void setTicketAmount(Integer ticketAmount) { this.ticketAmount = ticketAmount; } @@ -49,5 +56,13 @@ public void setTicketPrice(BigDecimal ticketPrice) { this.ticketPrice = ticketPrice; } + public LocalDateTime getLastUpdate() { + return lastUpdate; + } + + public void setLastUpdate(LocalDateTime lastUpdate) { + this.lastUpdate = lastUpdate; + } + } From a1ee76d4fafc175e70c18c268f4e34fc7e0e86cc Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Tue, 20 Feb 2024 15:13:51 +0700 Subject: [PATCH 22/54] feat(Lottery) : change design DB --- .../kbtg/bootcamp/lottery/repository/LotteryRepository.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/LotteryRepository.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/LotteryRepository.java index 02aea6eb..67faa7b3 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/LotteryRepository.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/LotteryRepository.java @@ -3,6 +3,6 @@ import com.kbtg.bootcamp.lottery.entity.Lottery; import org.springframework.data.jpa.repository.JpaRepository; -public interface LotteryRepository extends JpaRepository { +public interface LotteryRepository extends JpaRepository { -} +} \ No newline at end of file From 24e4f127dbc731201aaf709d7cd8af12e18a4c0b Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Tue, 20 Feb 2024 15:14:40 +0700 Subject: [PATCH 23/54] feat(Lottery) : implement UserController --- .../lottery/controller/UserController.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/UserController.java diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/UserController.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/UserController.java new file mode 100644 index 00000000..c0f37209 --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/UserController.java @@ -0,0 +1,28 @@ +package com.kbtg.bootcamp.lottery.controller; + +import com.kbtg.bootcamp.lottery.response.LotteryPurchaseResponseDto; +import com.kbtg.bootcamp.lottery.service.LotteryService; +import org.springframework.beans.factory.annotation.Autowired; +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 +@RequestMapping("/users") +public class UserController { + + private final LotteryService lotteryService; + + @Autowired + public UserController(LotteryService lotteryService) { + this.lotteryService = lotteryService; + } + + @PostMapping("/{userId}/lotteries/{lotteryNumber}") + public LotteryPurchaseResponseDto purchaseLotteryTicket(@PathVariable String userId, @PathVariable String lotteryNumber) throws Exception { + return lotteryService.purchaseLotteryTicket(userId, lotteryNumber); + } + + // Additional methods as per requirements... +} \ No newline at end of file From b0d80ab81383356aeedf4cd1a7db719ca4e2e23b Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Tue, 20 Feb 2024 15:14:51 +0700 Subject: [PATCH 24/54] feat(Lottery) : implement UserTicket Entity --- .../bootcamp/lottery/entity/UserTicket.java | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/UserTicket.java diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/UserTicket.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/UserTicket.java new file mode 100644 index 00000000..b355acbe --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/UserTicket.java @@ -0,0 +1,89 @@ +package com.kbtg.bootcamp.lottery.entity; + +import jakarta.persistence.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Entity +@Table(name = "user_ticket") +public class UserTicket { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer ticket_id; + + @Column(name = "ticket_number") + private String ticketNumber; + + @Column(name = "user_id") + private String userId; + + @Column(name = "is_sold_back_flag") + private String isSoldBackFlag; + + @Column(name = "price_paid") + private BigDecimal pricePaid; + + @Column(name = "purchase_date") + private LocalDateTime purchaseDate; + + @Column(name = "last_updated") + private LocalDateTime lastUpdated; + + public Integer getTicketId() { + return ticket_id; + } + + public void setTicketId(Integer ticket_id) { + this.ticket_id = ticket_id; + } + + public String getTicketNumber() { + return ticketNumber; + } + + public void setTicketNumber(String ticketNumber) { + this.ticketNumber = ticketNumber; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getIsSoldBackFlag() { + return isSoldBackFlag; + } + + public void setIsSoldBackFlag(String isSoldBackFlag) { + this.isSoldBackFlag = isSoldBackFlag; + } + + public BigDecimal getPricePaid() { + return pricePaid; + } + + public void setPricePaid(BigDecimal pricePaid) { + this.pricePaid = pricePaid; + } + + public LocalDateTime getPurchaseDate() { + return purchaseDate; + } + + public void setPurchaseDate(LocalDateTime purchaseDate) { + this.purchaseDate = purchaseDate; + } + + public LocalDateTime getLastUpdated() { + return lastUpdated; + } + + public void setLastUpdated(LocalDateTime lastUpdated) { + this.lastUpdated = lastUpdated; + } +} \ No newline at end of file From cda71a14e926ca5eaa42d8d12b31655d4d6c2239 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Tue, 20 Feb 2024 15:14:55 +0700 Subject: [PATCH 25/54] feat(Lottery) : implement Users Entity --- .../kbtg/bootcamp/lottery/entity/Users.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Users.java diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Users.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Users.java new file mode 100644 index 00000000..8d01c7c3 --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Users.java @@ -0,0 +1,20 @@ +package com.kbtg.bootcamp.lottery.entity; + +import jakarta.persistence.*; + +@Entity +@Table(name = "users") +public class Users { + + @Id + @Column(name = "user_id") + private String userId; + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getUserId() { + return userId; + } +} From 5af8ebbed2cafdc98b1898a05c4ca62617de84f6 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Tue, 20 Feb 2024 15:15:20 +0700 Subject: [PATCH 26/54] feat(Lottery) : add repo for Users entity and usersTicket entity --- .../bootcamp/lottery/repository/UserTicketRepository.java | 7 +++++++ .../kbtg/bootcamp/lottery/repository/UsersRepository.java | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/UserTicketRepository.java create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/UsersRepository.java diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/UserTicketRepository.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/UserTicketRepository.java new file mode 100644 index 00000000..62419b40 --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/UserTicketRepository.java @@ -0,0 +1,7 @@ +package com.kbtg.bootcamp.lottery.repository; + +import com.kbtg.bootcamp.lottery.entity.UserTicket; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserTicketRepository extends JpaRepository { +} diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/UsersRepository.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/UsersRepository.java new file mode 100644 index 00000000..a1c15e55 --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/UsersRepository.java @@ -0,0 +1,7 @@ +package com.kbtg.bootcamp.lottery.repository; + +import com.kbtg.bootcamp.lottery.entity.Users; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UsersRepository extends JpaRepository { +} From 557f776981b7f6a824e68487e9a983d8bde729e6 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Tue, 20 Feb 2024 15:15:45 +0700 Subject: [PATCH 27/54] feat(Lottery) : implement purchase logic to lotteryService --- .../lottery/service/LotteryService.java | 8 +- .../service/impl/LotteryServiceImpl.java | 106 ++++++++++++++++-- 2 files changed, 99 insertions(+), 15 deletions(-) diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/LotteryService.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/LotteryService.java index a6b2cca6..e8dd4bdb 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/LotteryService.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/LotteryService.java @@ -1,10 +1,12 @@ package com.kbtg.bootcamp.lottery.service; import com.kbtg.bootcamp.lottery.request.LotteryRequestDto; -import com.kbtg.bootcamp.lottery.response.LotteryResponse; +import com.kbtg.bootcamp.lottery.response.LotteryPurchaseResponseDto; +import com.kbtg.bootcamp.lottery.response.LotteryResponseDto; public interface LotteryService { - LotteryResponse createLottery(LotteryRequestDto lotteryDTO); - LotteryResponse getAllLotteryTickets() throws Exception; + LotteryResponseDto createLottery(LotteryRequestDto lotteryDTO) throws Exception; + LotteryResponseDto getAllLotteryTickets() throws Exception; + LotteryPurchaseResponseDto purchaseLotteryTicket(String userId, String ticketNumber) throws Exception; } diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java index 5f99759e..9ea26dfe 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java @@ -1,40 +1,122 @@ package com.kbtg.bootcamp.lottery.service.impl; import com.kbtg.bootcamp.lottery.entity.Lottery; +import com.kbtg.bootcamp.lottery.entity.UserTicket; +import com.kbtg.bootcamp.lottery.entity.Users; +import com.kbtg.bootcamp.lottery.exception.NotFoundException; import com.kbtg.bootcamp.lottery.repository.LotteryRepository; +import com.kbtg.bootcamp.lottery.repository.UserTicketRepository; +import com.kbtg.bootcamp.lottery.repository.UsersRepository; import com.kbtg.bootcamp.lottery.request.LotteryRequestDto; -import com.kbtg.bootcamp.lottery.response.LotteryResponse; +import com.kbtg.bootcamp.lottery.response.LotteryPurchaseResponseDto; +import com.kbtg.bootcamp.lottery.response.LotteryResponseDto; import com.kbtg.bootcamp.lottery.service.LotteryService; +import jakarta.transaction.Transactional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.math.BigDecimal; +import java.time.LocalDateTime; import java.util.List; +import java.util.Optional; + +import static com.kbtg.bootcamp.lottery.lib.Const.*; @Service public class LotteryServiceImpl implements LotteryService { private final LotteryRepository lotteryRepository; + private final UserTicketRepository userTicketRepository; + private final UsersRepository usersRepository; @Autowired - public LotteryServiceImpl(LotteryRepository lotteryRepository) throws Exception { + public LotteryServiceImpl(LotteryRepository lotteryRepository, UserTicketRepository userTicketRepository, UsersRepository usersRepository) throws Exception { this.lotteryRepository = lotteryRepository; + this.userTicketRepository = userTicketRepository; + this.usersRepository = usersRepository; } @Override - public LotteryResponse createLottery(LotteryRequestDto lotteryDTO) { - Lottery lottery = new Lottery(); - lottery.setTicketNumber(lotteryDTO.getTicketNumber()); - lottery.setTicketPrice(lotteryDTO.getTicketPrice()); - lottery.setTicketAmount(lotteryDTO.getTicketAmount()); - lotteryRepository.save(lottery); - return new LotteryResponse(lottery.getTicketNumber()); + @Transactional + public LotteryResponseDto createLottery(LotteryRequestDto lotteryDTO) { + Optional optionalLottery = lotteryRepository.findById(lotteryDTO.getTicketNumber()); + if (optionalLottery.isPresent()) { + Lottery lottery = optionalLottery.get(); + int totalAmount; + totalAmount = lotteryDTO.getTicketAmount() + lottery.getTicketAmount(); + lottery.setTicketPrice(lotteryDTO.getTicketPrice()); + lottery.setLastUpdate(LocalDateTime.now()); + lottery.setTicketAmount(totalAmount); + lotteryRepository.save(optionalLottery.get()); + return new LotteryResponseDto(optionalLottery.get().getTicketNumber()); + } + + Lottery newLottery = new Lottery(); + newLottery.setTicketNumber(lotteryDTO.getTicketNumber()); + newLottery.setTicketPrice(lotteryDTO.getTicketPrice()); + newLottery.setTicketAmount(lotteryDTO.getTicketAmount()); + newLottery.setLastUpdate(LocalDateTime.now()); + lotteryRepository.save(newLottery); + return new LotteryResponseDto(newLottery.getTicketNumber()); } @Override - public LotteryResponse getAllLotteryTickets() throws Exception { + public LotteryResponseDto getAllLotteryTickets() throws Exception { List ticketNumbers = lotteryRepository.findAll().stream() .map(Lottery::getTicketNumber) - .distinct() .toList(); - return new LotteryResponse(ticketNumbers); + return new LotteryResponseDto(ticketNumbers); + } + + + @Override + @Transactional + public LotteryPurchaseResponseDto purchaseLotteryTicket(final String userId, final String ticketNumber) { + + Optional optionalLottery = lotteryRepository.findById(ticketNumber); + if (optionalLottery.isEmpty()) { + throw new NotFoundException(String.format(LOTTERY_NOT_FOUND, ticketNumber)); + } + Lottery lottery = optionalLottery.get(); + validateLotteryAvailable(lottery, ticketNumber); + + + Optional optionalUser = usersRepository.findById(userId); + if (optionalUser.isEmpty()){ + createUser(userId); + } + + decrementTicketAmount(lottery); + final UserTicket userTicket = createUserTicket(userId, ticketNumber, lottery.getTicketPrice()); + userTicketRepository.save(userTicket); + return new LotteryPurchaseResponseDto(userTicket.getTicketId()); } + + private void validateLotteryAvailable(final Lottery lottery, final String ticketNumber) { + if (lottery.getTicketAmount() <= 0) { + throw new NotFoundException(String.format(LOTTERY_SOLD_OUT_MESSAGE_FORMAT, ticketNumber)); + } + } + + private void createUser(final String userId) { + final Users newUser = new Users(); + newUser.setUserId(userId); + usersRepository.save(newUser); + } + + private void decrementTicketAmount(final Lottery lottery) { + lottery.setTicketAmount(lottery.getTicketAmount() - 1); + lotteryRepository.save(lottery); + } + + private UserTicket createUserTicket(final String userId, final String ticketNumber, final BigDecimal ticketPrice) { + final UserTicket userTicket = new UserTicket(); + userTicket.setTicketNumber(ticketNumber); + userTicket.setUserId(userId); + userTicket.setPricePaid(ticketPrice); + userTicket.setPurchaseDate(LocalDateTime.now()); + userTicket.setIsSoldBackFlag(LOTTERY_NOT_SOLD_BACK_FLAG); + userTicket.setLastUpdated(LocalDateTime.now()); + return userTicket; + } + } From f97291a26e2ca1a849cad2906ba195924e178cf2 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Tue, 20 Feb 2024 15:16:24 +0700 Subject: [PATCH 28/54] feat(Lottery) : change LotteryResponse Class to LotteryResponseDto --- .../{LotteryResponse.java => LotteryResponseDto.java} | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) rename posttest/src/main/java/com/kbtg/bootcamp/lottery/response/{LotteryResponse.java => LotteryResponseDto.java} (82%) diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryResponse.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryResponseDto.java similarity index 82% rename from posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryResponse.java rename to posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryResponseDto.java index 7ef85607..38147659 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryResponse.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryResponseDto.java @@ -4,7 +4,7 @@ import java.util.List; -public class LotteryResponse { +public class LotteryResponseDto { @JsonInclude(JsonInclude.Include.NON_NULL) private String ticket; @@ -13,15 +13,14 @@ public class LotteryResponse { private List tickets; - public LotteryResponse(List tickets) { + public LotteryResponseDto(List tickets) { this.tickets = tickets; } - public LotteryResponse(String ticket) { + public LotteryResponseDto(String ticket) { this.ticket = ticket; } - // Getter public List getTickets() { return tickets; } From 3d42853f0cf2cc7f9ae2c95c0b7e2908dd1dba69 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Tue, 20 Feb 2024 15:17:02 +0700 Subject: [PATCH 29/54] feat(Lottery) : implement response LotteryPurchase --- .../lottery/controller/LotteryController.java | 4 ++-- .../response/LotteryPurchaseResponseDto.java | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryPurchaseResponseDto.java diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/LotteryController.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/LotteryController.java index 8fdfab7f..f23f7335 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/LotteryController.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/LotteryController.java @@ -1,7 +1,7 @@ package com.kbtg.bootcamp.lottery.controller; -import com.kbtg.bootcamp.lottery.response.LotteryResponse; +import com.kbtg.bootcamp.lottery.response.LotteryResponseDto; import com.kbtg.bootcamp.lottery.service.LotteryService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; @@ -19,7 +19,7 @@ public LotteryController(LotteryService lotteryService) { } @GetMapping("/lotteries") - public LotteryResponse getLotteries() throws Exception { + public LotteryResponseDto getLotteries() throws Exception { return lotteryService.getAllLotteryTickets(); } } diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryPurchaseResponseDto.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryPurchaseResponseDto.java new file mode 100644 index 00000000..c92464ec --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryPurchaseResponseDto.java @@ -0,0 +1,15 @@ +package com.kbtg.bootcamp.lottery.response; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class LotteryPurchaseResponseDto { + + @JsonProperty("id") + private String ticketId; + + public LotteryPurchaseResponseDto(Integer ticketId) { + this.ticketId = String.valueOf(ticketId); + } + + +} From c93d38bd32a627cdce1a0d598a6484298ed43ef5 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Tue, 20 Feb 2024 15:17:19 +0700 Subject: [PATCH 30/54] feat(Lottery) : refactor name class LotteryResponse to LotteryResponseDto --- .../kbtg/bootcamp/lottery/controller/AdminController.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/AdminController.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/AdminController.java index 641e46fb..2c18cf5e 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/AdminController.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/AdminController.java @@ -1,7 +1,7 @@ package com.kbtg.bootcamp.lottery.controller; import com.kbtg.bootcamp.lottery.request.LotteryRequestDto; -import com.kbtg.bootcamp.lottery.response.LotteryResponse; +import com.kbtg.bootcamp.lottery.response.LotteryResponseDto; import com.kbtg.bootcamp.lottery.service.LotteryService; import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; @@ -25,12 +25,8 @@ public AdminController(LotteryService lotteryService) { @PostMapping("/lotteries") @PreAuthorize("hasRole('ADMIN')") - public LotteryResponse createLottery(@Valid @RequestBody LotteryRequestDto lotteryDTO) throws Exception { + public LotteryResponseDto createLottery(@Valid @RequestBody LotteryRequestDto lotteryDTO) throws Exception { return lotteryService.createLottery(lotteryDTO); } -// public ResponseEntity createLottery(@RequestBody @Valid LotteryRequestDto lotteryDTO) throws Exception { -// Lottery createdLottery = lotteryService.createLottery(lotteryDTO); -// return ResponseEntity.ok(createdLottery); -// } } From 71e7ce7c5db4248d992afc6d65296aaf5d468154 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Wed, 21 Feb 2024 00:05:35 +0700 Subject: [PATCH 31/54] feat(Lottery) : implement Join column on all entity --- .../kbtg/bootcamp/lottery/entity/Lottery.java | 4 +- .../bootcamp/lottery/entity/UserTicket.java | 52 +++++++++++-------- .../kbtg/bootcamp/lottery/entity/Users.java | 11 +++- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java index 4ab6f67c..072f2ca3 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java @@ -16,7 +16,6 @@ public class Lottery { @Id - @Column(name = "ticket_number") private String ticketNumber; @Column(name = "price") private BigDecimal ticketPrice; @@ -35,7 +34,6 @@ public void setTicketAmount(Integer ticketAmount) { this.ticketAmount = ticketAmount; } - public String getTicketNumber() { return ticketNumber; } @@ -64,5 +62,7 @@ public void setLastUpdate(LocalDateTime lastUpdate) { this.lastUpdate = lastUpdate; } + public Lottery() { + } } diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/UserTicket.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/UserTicket.java index b355acbe..c20d3efa 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/UserTicket.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/UserTicket.java @@ -11,13 +11,15 @@ public class UserTicket { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private Integer ticket_id; + private Integer ticketId; - @Column(name = "ticket_number") - private String ticketNumber; + @ManyToOne + @JoinColumn(name = "ticket_number", referencedColumnName = "ticketNumber") + private Lottery lottery; - @Column(name = "user_id") - private String userId; + @ManyToOne + @JoinColumn(name = "user_id", referencedColumnName = "userId") + private Users userId; @Column(name = "is_sold_back_flag") private String isSoldBackFlag; @@ -31,28 +33,13 @@ public class UserTicket { @Column(name = "last_updated") private LocalDateTime lastUpdated; + public Integer getTicketId() { - return ticket_id; + return ticketId; } public void setTicketId(Integer ticket_id) { - this.ticket_id = ticket_id; - } - - public String getTicketNumber() { - return ticketNumber; - } - - public void setTicketNumber(String ticketNumber) { - this.ticketNumber = ticketNumber; - } - - public String getUserId() { - return userId; - } - - public void setUserId(String userId) { - this.userId = userId; + this.ticketId = ticket_id; } public String getIsSoldBackFlag() { @@ -86,4 +73,23 @@ public LocalDateTime getLastUpdated() { public void setLastUpdated(LocalDateTime lastUpdated) { this.lastUpdated = lastUpdated; } + + + public void setLotteryNumber(String lotteryNumber) { + this.lottery = new Lottery(); + this.lottery.setTicketNumber(lotteryNumber); + } + + public String getLotteryNumber() { + return this.lottery.getTicketNumber(); + } + public void setUserId(String userId) { + this.userId = new Users(); + this.userId.setUserId(userId); + } + + public Users getUserId() { + return userId; + } + } \ No newline at end of file diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Users.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Users.java index 8d01c7c3..55e943f1 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Users.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Users.java @@ -2,19 +2,28 @@ import jakarta.persistence.*; +import java.util.List; + @Entity @Table(name = "users") public class Users { + @OneToMany(mappedBy = "userId") + private List tickets; + @Id - @Column(name = "user_id") private String userId; public void setUserId(String userId) { this.userId = userId; } + public String getUserId() { return userId; } + + public List getTickets() { + return tickets; + } } From 2f8cfb485d8cb0486c28d7d278834f683d4765b6 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Wed, 21 Feb 2024 00:05:55 +0700 Subject: [PATCH 32/54] feat(Lottery) : implement query for UserTicketRepository --- .../repository/UserTicketRepository.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/UserTicketRepository.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/UserTicketRepository.java index 62419b40..b6dc6baf 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/UserTicketRepository.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/UserTicketRepository.java @@ -1,7 +1,34 @@ package com.kbtg.bootcamp.lottery.repository; +import com.kbtg.bootcamp.lottery.entity.Lottery; import com.kbtg.bootcamp.lottery.entity.UserTicket; +import com.kbtg.bootcamp.lottery.entity.Users; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.math.BigDecimal; +import java.util.List; public interface UserTicketRepository extends JpaRepository { + List findByUserId(Users userId); + @Modifying + @Query("SELECT SUM(ut.pricePaid) FROM UserTicket ut WHERE ut.userId = :userId") + BigDecimal getTotalPricePaidByUserId(@Param("userId") Users userId); + + @Modifying + @Query("SELECT COUNT(ut) FROM UserTicket ut WHERE ut.userId = :userId") + Long getTicketCountByUserId(@Param("userId") Users userId); + + @Modifying + @Query("DELETE FROM UserTicket ut WHERE ut.userId = :userId and ut.ticketId = :ticketId") + void deleteLotteryTicketById(@Param("userId") Users userId, @Param("ticketId") Long ticketId); + + @Query("SELECT ut.lottery FROM UserTicket ut WHERE ut.userId = :userId and ut.ticketId = :ticketId") + Lottery findByTicketIdAndUserId(@Param("userId") Users userId, @Param("ticketId") Long ticketId); + + @Query("SELECT COUNT(ut) > 0 FROM UserTicket ut WHERE ut.userId = :userId AND ut.ticketId = :ticketId") + boolean existsByTicketIdAndUserId(@Param("userId") Users userId, @Param("ticketId") String ticketId); + } From 8c09dfaf5ae87a610b276be4d700999f7372f26c Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Wed, 21 Feb 2024 00:07:19 +0700 Subject: [PATCH 33/54] feat(Lottery) : implement delete, purchase and get lottery by user and implement valid data --- .../lottery/controller/UserController.java | 86 +++++++++++++++++-- 1 file changed, 80 insertions(+), 6 deletions(-) diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/UserController.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/UserController.java index c0f37209..697053a1 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/UserController.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/UserController.java @@ -1,12 +1,14 @@ package com.kbtg.bootcamp.lottery.controller; +import com.kbtg.bootcamp.lottery.exception.BadRequestException; import com.kbtg.bootcamp.lottery.response.LotteryPurchaseResponseDto; +import com.kbtg.bootcamp.lottery.response.LotteryResponseDto; +import com.kbtg.bootcamp.lottery.response.LotteryUserResponseDto; import com.kbtg.bootcamp.lottery.service.LotteryService; import org.springframework.beans.factory.annotation.Autowired; -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; +import org.springframework.web.bind.annotation.*; + +import static java.util.regex.Pattern.matches; @RestController @RequestMapping("/users") @@ -19,10 +21,82 @@ public UserController(LotteryService lotteryService) { this.lotteryService = lotteryService; } + + @GetMapping("/{userId}/lotteries") + public LotteryUserResponseDto getLotteries(@PathVariable("userId") String userId) throws Exception { + String userValidationResult = validateUserId(userId); + if (!userValidationResult.equals("valid")) { + throw new BadRequestException(userValidationResult); + } + + return lotteryService.getUserLotteryTickets(userId); + } + @PostMapping("/{userId}/lotteries/{lotteryNumber}") - public LotteryPurchaseResponseDto purchaseLotteryTicket(@PathVariable String userId, @PathVariable String lotteryNumber) throws Exception { + public LotteryPurchaseResponseDto purchaseLotteryTicket(@PathVariable("userId") String userId, @PathVariable("lotteryNumber") String lotteryNumber) throws Exception { + String userValidationResult = validateUserId(userId); + if (!userValidationResult.equals("valid")) { + throw new BadRequestException(userValidationResult); + } + + String lotteryValidationResult = validateLotteryNumber(lotteryNumber); + if (!lotteryValidationResult.equals("valid")) { + throw new BadRequestException(lotteryValidationResult); + } + return lotteryService.purchaseLotteryTicket(userId, lotteryNumber); } - // Additional methods as per requirements... + @DeleteMapping("/{userId}/lotteries/{ticketId}") + public LotteryResponseDto deleteLottery(@PathVariable("userId") String userId, @PathVariable("ticketId") String ticketId) throws Exception { + + String userValidationResult = validateUserId(userId); + if (!userValidationResult.equals("valid")) { + throw new BadRequestException(userValidationResult); + } + + String ticketValidationResult = validateTicketId(ticketId); + if (!ticketValidationResult.equals("valid")) { + throw new BadRequestException(ticketValidationResult); + } + + return lotteryService.deleteLottery(userId, ticketId); + } + + private String validateUserId(String userId) { + if (userId == null || userId.isBlank()) { + return "User ID cannot be null or empty."; + } + if (userId.isEmpty() || userId.length() > 100) { + return "User ID must be between 1 and 100 characters."; + } + if (!matches("^[a-zA-Z0-9]*$", userId)) { + return "User ID must be alphanumeric."; + } + return "valid"; + } + + private String validateLotteryNumber(String lotteryNumber) { + if (lotteryNumber == null || lotteryNumber.isBlank()) { + return "Lottery number cannot be null or empty."; + } + if (lotteryNumber.length() != 6) { + return "Lottery number must be 6 numbers."; + } + if (!matches("^[0-9]*$", lotteryNumber)) { + return "Lottery number must be numeric."; + } + return "valid"; + } + + private String validateTicketId(String ticketId) { + if (ticketId == null || ticketId.isBlank()) { + return "TicketId cannot be null or empty."; + } + if (!matches("^[0-9]*$", ticketId)) { + return "TicketId must be numeric."; + } + return "valid"; + } + } \ No newline at end of file From e548b835713e8737cb0d0edde351731298931ee6 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Wed, 21 Feb 2024 00:07:38 +0700 Subject: [PATCH 34/54] feat(Lottery) : implement LotteryUserResponseDto --- .../response/LotteryUserResponseDto.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryUserResponseDto.java diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryUserResponseDto.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryUserResponseDto.java new file mode 100644 index 00000000..0f91be96 --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryUserResponseDto.java @@ -0,0 +1,25 @@ +package com.kbtg.bootcamp.lottery.response; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.math.BigDecimal; +import java.util.List; + +public class LotteryUserResponseDto { + @JsonProperty("tickets") + private List ticketNumber; + + @JsonProperty("cost") + private BigDecimal pricePaid; + + @JsonProperty("count") + private Long totalTicket; + + + + public LotteryUserResponseDto(List ticketNumbers, BigDecimal totalPrice, Long totalTicket) { + this.ticketNumber = ticketNumbers; + this.pricePaid = totalPrice; + this.totalTicket = totalTicket; + } +} From da7fc286450d0835d73c55cfe9283cc26424399d Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Wed, 21 Feb 2024 00:08:18 +0700 Subject: [PATCH 35/54] feat(Lottery) : implement getUserLottery and deleteLottery --- .../com/kbtg/bootcamp/lottery/service/LotteryService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/LotteryService.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/LotteryService.java index e8dd4bdb..b0725957 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/LotteryService.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/LotteryService.java @@ -3,10 +3,14 @@ import com.kbtg.bootcamp.lottery.request.LotteryRequestDto; import com.kbtg.bootcamp.lottery.response.LotteryPurchaseResponseDto; import com.kbtg.bootcamp.lottery.response.LotteryResponseDto; +import com.kbtg.bootcamp.lottery.response.LotteryUserResponseDto; public interface LotteryService { LotteryResponseDto createLottery(LotteryRequestDto lotteryDTO) throws Exception; LotteryResponseDto getAllLotteryTickets() throws Exception; LotteryPurchaseResponseDto purchaseLotteryTicket(String userId, String ticketNumber) throws Exception; + LotteryUserResponseDto getUserLotteryTickets(String userId) throws Exception; + LotteryResponseDto deleteLottery(String userId, String ticketId) throws Exception; + } From 708b8f321acfecc6f2725673ab8565cc46fbfbda Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Wed, 21 Feb 2024 00:08:58 +0700 Subject: [PATCH 36/54] feat(Lottery) : implement deleteLottery and refactor class name on another function --- .../service/impl/LotteryServiceImpl.java | 69 +++++++++++++++---- 1 file changed, 55 insertions(+), 14 deletions(-) diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java index 9ea26dfe..5181369c 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java @@ -10,6 +10,7 @@ import com.kbtg.bootcamp.lottery.request.LotteryRequestDto; import com.kbtg.bootcamp.lottery.response.LotteryPurchaseResponseDto; import com.kbtg.bootcamp.lottery.response.LotteryResponseDto; +import com.kbtg.bootcamp.lottery.response.LotteryUserResponseDto; import com.kbtg.bootcamp.lottery.service.LotteryService; import jakarta.transaction.Transactional; import org.springframework.beans.factory.annotation.Autowired; @@ -20,7 +21,7 @@ import java.util.List; import java.util.Optional; -import static com.kbtg.bootcamp.lottery.lib.Const.*; +import static com.kbtg.bootcamp.lottery.lib.Constant.*; @Service public class LotteryServiceImpl implements LotteryService { @@ -37,7 +38,7 @@ public LotteryServiceImpl(LotteryRepository lotteryRepository, UserTicketReposit @Override @Transactional - public LotteryResponseDto createLottery(LotteryRequestDto lotteryDTO) { + public LotteryResponseDto createLottery(LotteryRequestDto lotteryDTO) throws Exception { Optional optionalLottery = lotteryRepository.findById(lotteryDTO.getTicketNumber()); if (optionalLottery.isPresent()) { Lottery lottery = optionalLottery.get(); @@ -61,23 +62,63 @@ public LotteryResponseDto createLottery(LotteryRequestDto lotteryDTO) { @Override public LotteryResponseDto getAllLotteryTickets() throws Exception { - List ticketNumbers = lotteryRepository.findAll().stream() + List ticketNumbers = lotteryRepository + .findAll() + .stream() .map(Lottery::getTicketNumber) .toList(); return new LotteryResponseDto(ticketNumbers); } + @Override + @Transactional + public LotteryResponseDto deleteLottery(String userId, String ticketId) throws Exception { + Users user = new Users(); + user.setUserId(userId); + boolean isExist = userTicketRepository.existsByTicketIdAndUserId(user, ticketId); + if (!isExist) { + throw new NotFoundException(TICKET_NOT_FOUND_OR_USER_ID); + } + Lottery ticket = userTicketRepository.findByTicketIdAndUserId(user, Long.valueOf(ticketId)); + userTicketRepository.deleteLotteryTicketById(user, Long.valueOf(ticketId)); + Optional optionalLottery = lotteryRepository.findById(ticket.getTicketNumber()); + if (optionalLottery.isPresent()) { + Lottery lottery = optionalLottery.get(); + lottery.setTicketAmount(lottery.getTicketAmount() + 1); + lotteryRepository.save(lottery); + } + return new LotteryResponseDto(ticket.getTicketNumber()); + } + @Override @Transactional - public LotteryPurchaseResponseDto purchaseLotteryTicket(final String userId, final String ticketNumber) { + public LotteryUserResponseDto getUserLotteryTickets(String userId) throws Exception { + Users user = new Users(); + user.setUserId(userId); + List userTickets = userTicketRepository.findByUserId(user); + if (userTickets.isEmpty()) { + throw new NotFoundException(String.format(USER_NOT_FOUND, userId)); + } + List ticketNumbers = userTickets.stream() + .map(UserTicket::getLotteryNumber) + .toList(); + BigDecimal totalPrice = userTicketRepository.getTotalPricePaidByUserId(user); + Long totalTicket = userTicketRepository.getTicketCountByUserId(user); + return new LotteryUserResponseDto(ticketNumbers, totalPrice, totalTicket); + } + + @Override + @Transactional + public LotteryPurchaseResponseDto purchaseLotteryTicket(final String userId,final String lotteryNumber) throws Exception { + Optional optionalLottery = lotteryRepository.findById(lotteryNumber); - Optional optionalLottery = lotteryRepository.findById(ticketNumber); if (optionalLottery.isEmpty()) { - throw new NotFoundException(String.format(LOTTERY_NOT_FOUND, ticketNumber)); + throw new NotFoundException(String.format(LOTTERY_NOT_FOUND, lotteryNumber)); } + Lottery lottery = optionalLottery.get(); - validateLotteryAvailable(lottery, ticketNumber); + validateLotteryAvailable(lottery, lotteryNumber); Optional optionalUser = usersRepository.findById(userId); @@ -86,31 +127,31 @@ public LotteryPurchaseResponseDto purchaseLotteryTicket(final String userId, fin } decrementTicketAmount(lottery); - final UserTicket userTicket = createUserTicket(userId, ticketNumber, lottery.getTicketPrice()); + final UserTicket userTicket = createUserTicket(userId, lotteryNumber, lottery.getTicketPrice()); userTicketRepository.save(userTicket); return new LotteryPurchaseResponseDto(userTicket.getTicketId()); } - private void validateLotteryAvailable(final Lottery lottery, final String ticketNumber) { + private void validateLotteryAvailable(Lottery lottery, String ticketNumber) { if (lottery.getTicketAmount() <= 0) { throw new NotFoundException(String.format(LOTTERY_SOLD_OUT_MESSAGE_FORMAT, ticketNumber)); } } - private void createUser(final String userId) { + private void createUser(String userId) { final Users newUser = new Users(); newUser.setUserId(userId); usersRepository.save(newUser); } - private void decrementTicketAmount(final Lottery lottery) { + private void decrementTicketAmount(Lottery lottery) { lottery.setTicketAmount(lottery.getTicketAmount() - 1); lotteryRepository.save(lottery); } - private UserTicket createUserTicket(final String userId, final String ticketNumber, final BigDecimal ticketPrice) { - final UserTicket userTicket = new UserTicket(); - userTicket.setTicketNumber(ticketNumber); + private UserTicket createUserTicket(String userId, String ticketNumber, BigDecimal ticketPrice) { + UserTicket userTicket = new UserTicket(); + userTicket.setLotteryNumber(ticketNumber); userTicket.setUserId(userId); userTicket.setPricePaid(ticketPrice); userTicket.setPurchaseDate(LocalDateTime.now()); From b58d2f17dca3fe5775bc176bd1498ce9e9eeac51 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Wed, 21 Feb 2024 00:09:31 +0700 Subject: [PATCH 37/54] feat(Lottery) : implement globalException about validate --- .../exception/GlobalExceptionHandler.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/GlobalExceptionHandler.java diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/GlobalExceptionHandler.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/GlobalExceptionHandler.java new file mode 100644 index 00000000..1396e8af --- /dev/null +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/GlobalExceptionHandler.java @@ -0,0 +1,25 @@ +package com.kbtg.bootcamp.lottery.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.context.request.WebRequest; + +import java.time.ZonedDateTime; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleValidationExceptions(MethodArgumentNotValidException ex, WebRequest request) { + String message = ex.getBindingResult().getAllErrors().get(0).getDefaultMessage(); + ApiErrorResponse errors = new ApiErrorResponse( + message, + HttpStatus.BAD_REQUEST, + ZonedDateTime.now()); + + return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); + } +} \ No newline at end of file From 398fae8b40330ef08f736669251adcf6a715baad Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Wed, 21 Feb 2024 00:09:46 +0700 Subject: [PATCH 38/54] feat(Lottery) : add more constant variable --- .../kbtg/bootcamp/lottery/lib/{Const.java => Constant.java} | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) rename posttest/src/main/java/com/kbtg/bootcamp/lottery/lib/{Const.java => Constant.java} (58%) diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/lib/Const.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/lib/Constant.java similarity index 58% rename from posttest/src/main/java/com/kbtg/bootcamp/lottery/lib/Const.java rename to posttest/src/main/java/com/kbtg/bootcamp/lottery/lib/Constant.java index 6f1978b0..d4d568e0 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/lib/Const.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/lib/Constant.java @@ -1,7 +1,9 @@ package com.kbtg.bootcamp.lottery.lib; -public final class Const { +public final class Constant { public static final String LOTTERY_NOT_SOLD_BACK_FLAG = "N"; public static final String LOTTERY_SOLD_OUT_MESSAGE_FORMAT = "Lottery number: %s sold out"; public static final String LOTTERY_NOT_FOUND = "Lottery number: %s not found."; + public static final String USER_NOT_FOUND = "User %s not found."; + public static final String TICKET_NOT_FOUND_OR_USER_ID = "Ticket not found or user id not found."; } From 582d887a6aafee56cfcaaef0694789e03b8ff57e Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Wed, 21 Feb 2024 00:10:19 +0700 Subject: [PATCH 39/54] feat(Lottery) : refactor annotation valid to validate --- .../kbtg/bootcamp/lottery/exception/ApiExceptionHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/ApiExceptionHandler.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/ApiExceptionHandler.java index 755466d0..8ae08f9c 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/ApiExceptionHandler.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/ApiExceptionHandler.java @@ -18,7 +18,7 @@ public ResponseEntity handleNotFoundException(NotFoundExceptio } @ExceptionHandler({ BadRequestException.class }) - public ResponseEntity handleNotFoundException(BadRequestException e) { + public ResponseEntity BadRequestException(BadRequestException e) { ApiErrorResponse errorResponse = new ApiErrorResponse(e.getMessage(), HttpStatus.BAD_REQUEST, ZonedDateTime.now()); return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); From f892a39e1fd3bcc2fc6743a00a8e8dcaecf763c7 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Wed, 21 Feb 2024 00:10:24 +0700 Subject: [PATCH 40/54] feat(Lottery) : refactor annotation valid to validate --- .../com/kbtg/bootcamp/lottery/controller/AdminController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/AdminController.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/AdminController.java index 2c18cf5e..f944b583 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/AdminController.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/AdminController.java @@ -3,9 +3,9 @@ import com.kbtg.bootcamp.lottery.request.LotteryRequestDto; import com.kbtg.bootcamp.lottery.response.LotteryResponseDto; import com.kbtg.bootcamp.lottery.service.LotteryService; -import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -25,7 +25,7 @@ public AdminController(LotteryService lotteryService) { @PostMapping("/lotteries") @PreAuthorize("hasRole('ADMIN')") - public LotteryResponseDto createLottery(@Valid @RequestBody LotteryRequestDto lotteryDTO) throws Exception { + public LotteryResponseDto createLottery(@Validated @RequestBody LotteryRequestDto lotteryDTO) throws Exception { return lotteryService.createLottery(lotteryDTO); } From 83c9cf679be308db2a83160dd0851c7878a75a17 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Wed, 21 Feb 2024 00:14:30 +0700 Subject: [PATCH 41/54] feat(Lottery) : delete @Modify from SELECT Query on UserTicketRepository --- .../kbtg/bootcamp/lottery/repository/UserTicketRepository.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/UserTicketRepository.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/UserTicketRepository.java index b6dc6baf..ce21a5b2 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/UserTicketRepository.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/UserTicketRepository.java @@ -13,11 +13,9 @@ public interface UserTicketRepository extends JpaRepository { List findByUserId(Users userId); - @Modifying @Query("SELECT SUM(ut.pricePaid) FROM UserTicket ut WHERE ut.userId = :userId") BigDecimal getTotalPricePaidByUserId(@Param("userId") Users userId); - @Modifying @Query("SELECT COUNT(ut) FROM UserTicket ut WHERE ut.userId = :userId") Long getTicketCountByUserId(@Param("userId") Users userId); From a70749e462c9c56a0543288e9fa707c5119e257b Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Wed, 21 Feb 2024 02:43:12 +0700 Subject: [PATCH 42/54] feat(Lottery) : delete sold_back_flag because Delete Method is requirement and clean code --- posttest/database/init.sql | 1 - .../com/kbtg/bootcamp/lottery/Application.java | 6 +++--- .../lottery/controller/UserController.java | 6 +++--- .../kbtg/bootcamp/lottery/entity/UserTicket.java | 14 ++------------ .../com/kbtg/bootcamp/lottery/entity/Users.java | 8 ++++++++ .../lottery/exception/ApiExceptionHandler.java | 4 ++-- .../com/kbtg/bootcamp/lottery/lib/Constant.java | 1 + .../lottery/repository/UserTicketRepository.java | 1 + .../lottery/response/LotteryUserResponseDto.java | 1 - .../bootcamp/lottery/service/LotteryService.java | 6 +++++- .../lottery/service/impl/LotteryServiceImpl.java | 16 +++++++--------- .../src/main/resources/application.properties | 9 ++++++--- 12 files changed, 38 insertions(+), 35 deletions(-) diff --git a/posttest/database/init.sql b/posttest/database/init.sql index 01d2609c..0ff6b3b0 100644 --- a/posttest/database/init.sql +++ b/posttest/database/init.sql @@ -19,7 +19,6 @@ CREATE TABLE user_ticket ( ticket_number VARCHAR(6) NOT NULL, user_id VARCHAR(100) NOT NULL, price_paid DECIMAL NOT NULL, - is_sold_back_flag VARCHAR(1) DEFAULT 'N', purchase_date TIMESTAMP WITHOUT TIME ZONE DEFAULT (NOW()), created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW(), last_updated TIMESTAMP WITHOUT TIME ZONE DEFAULT (NOW()), diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/Application.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/Application.java index 8826641f..6d0fdc54 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/Application.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/Application.java @@ -5,7 +5,7 @@ @SpringBootApplication public class Application { - public static void main(String[] args) { - SpringApplication.run(Application.class, args); - } + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } } \ No newline at end of file diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/UserController.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/UserController.java index 697053a1..225a9547 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/UserController.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/UserController.java @@ -33,7 +33,7 @@ public LotteryUserResponseDto getLotteries(@PathVariable("userId") String userId } @PostMapping("/{userId}/lotteries/{lotteryNumber}") - public LotteryPurchaseResponseDto purchaseLotteryTicket(@PathVariable("userId") String userId, @PathVariable("lotteryNumber") String lotteryNumber) throws Exception { + public LotteryPurchaseResponseDto purchaseLotteryTicket(@PathVariable("userId") String userId, @PathVariable("lotteryNumber") String lotteryNumber) throws Exception { String userValidationResult = validateUserId(userId); if (!userValidationResult.equals("valid")) { throw new BadRequestException(userValidationResult); @@ -48,7 +48,7 @@ public LotteryPurchaseResponseDto purchaseLotteryTicket(@PathVariable("userId") } @DeleteMapping("/{userId}/lotteries/{ticketId}") - public LotteryResponseDto deleteLottery(@PathVariable("userId") String userId, @PathVariable("ticketId") String ticketId) throws Exception { + public LotteryResponseDto deleteLottery(@PathVariable("userId") String userId, @PathVariable("ticketId") String ticketId) throws Exception { String userValidationResult = validateUserId(userId); if (!userValidationResult.equals("valid")) { @@ -60,7 +60,7 @@ public LotteryResponseDto deleteLottery(@PathVariable("userId") String userId, @ throw new BadRequestException(ticketValidationResult); } - return lotteryService.deleteLottery(userId, ticketId); + return lotteryService.soldBackLotteryTicket(userId, ticketId); } private String validateUserId(String userId) { diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/UserTicket.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/UserTicket.java index c20d3efa..11151594 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/UserTicket.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/UserTicket.java @@ -21,9 +21,6 @@ public class UserTicket { @JoinColumn(name = "user_id", referencedColumnName = "userId") private Users userId; - @Column(name = "is_sold_back_flag") - private String isSoldBackFlag; - @Column(name = "price_paid") private BigDecimal pricePaid; @@ -42,14 +39,6 @@ public void setTicketId(Integer ticket_id) { this.ticketId = ticket_id; } - public String getIsSoldBackFlag() { - return isSoldBackFlag; - } - - public void setIsSoldBackFlag(String isSoldBackFlag) { - this.isSoldBackFlag = isSoldBackFlag; - } - public BigDecimal getPricePaid() { return pricePaid; } @@ -81,8 +70,9 @@ public void setLotteryNumber(String lotteryNumber) { } public String getLotteryNumber() { - return this.lottery.getTicketNumber(); + return this.lottery.getTicketNumber(); } + public void setUserId(String userId) { this.userId = new Users(); this.userId.setUserId(userId); diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Users.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Users.java index 55e943f1..4448596b 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Users.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Users.java @@ -14,10 +14,18 @@ public class Users { @Id private String userId; + public void setUserId(String userId) { this.userId = userId; } + public Users() { + + } + + public Users(String userId) { + this.userId = userId; + } public String getUserId() { return userId; diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/ApiExceptionHandler.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/ApiExceptionHandler.java index 8ae08f9c..302ba8e9 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/ApiExceptionHandler.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/exception/ApiExceptionHandler.java @@ -10,14 +10,14 @@ @RestControllerAdvice public class ApiExceptionHandler { - @ExceptionHandler({ NotFoundException.class }) + @ExceptionHandler({NotFoundException.class}) public ResponseEntity handleNotFoundException(NotFoundException e) { ApiErrorResponse errorResponse = new ApiErrorResponse(e.getMessage(), HttpStatus.NOT_FOUND, ZonedDateTime.now()); return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); } - @ExceptionHandler({ BadRequestException.class }) + @ExceptionHandler({BadRequestException.class}) public ResponseEntity BadRequestException(BadRequestException e) { ApiErrorResponse errorResponse = new ApiErrorResponse(e.getMessage(), HttpStatus.BAD_REQUEST, ZonedDateTime.now()); diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/lib/Constant.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/lib/Constant.java index d4d568e0..c2012f3f 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/lib/Constant.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/lib/Constant.java @@ -2,6 +2,7 @@ public final class Constant { public static final String LOTTERY_NOT_SOLD_BACK_FLAG = "N"; + public static final String LOTTERY_SOLD_BACK_FLAG = "Y"; public static final String LOTTERY_SOLD_OUT_MESSAGE_FORMAT = "Lottery number: %s sold out"; public static final String LOTTERY_NOT_FOUND = "Lottery number: %s not found."; public static final String USER_NOT_FOUND = "User %s not found."; diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/UserTicketRepository.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/UserTicketRepository.java index ce21a5b2..3494eaf7 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/UserTicketRepository.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/repository/UserTicketRepository.java @@ -13,6 +13,7 @@ public interface UserTicketRepository extends JpaRepository { List findByUserId(Users userId); + @Query("SELECT SUM(ut.pricePaid) FROM UserTicket ut WHERE ut.userId = :userId") BigDecimal getTotalPricePaidByUserId(@Param("userId") Users userId); diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryUserResponseDto.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryUserResponseDto.java index 0f91be96..67b1d756 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryUserResponseDto.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryUserResponseDto.java @@ -16,7 +16,6 @@ public class LotteryUserResponseDto { private Long totalTicket; - public LotteryUserResponseDto(List ticketNumbers, BigDecimal totalPrice, Long totalTicket) { this.ticketNumber = ticketNumbers; this.pricePaid = totalPrice; diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/LotteryService.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/LotteryService.java index b0725957..d5798c4a 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/LotteryService.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/LotteryService.java @@ -8,9 +8,13 @@ public interface LotteryService { LotteryResponseDto createLottery(LotteryRequestDto lotteryDTO) throws Exception; + LotteryResponseDto getAllLotteryTickets() throws Exception; + LotteryPurchaseResponseDto purchaseLotteryTicket(String userId, String ticketNumber) throws Exception; + LotteryUserResponseDto getUserLotteryTickets(String userId) throws Exception; - LotteryResponseDto deleteLottery(String userId, String ticketId) throws Exception; + + LotteryResponseDto soldBackLotteryTicket(String userId, String ticketId) throws Exception; } diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java index 5181369c..f4f2c33d 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java @@ -72,14 +72,13 @@ public LotteryResponseDto getAllLotteryTickets() throws Exception { @Override @Transactional - public LotteryResponseDto deleteLottery(String userId, String ticketId) throws Exception { - Users user = new Users(); - user.setUserId(userId); + public LotteryResponseDto soldBackLotteryTicket(String userId, String ticketId) throws Exception { + Users user = new Users(userId); boolean isExist = userTicketRepository.existsByTicketIdAndUserId(user, ticketId); if (!isExist) { throw new NotFoundException(TICKET_NOT_FOUND_OR_USER_ID); } - Lottery ticket = userTicketRepository.findByTicketIdAndUserId(user, Long.valueOf(ticketId)); + Lottery ticket = userTicketRepository.findByTicketIdAndUserId(user, Long.valueOf(ticketId)); userTicketRepository.deleteLotteryTicketById(user, Long.valueOf(ticketId)); Optional optionalLottery = lotteryRepository.findById(ticket.getTicketNumber()); if (optionalLottery.isPresent()) { @@ -94,8 +93,7 @@ public LotteryResponseDto deleteLottery(String userId, String ticketId) throws E @Override @Transactional public LotteryUserResponseDto getUserLotteryTickets(String userId) throws Exception { - Users user = new Users(); - user.setUserId(userId); + Users user = new Users(userId); List userTickets = userTicketRepository.findByUserId(user); if (userTickets.isEmpty()) { throw new NotFoundException(String.format(USER_NOT_FOUND, userId)); @@ -110,11 +108,11 @@ public LotteryUserResponseDto getUserLotteryTickets(String userId) throws Except @Override @Transactional - public LotteryPurchaseResponseDto purchaseLotteryTicket(final String userId,final String lotteryNumber) throws Exception { + public LotteryPurchaseResponseDto purchaseLotteryTicket(final String userId, final String lotteryNumber) throws Exception { Optional optionalLottery = lotteryRepository.findById(lotteryNumber); if (optionalLottery.isEmpty()) { - throw new NotFoundException(String.format(LOTTERY_NOT_FOUND, lotteryNumber)); + throw new NotFoundException(String.format(LOTTERY_NOT_FOUND, lotteryNumber)); } Lottery lottery = optionalLottery.get(); @@ -122,7 +120,7 @@ public LotteryPurchaseResponseDto purchaseLotteryTicket(final String userId,fina Optional optionalUser = usersRepository.findById(userId); - if (optionalUser.isEmpty()){ + if (optionalUser.isEmpty()) { createUser(userId); } diff --git a/posttest/src/main/resources/application.properties b/posttest/src/main/resources/application.properties index 2a6c3d84..37c1e46a 100644 --- a/posttest/src/main/resources/application.properties +++ b/posttest/src/main/resources/application.properties @@ -1,8 +1,11 @@ server.port=8888 # DataSource Configuration -spring.datasource.url=jdbc:postgresql://localhost:5432/lottery -spring.datasource.username=lottery -spring.datasource.password=Plottery001!@# +#spring.datasource.url=jdbc:postgresql://localhost:5432/lottery +#spring.datasource.username=lottery +#spring.datasource.password=Plottery001!@# +spring.datasource.url=${DATABASE_URL} +spring.datasource.username=${DATABASE_USERNAME} +spring.datasource.password=${DATABASE_PASSWORD} spring.datasource.driver-class-name=org.postgresql.Driver # JPA & Hibernate Configuration From 44148ad373a64aaf2e86a18a57d208c040e4a8ed Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Wed, 21 Feb 2024 02:43:29 +0700 Subject: [PATCH 43/54] feat(Lottery) : delete sold_back_flag because Delete Method is requirement and clean code --- .../kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java index f4f2c33d..810b4af8 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java @@ -153,7 +153,6 @@ private UserTicket createUserTicket(String userId, String ticketNumber, BigDecim userTicket.setUserId(userId); userTicket.setPricePaid(ticketPrice); userTicket.setPurchaseDate(LocalDateTime.now()); - userTicket.setIsSoldBackFlag(LOTTERY_NOT_SOLD_BACK_FLAG); userTicket.setLastUpdated(LocalDateTime.now()); return userTicket; } From d5bc16ef3d31c002ab01216710d3052ac618d49e Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Wed, 21 Feb 2024 02:55:20 +0700 Subject: [PATCH 44/54] feat(Lottery) : delete sold_back_flag because Delete Method is requirement and clean code --- .../src/main/java/com/kbtg/bootcamp/lottery/lib/Constant.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/lib/Constant.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/lib/Constant.java index c2012f3f..5e7e66fa 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/lib/Constant.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/lib/Constant.java @@ -1,8 +1,6 @@ package com.kbtg.bootcamp.lottery.lib; public final class Constant { - public static final String LOTTERY_NOT_SOLD_BACK_FLAG = "N"; - public static final String LOTTERY_SOLD_BACK_FLAG = "Y"; public static final String LOTTERY_SOLD_OUT_MESSAGE_FORMAT = "Lottery number: %s sold out"; public static final String LOTTERY_NOT_FOUND = "Lottery number: %s not found."; public static final String USER_NOT_FOUND = "User %s not found."; From 72a91baa4f17f9c6b139c5bc33580b62072a87ab Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Wed, 21 Feb 2024 21:09:30 +0700 Subject: [PATCH 45/54] Test(UserController) : refactor code a bit --- .../kbtg/bootcamp/lottery/entity/Lottery.java | 21 +++++++++---------- .../bootcamp/lottery/entity/UserTicket.java | 11 +++++----- .../kbtg/bootcamp/lottery/entity/Users.java | 13 +++++++----- .../lottery/request/LotteryRequestDto.java | 8 +++---- 4 files changed, 27 insertions(+), 26 deletions(-) diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java index 072f2ca3..01aa4999 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java @@ -22,14 +22,17 @@ public class Lottery { @Column(name = "amount_available") private Integer ticketAmount; + @Column(name = "last_updated") + private LocalDateTime lastUpdate; + + public Lottery() { + + } public Integer getTicketAmount() { return ticketAmount; } - @Column(name = "last_updated") - private LocalDateTime lastUpdate; - public void setTicketAmount(Integer ticketAmount) { this.ticketAmount = ticketAmount; } @@ -38,14 +41,14 @@ public String getTicketNumber() { return ticketNumber; } - public List getTicketNumbers() { - return Collections.singletonList(ticketNumber); - } - public void setTicketNumber(String ticketNumber) { this.ticketNumber = ticketNumber; } + public List getTicketNumbers() { + return Collections.singletonList(ticketNumber); + } + public BigDecimal getTicketPrice() { return ticketPrice; } @@ -61,8 +64,4 @@ public LocalDateTime getLastUpdate() { public void setLastUpdate(LocalDateTime lastUpdate) { this.lastUpdate = lastUpdate; } - - public Lottery() { - - } } diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/UserTicket.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/UserTicket.java index 11151594..76efe02a 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/UserTicket.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/UserTicket.java @@ -63,14 +63,17 @@ public void setLastUpdated(LocalDateTime lastUpdated) { this.lastUpdated = lastUpdated; } + public String getLotteryNumber() { + return this.lottery.getTicketNumber(); + } public void setLotteryNumber(String lotteryNumber) { this.lottery = new Lottery(); this.lottery.setTicketNumber(lotteryNumber); } - public String getLotteryNumber() { - return this.lottery.getTicketNumber(); + public Users getUserId() { + return userId; } public void setUserId(String userId) { @@ -78,8 +81,4 @@ public void setUserId(String userId) { this.userId.setUserId(userId); } - public Users getUserId() { - return userId; - } - } \ No newline at end of file diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Users.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Users.java index 4448596b..4b2c995d 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Users.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Users.java @@ -1,6 +1,9 @@ package com.kbtg.bootcamp.lottery.entity; -import jakarta.persistence.*; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; import java.util.List; @@ -15,10 +18,6 @@ public class Users { private String userId; - public void setUserId(String userId) { - this.userId = userId; - } - public Users() { } @@ -31,6 +30,10 @@ public String getUserId() { return userId; } + public void setUserId(String userId) { + this.userId = userId; + } + public List getTickets() { return tickets; } diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/request/LotteryRequestDto.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/request/LotteryRequestDto.java index 04534729..c6ce94ae 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/request/LotteryRequestDto.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/request/LotteryRequestDto.java @@ -34,6 +34,10 @@ public String getTicketNumber() { return ticketNumber; } + public void setTicketNumber(String ticketNumber) { + this.ticketNumber = ticketNumber; + } + public BigDecimal getTicketPrice() { return ticketPrice; } @@ -41,9 +45,5 @@ public BigDecimal getTicketPrice() { public void setTicketPrice(BigDecimal ticketPrice) { this.ticketPrice = ticketPrice; } - - public void setTicketNumber(String ticketNumber) { - this.ticketNumber = ticketNumber; - } } From c3278ec52f17625e75156d94c309e6d5d88b3bfa Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Wed, 21 Feb 2024 21:09:42 +0700 Subject: [PATCH 46/54] Test(UserController) : implement test case for UserController --- .../bootcamp/lottery/UserControllerTest.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 posttest/src/test/java/com/kbtg/bootcamp/lottery/UserControllerTest.java diff --git a/posttest/src/test/java/com/kbtg/bootcamp/lottery/UserControllerTest.java b/posttest/src/test/java/com/kbtg/bootcamp/lottery/UserControllerTest.java new file mode 100644 index 00000000..f6852112 --- /dev/null +++ b/posttest/src/test/java/com/kbtg/bootcamp/lottery/UserControllerTest.java @@ -0,0 +1,85 @@ +package com.kbtg.bootcamp.lottery; + + +import com.kbtg.bootcamp.lottery.controller.UserController; +import com.kbtg.bootcamp.lottery.response.LotteryPurchaseResponseDto; +import com.kbtg.bootcamp.lottery.response.LotteryResponseDto; +import com.kbtg.bootcamp.lottery.response.LotteryUserResponseDto; +import com.kbtg.bootcamp.lottery.service.LotteryService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import java.math.BigDecimal; +import java.util.List; + +import static org.hamcrest.core.Is.is; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(MockitoExtension.class) +class UserControllerTest { + + MockMvc mockMvc; + @Mock + LotteryService lotteryService; + + @BeforeEach + void setUp() { + UserController userController = new UserController(lotteryService); + mockMvc = MockMvcBuilders.standaloneSetup(userController) + .alwaysDo(print()) + .build(); + } + + @Test + @DisplayName("when user purchase lottery on POST: /api/wallets should return status 200 and body contain ticket id.") + void createLottery() throws Exception { + LotteryPurchaseResponseDto lottery = new LotteryPurchaseResponseDto(1); + when(lotteryService.purchaseLotteryTicket("2024022100", "123456") + ).thenReturn(lottery); + mockMvc.perform( + post("/users/2024022100/lotteries/123456") + ) + .andExpect(jsonPath("$.id", is("1"))) + .andExpect(status().isOk()); + } + + @Test + @DisplayName("when perform on GET: /{userId}/lotteries should return status 200 and body contain list of lotteries from userId.") + void viewLotteriesByUser() throws Exception { + List ticketNumbers = List.of("123456", "123457"); + BigDecimal totalPrice = new BigDecimal("160.0"); + Long totalTicket = 2L; + LotteryUserResponseDto lotteryUser = new LotteryUserResponseDto(ticketNumbers, totalPrice, totalTicket); + when(lotteryService.getUserLotteryTickets("2024022100")).thenReturn(lotteryUser); + mockMvc.perform( + get("/users/2024022100/lotteries") + ) + .andExpect(jsonPath("$.tickets", is(ticketNumbers))) // Correctly navigate to the first ticket + .andExpect(jsonPath("$.cost", is(160.0))) // Verify the cost + .andExpect(jsonPath("$.count", is(2))) // Verify the count + .andExpect(status().isOk()); + } + + @Test + @DisplayName("when perform on GET: /{userId}/lotteries/{ticketId} should return status 200 and body contain list of lotteries from userId.") + void deleteLottery() throws Exception { + LotteryResponseDto lottery = new LotteryResponseDto("123456"); + when(lotteryService.soldBackLotteryTicket("2024022100","1")).thenReturn(lottery); + + mockMvc.perform( + delete("/users/2024022100/lotteries/1") + ) + .andExpect(jsonPath("$.ticket", is("123456"))) + .andExpect(status().isOk()); + } +} \ No newline at end of file From d6027b4a9055e00bb6a05eb251421ca9d45720f3 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Thu, 22 Feb 2024 14:01:27 +0700 Subject: [PATCH 47/54] feat(docker-compose) : add docker compose test and run --- posttest/docker-compose.run.yml | 37 +++++++++++++++++++++++++++++++++ posttest/docker-compose.yml | 5 ++++- 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 posttest/docker-compose.run.yml diff --git a/posttest/docker-compose.run.yml b/posttest/docker-compose.run.yml new file mode 100644 index 00000000..56e501fb --- /dev/null +++ b/posttest/docker-compose.run.yml @@ -0,0 +1,37 @@ +#file: noinspection YAMLSchemaValidation +version: '3.8' + +networks: + lottery_application: +services: + postgres: + image: postgres:latest + restart: always + environment: + POSTGRES_DB: lottery + POSTGRES_USER: lottery + POSTGRES_PASSWORD: Plottery001!@# + volumes: + - ./database/init.sql:/docker-entrypoint-initdb.d/init.sql + ports: + - "5432:5432" + networks: + - lottery_application + lottery_app: + image: amazoncorretto:17.0.9-alpine3.18 + volumes: + - $PWD:/app + working_dir: /app + environment: + - DATABASE_URL=jdbc:postgresql://postgres:5432/lottery + - DATABASE_USERNAME=lottery + - DATABASE_PASSWORD=Plottery001!@# + depends_on: + - postgres + ports: + - "8888:8888" + networks: + - lottery_application + command: [ "./gradlew", "clean", "bootRun"] +volumes: + data: diff --git a/posttest/docker-compose.yml b/posttest/docker-compose.yml index b179e0d5..29219298 100644 --- a/posttest/docker-compose.yml +++ b/posttest/docker-compose.yml @@ -1,5 +1,7 @@ version: '3' +networks: + lottery_application: services: postgres: image: postgres:latest @@ -9,9 +11,10 @@ services: POSTGRES_USER: lottery POSTGRES_PASSWORD: Plottery001!@# volumes: - # - pgdata:/var/lib/postgresql/data - ./database/init.sql:/docker-entrypoint-initdb.d/init.sql ports: - "5432:5432" + networks: + - lottery_application volumes: pgdata: \ No newline at end of file From 6fe44614f70e04c2bb72a509dfea21647ef46b50 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Thu, 22 Feb 2024 14:03:42 +0700 Subject: [PATCH 48/54] Test(userController) : add method for test and change wrong description on purchaseLottery --- posttest/build.gradle | 18 +++++++++++++++++- .../lottery/request/LotteryRequestDto.java | 4 ++++ .../response/LotteryPurchaseResponseDto.java | 3 +++ .../src/main/resources/application.properties | 3 --- .../bootcamp/lottery/UserControllerTest.java | 6 ++++-- 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/posttest/build.gradle b/posttest/build.gradle index ff760156..661bb621 100644 --- a/posttest/build.gradle +++ b/posttest/build.gradle @@ -27,10 +27,26 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.postgresql:postgresql' - testImplementation 'org.springframework.boot:spring-boot-starter-test' } +configurations { + integrationTestCompile.extendsFrom testCompile + integrationTestRuntime.extendsFrom testRuntime + integrationTestImplementation.extendsFrom testImplementation +} + +tasks.register('integrationTest', Test) { + jvmArgs('-Djdk.attach.allowAttachSelf=true', '-XX:+StartAttachListener') + + description = 'Runs the integration tests.' + group = 'verification' + useJUnitPlatform() + filter { + include '**/*IntegrationTest.*' + } +} + tasks.named('test') { useJUnitPlatform() } diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/request/LotteryRequestDto.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/request/LotteryRequestDto.java index c6ce94ae..0fda47a8 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/request/LotteryRequestDto.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/request/LotteryRequestDto.java @@ -22,6 +22,10 @@ public class LotteryRequestDto { @JsonProperty("amount") private Integer ticketAmount; + public LotteryRequestDto(String ticketNumber, BigDecimal totalPrice, Integer amount) { + } + public LotteryRequestDto() { + } public Integer getTicketAmount() { return ticketAmount; } diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryPurchaseResponseDto.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryPurchaseResponseDto.java index c92464ec..7eaa794d 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryPurchaseResponseDto.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/response/LotteryPurchaseResponseDto.java @@ -12,4 +12,7 @@ public LotteryPurchaseResponseDto(Integer ticketId) { } + public String getTicketId () { + return this.ticketId; + } } diff --git a/posttest/src/main/resources/application.properties b/posttest/src/main/resources/application.properties index 37c1e46a..77e880ff 100644 --- a/posttest/src/main/resources/application.properties +++ b/posttest/src/main/resources/application.properties @@ -1,8 +1,5 @@ server.port=8888 # DataSource Configuration -#spring.datasource.url=jdbc:postgresql://localhost:5432/lottery -#spring.datasource.username=lottery -#spring.datasource.password=Plottery001!@# spring.datasource.url=${DATABASE_URL} spring.datasource.username=${DATABASE_USERNAME} spring.datasource.password=${DATABASE_PASSWORD} diff --git a/posttest/src/test/java/com/kbtg/bootcamp/lottery/UserControllerTest.java b/posttest/src/test/java/com/kbtg/bootcamp/lottery/UserControllerTest.java index f6852112..ba5bf0e7 100644 --- a/posttest/src/test/java/com/kbtg/bootcamp/lottery/UserControllerTest.java +++ b/posttest/src/test/java/com/kbtg/bootcamp/lottery/UserControllerTest.java @@ -12,6 +12,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @@ -25,6 +26,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +@SpringBootTest @ExtendWith(MockitoExtension.class) class UserControllerTest { @@ -41,8 +43,8 @@ void setUp() { } @Test - @DisplayName("when user purchase lottery on POST: /api/wallets should return status 200 and body contain ticket id.") - void createLottery() throws Exception { + @DisplayName("when user purchase lottery on POST: /users/:userId/lotteries/:ticketId should return status 200 and body contain ticket id.") + void purchaseLottery() throws Exception { LotteryPurchaseResponseDto lottery = new LotteryPurchaseResponseDto(1); when(lotteryService.purchaseLotteryTicket("2024022100", "123456") ).thenReturn(lottery); From 645045c17409a210eb56da4b632f8d0e2164fa7b Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Thu, 22 Feb 2024 14:27:49 +0700 Subject: [PATCH 49/54] Defect(response-code) : hotfix response code create 200 to 201 --- .../kbtg/bootcamp/lottery/controller/AdminController.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/AdminController.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/AdminController.java index f944b583..9feeaba3 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/AdminController.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/controller/AdminController.java @@ -4,6 +4,7 @@ import com.kbtg.bootcamp.lottery.response.LotteryResponseDto; import com.kbtg.bootcamp.lottery.service.LotteryService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; @@ -25,8 +26,9 @@ public AdminController(LotteryService lotteryService) { @PostMapping("/lotteries") @PreAuthorize("hasRole('ADMIN')") - public LotteryResponseDto createLottery(@Validated @RequestBody LotteryRequestDto lotteryDTO) throws Exception { - return lotteryService.createLottery(lotteryDTO); + public ResponseEntity createLottery(@Validated @RequestBody LotteryRequestDto lotteryDTO) throws Exception { + LotteryResponseDto createdLottery = lotteryService.createLottery(lotteryDTO); + return ResponseEntity.status(201).body(createdLottery); } } From 775568a921273408cb4897f2d7bb1c74c117e753 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Thu, 22 Feb 2024 14:47:38 +0700 Subject: [PATCH 50/54] Test(UserController) : combine Integration to UIS and add docker-compose.test.yml --- posttest/build.gradle | 3 --- posttest/docker-compose.test.yml | 37 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 posttest/docker-compose.test.yml diff --git a/posttest/build.gradle b/posttest/build.gradle index 661bb621..8e473a41 100644 --- a/posttest/build.gradle +++ b/posttest/build.gradle @@ -42,9 +42,6 @@ tasks.register('integrationTest', Test) { description = 'Runs the integration tests.' group = 'verification' useJUnitPlatform() - filter { - include '**/*IntegrationTest.*' - } } tasks.named('test') { diff --git a/posttest/docker-compose.test.yml b/posttest/docker-compose.test.yml new file mode 100644 index 00000000..4b3e8a6f --- /dev/null +++ b/posttest/docker-compose.test.yml @@ -0,0 +1,37 @@ +#file: noinspection YAMLSchemaValidation +version: '3.8' + +networks: + integration-test: +services: + postgres: + image: postgres:latest + restart: always + environment: + POSTGRES_DB: lottery + POSTGRES_USER: lottery + POSTGRES_PASSWORD: Plottery001!@# + volumes: + - ./database/init.sql:/docker-entrypoint-initdb.d/init.sql + ports: + - "5432:5432" + networks: + - integration-test + it_test: + image: amazoncorretto:17.0.9-alpine3.18 + volumes: + - $PWD:/app + working_dir: /app + depends_on: + - postgres + environment: + - DATABASE_URL=jdbc:postgresql://postgres:5432/lottery + - DATABASE_USERNAME=lottery + - DATABASE_PASSWORD=Plottery001!@# + ports: + - "8888:8888" + networks: + - integration-test + command: ["./gradlew", "clean", "integrationTest"] +volumes: + data: From f44f83e9345eab14d0bf5e9dddb00c5c57bda640 Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Thu, 22 Feb 2024 15:11:23 +0700 Subject: [PATCH 51/54] Change(Lottery) : delete useless annotation and add useful annotation --- .../kbtg/bootcamp/lottery/entity/Lottery.java | 5 +++++ .../service/impl/LotteryServiceImpl.java | 2 -- .../lottery/EnvironmentVariableTest.java | 20 ------------------- 3 files changed, 5 insertions(+), 22 deletions(-) delete mode 100644 posttest/src/test/java/com/kbtg/bootcamp/lottery/EnvironmentVariableTest.java diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java index 01aa4999..f3685708 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/entity/Lottery.java @@ -4,6 +4,7 @@ import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; import java.math.BigDecimal; import java.time.LocalDateTime; @@ -16,13 +17,17 @@ public class Lottery { @Id + @NotNull private String ticketNumber; @Column(name = "price") + @NotNull private BigDecimal ticketPrice; @Column(name = "amount_available") + @NotNull private Integer ticketAmount; @Column(name = "last_updated") + @NotNull private LocalDateTime lastUpdate; public Lottery() { diff --git a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java index 810b4af8..b754f229 100644 --- a/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java +++ b/posttest/src/main/java/com/kbtg/bootcamp/lottery/service/impl/LotteryServiceImpl.java @@ -37,7 +37,6 @@ public LotteryServiceImpl(LotteryRepository lotteryRepository, UserTicketReposit } @Override - @Transactional public LotteryResponseDto createLottery(LotteryRequestDto lotteryDTO) throws Exception { Optional optionalLottery = lotteryRepository.findById(lotteryDTO.getTicketNumber()); if (optionalLottery.isPresent()) { @@ -91,7 +90,6 @@ public LotteryResponseDto soldBackLotteryTicket(String userId, String ticketId) @Override - @Transactional public LotteryUserResponseDto getUserLotteryTickets(String userId) throws Exception { Users user = new Users(userId); List userTickets = userTicketRepository.findByUserId(user); diff --git a/posttest/src/test/java/com/kbtg/bootcamp/lottery/EnvironmentVariableTest.java b/posttest/src/test/java/com/kbtg/bootcamp/lottery/EnvironmentVariableTest.java deleted file mode 100644 index 38fe796d..00000000 --- a/posttest/src/test/java/com/kbtg/bootcamp/lottery/EnvironmentVariableTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.kbtg.bootcamp.lottery; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.test.context.SpringBootTest; -import static org.assertj.core.api.Assertions.assertThat; - -@SpringBootTest -public class EnvironmentVariableTest -{ - @Value("${spring.datasource.url}") - private String datasourceUrl; - - @Test - public void testDataSourceUrl() { - assertThat(datasourceUrl).isNotNull(); - System.out.println("DataSource URL: " + datasourceUrl); - // You can add more assertions here to validate the format or the content of the datasourceUrl - } -} From 4ada86899cf4cdedf8d6f49d88d6a1e8c116faaf Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Thu, 22 Feb 2024 17:30:28 +0700 Subject: [PATCH 52/54] Test(AdminController) : implement Test for AdminControllerTest --- posttest/build.gradle | 2 + .../bootcamp/lottery/AdminControllerTest.java | 64 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 posttest/src/test/java/com/kbtg/bootcamp/lottery/AdminControllerTest.java diff --git a/posttest/build.gradle b/posttest/build.gradle index 8e473a41..11344b31 100644 --- a/posttest/build.gradle +++ b/posttest/build.gradle @@ -27,7 +27,9 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.postgresql:postgresql' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.testcontainers:junit-jupiter' } configurations { diff --git a/posttest/src/test/java/com/kbtg/bootcamp/lottery/AdminControllerTest.java b/posttest/src/test/java/com/kbtg/bootcamp/lottery/AdminControllerTest.java new file mode 100644 index 00000000..4dcd3d38 --- /dev/null +++ b/posttest/src/test/java/com/kbtg/bootcamp/lottery/AdminControllerTest.java @@ -0,0 +1,64 @@ +package com.kbtg.bootcamp.lottery; + + +import com.kbtg.bootcamp.lottery.controller.AdminController; +import com.kbtg.bootcamp.lottery.request.LotteryRequestDto; +import com.kbtg.bootcamp.lottery.response.LotteryResponseDto; +import com.kbtg.bootcamp.lottery.service.LotteryService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import static org.hamcrest.core.Is.is; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@ExtendWith(MockitoExtension.class) +public class AdminControllerTest { + + MockMvc mockMvc; + @Mock + LotteryService lotteryService; + + @BeforeEach + void setUp() { + AdminController adminController = new AdminController(lotteryService); + mockMvc = MockMvcBuilders.standaloneSetup(adminController) + .alwaysDo(print()) + .build(); + } + + @Test + @DisplayName("when admin create lottery on POST: /admin/lotteries should return status 201 and body contain lottery number.") + void createLottery() throws Exception { + LotteryResponseDto responseDto = new LotteryResponseDto("123456"); + when(lotteryService.createLottery(any(LotteryRequestDto.class))).thenReturn(responseDto); + mockMvc.perform(post("/admin/lotteries") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content( + """ + { + "ticket": "123456", + "price": 80, + "amount": 20 + } + """ + )) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.ticket", is("123456"))); + + } + +} From fcd9eb73246177e64d726cf85e144ec46b3dd76b Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Thu, 22 Feb 2024 17:38:31 +0700 Subject: [PATCH 53/54] Test(LotteryController) : implement test for LotteryControllerTest --- .../lottery/LotteryControllerTest.java | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 posttest/src/test/java/com/kbtg/bootcamp/lottery/LotteryControllerTest.java diff --git a/posttest/src/test/java/com/kbtg/bootcamp/lottery/LotteryControllerTest.java b/posttest/src/test/java/com/kbtg/bootcamp/lottery/LotteryControllerTest.java new file mode 100644 index 00000000..9a10c713 --- /dev/null +++ b/posttest/src/test/java/com/kbtg/bootcamp/lottery/LotteryControllerTest.java @@ -0,0 +1,56 @@ +package com.kbtg.bootcamp.lottery; + + +import com.kbtg.bootcamp.lottery.controller.LotteryController; +import com.kbtg.bootcamp.lottery.response.LotteryResponseDto; +import com.kbtg.bootcamp.lottery.service.LotteryService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import java.util.List; + +import static org.hamcrest.core.Is.is; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@ExtendWith(MockitoExtension.class) +public class LotteryControllerTest { + + MockMvc mockMvc; + @Mock + LotteryService lotteryService; + + @BeforeEach + void setUp() { + LotteryController lotteryController = new LotteryController(lotteryService); + mockMvc = MockMvcBuilders.standaloneSetup(lotteryController) + .alwaysDo(print()) + .build(); + } + + @Test + @DisplayName("when perform on GET: /lotteries should return status 200 and body contain list of lotteries.") + void viewLotteries() throws Exception { + List ticketNumbers = List.of("123456", "123457"); + LotteryResponseDto Lotteries = new LotteryResponseDto(ticketNumbers); + when(lotteryService.getAllLotteryTickets()).thenReturn(Lotteries); + + mockMvc.perform( + get("/lotteries") + ) + .andExpect(jsonPath("$.tickets", is(ticketNumbers))) + .andExpect(status().isOk()); + } + +} From 2207c3f5716888ac6ab7072e7447e305acfbb60d Mon Sep 17 00:00:00 2001 From: Pantakarn Kowdokrak Date: Sat, 24 Feb 2024 09:59:48 +0700 Subject: [PATCH 54/54] Test(API) : add postman collection for newman test --- ...lotteryapplication.postman_collection.json | 300 ++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 posttest/postmancollection/lotteryapplication.postman_collection.json diff --git a/posttest/postmancollection/lotteryapplication.postman_collection.json b/posttest/postmancollection/lotteryapplication.postman_collection.json new file mode 100644 index 00000000..93d7053c --- /dev/null +++ b/posttest/postmancollection/lotteryapplication.postman_collection.json @@ -0,0 +1,300 @@ +{ + "info": { + "_postman_id": "8257d688-a006-4e7f-a987-be8d0573adbe", + "name": "REST API: JAVA TEST LOTTERY APPLICATION", + "description": "```\n## User Stories\n### Story: EXP01\n\t* As an admin, I want to add a new lottery ticket So that I can have a lottery store\n\t* ในฐานะผู้ดูแลระบบ ฉันต้องการเพิ่มใบลอตเตอรี่ เพื่อที่จะสร้างคลังเก็บลอตเตอรี่\n#### Technical Details: EXP01\n* POST /admin/lotteries\n* ต้องยืนยันสิทธิ์การเข้าใช้งานด้วย basic authentication (username = admin, password = password)\n* Request Body\n```json\n{\n\t\"ticket\": \"123456\",\n\t\"price\": 80,\n\t\"amount\": 1\n}\n```\n* Response Body\n```json\n{\n\t\"ticket\": \"123456\"\n}\n```\n\n\n### Story: EXP02\n\t* As a user, I want a list all lottery ticket So that I can pick what I want to buy\n\t* ในฐานะผู้ใช้ ฉันต้องการดูรายการลอตเตอรี่ทั้งหมด เพิื่อจะได้เลือกซื้อ\n#### Technical Details: EXP02\n* GET /lotteries\n* Response Body\n```json\n{\n\t\"tickets\": [\"000001\",\"000002\",\"123456\"]\n}\n```\n\n### Story: EXP03\n\t* As a user, I want to buy a lottery ticket So that I can get a change to win\n\t* ในฐานะผู้ใช้ ฉันต้องการซื้อลอตเตอรี่ เพื่อที่จะได้ลุ้นถูกหวย\n#### Technical Details: EXP03\n* POST /users/:userId/lotteries/:ticketId\n* userId และ ticketId เป็นค่าที่ผู้ใช้ป้อนเข้ามา\n* Response Body\n```json\n{\n\t\"id\": \"1\"\n}\n```\nโดย id มาจาก ID ของตาราง `user_ticket`\n\n### Story: EXP04\n\t* As a user, I want to list all my lottery ticket So that I can see which one I have already bought and it cost\n\t* ในฐานะผู้ใช้ ฉันต้องการดูรายการลอตเตอรี่ทั้งหมดที่เคยซื้อ\n#### Technical Details: EXP04\n```mermaid\nsequenceDiagram\n Client->>+API Server: call GET /users/:userId/lotteries\n API Server->>+Database: get all lottery ticket by user\n API Server->>+API Server: calculate total price\n API Server->>+API Server: count of total lottery ticket\n API Server-->>Client: return JSON body
tickets = list of ticket e.g. [\"000001\",\"000002\",\"123456\"]
count = number
cost = number\n```\n\n### Story: EXP05\n\t* As a user, I want to sell back my lottery ticket So that I can get my money back\n\t* ในฐานะผู้ใช้ ฉันต้องการขายคืนลอตเตอรี่เพื่อได้เงินคืน\n#### Technical Details: EXP05\n* DELETE /users/:userId/lotteries/:ticketId\n* userId และ ticketId เป็นค่าที่ผู้ใช้ป้อนเข้ามา\n* Response Body\n```json\n{\n\t\"ticket\": \"000001\",\n}\n```\n\n ```", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "16184483" + }, + "item": [ + { + "name": "Get all lotteries", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/lotteries", + "host": [ + "{{base_url}}" + ], + "path": [ + "lotteries" + ] + }, + "description": "This is a GET request and it is used to \"get\" data from an endpoint. There is no request body for a GET request, but you can use query parameters to help specify the resource you want data on (e.g., in this request, we have `id=1`).\n\nA successful GET response will have a `200 OK` status, and should include some kind of response body - for example, HTML web content or JSON data." + }, + "response": [] + }, + { + "name": "Create lottery", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Successful POST request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200, 201]);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "password", + "type": "string" + }, + { + "key": "username", + "value": "admin", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"ticket\": \"123457\",\n \"price\": 80.0,\n \"amount\": 5\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/admin/lotteries", + "host": [ + "{{base_url}}" + ], + "path": [ + "admin", + "lotteries" + ] + }, + "description": "This is a POST request, submitting data to an API via the request body. This request submits JSON data, and the data is reflected in the response.\n\nA successful POST request typically returns a `200 OK` or `201 Created` response code." + }, + "response": [] + }, + { + "name": "Create buy lottery by user", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Successful POST request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200, 201]);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "password", + "type": "string" + }, + { + "key": "username", + "value": "admin", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"ticket\": \"123457\",\n \"price\": 80.0,\n \"amount\": 5\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/admin/lotteries", + "host": [ + "{{base_url}}" + ], + "path": [ + "admin", + "lotteries" + ] + }, + "description": "This is a POST request, submitting data to an API via the request body. This request submits JSON data, and the data is reflected in the response.\n\nA successful POST request typically returns a `200 OK` or `201 Created` response code." + }, + "response": [] + }, + { + "name": "Get user summary lotteries", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Successful DELETE request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200, 202, 204]);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/users/2023022400/lotteries", + "host": [ + "{{base_url}}" + ], + "path": [ + "users", + "2023022400", + "lotteries" + ] + }, + "description": "This is a DELETE request, and it is used to delete data that was previously created via a POST request. You typically identify the entity being updated by including an identifier in the URL (eg. `id=1`).\n\nA successful DELETE request typically returns a `200 OK`, `202 Accepted`, or `204 No Content` response code." + }, + "response": [] + }, + { + "name": "Soldback lottery", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Successful PUT request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200, 201, 204]);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n\t\"name\": \"Add your name in the body\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/users/:userId/lotteries/:ticketId", + "host": [ + "{{base_url}}" + ], + "path": [ + "users", + ":userId", + "lotteries", + ":ticketId" + ], + "variable": [ + { + "key": "userId", + "value": "2023022400", + "description": "UserId" + }, + { + "key": "ticketId", + "value": "1", + "description": "Lottery Id" + } + ] + }, + "description": "This is a PUT request and it is used to overwrite an existing piece of data. For instance, after you create an entity with a POST request, you may want to modify that later. You can do that using a PUT request. You typically identify the entity being updated by including an identifier in the URL (eg. `id=1`).\n\nA successful PUT request typically returns a `200 OK`, `201 Created`, or `204 No Content` response code." + }, + "response": [] + } + ], + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "password", + "type": "string" + }, + { + "key": "username", + "value": "admin", + "type": "string" + } + ] + }, + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "variable": [ + { + "key": "id", + "value": "1" + }, + { + "key": "base_url", + "value": "localhost:8888" + } + ] +} \ No newline at end of file