Compare commits

...

16 Commits

Author SHA1 Message Date
dongHyo
15f93eec52 refactor: import 정리 및 하단 공백 추가 2022-05-07 01:29:15 +09:00
dongHyo
60ff034171 refactor: @Transactional 추가 2022-05-07 01:22:40 +09:00
dongHyo
c23714f404 feat: user insert db 연결 테스트 2022-05-07 00:46:05 +09:00
dongHyo
1bc78fa605 feat: @EnableJpaAuditing 추가
Jpa Auditing 활성화
2022-05-07 00:45:36 +09:00
dongHyo
1dca969907 feat: log4j2 설정 2022-05-07 00:32:28 +09:00
dongHyo
b92f6e7a6f refactor: intellij warring 해결 2022-05-06 13:52:59 +09:00
dongHyo
aa666bd847 test: health check test 작성 2022-05-06 13:49:06 +09:00
dongHyo
08ce8e6904 refactor: @Component 등록 2022-05-06 13:48:46 +09:00
dongHyo
3cbb3f0ee8 feat: 헬스 상태 수동 변경 컨트롤러 구현 2022-05-06 13:34:12 +09:00
dongHyo
58900e2331 refactor: security 주석 및 actuator 추가
security 사용 시 주석 해제 (아직 사용하지 않아 주석처리)
actuator 을 통해 health check 구현
2022-05-06 13:33:30 +09:00
dongHyo
358bbadf32 feat: Movie 패키지 구조 및 파일 세팅 2022-05-05 03:11:01 +09:00
dongHyo
c816994d8c feat: Payment 패키지 구조 및 파일 세팅 2022-05-05 03:10:49 +09:00
dongHyo
0a54967bbf feat: User 패키지 구조 및 파일 세팅 2022-05-05 03:10:27 +09:00
dongHyo
7e2ef8c876 feat: BaseEntity add 2022-05-05 03:09:57 +09:00
dongHyo
a310d2c059 refactor: data-source-properties 제거 2022-05-05 02:10:16 +09:00
dongHyo
cdfea7f6ac feat: jasypt 적용 2022-05-05 02:09:47 +09:00
51 changed files with 726 additions and 15 deletions

View File

@@ -23,13 +23,23 @@ repositories {
mavenCentral()
}
dependencies {
implementation ("org.springframework.boot:spring-boot-starter-data-jpa")
implementation ("org.springframework.boot:spring-boot-starter-security")
// implementation ("org.springframework.boot:spring-boot-starter-security")
implementation ("org.springframework.boot:spring-boot-starter-validation")
implementation ("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation ("com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.4")
implementation ("org.springframework.boot:spring-boot-starter-log4j2")
implementation ("com.lmax:disruptor:3.4.2")
modules {
module("org.springframework.boot:spring-boot-starter-logging") {
replacedBy("org.springframework.boot:spring-boot-starter-log4j2", "Use Log4j2 instead of Logback")
}
}
compileOnly ("org.projectlombok:lombok")
runtimeOnly ("mysql:mysql-connector-java")

View File

@@ -2,7 +2,9 @@ package com.ticketing.server;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
@EnableJpaAuditing
@SpringBootApplication
public class ServerApplication {

View File

@@ -0,0 +1,31 @@
package com.ticketing.server.global.dto.repository;
import java.time.LocalDateTime;
import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@Getter
public abstract class AbstractEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, updatable = false)
@CreatedDate
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
}

View File

@@ -0,0 +1,43 @@
package com.ticketing.server.global.health;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/l7check")
@RequiredArgsConstructor
public class L7checkController {
private final MutableHealthIndicator indicator;
@GetMapping
public ResponseEntity<Object> health() {
Health health = indicator.health();
boolean isUp = health.getStatus().equals(Status.UP);
return ResponseEntity
.status(isUp ? HttpStatus.OK : HttpStatus.SERVICE_UNAVAILABLE)
.build();
}
@DeleteMapping
@ResponseStatus(HttpStatus.NO_CONTENT)
public void down() {
indicator.setHealth(Health.down().build());
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public void up() {
indicator.setHealth(Health.up().build());
}
}

View File

@@ -0,0 +1,22 @@
package com.ticketing.server.global.health;
import java.util.concurrent.atomic.AtomicReference;
import org.springframework.boot.actuate.health.Health;
import org.springframework.stereotype.Component;
@Component
public class ManualHealthIndicator implements MutableHealthIndicator {
private final AtomicReference<Health> healthRef = new AtomicReference<>(Health.up().build());
@Override
public void setHealth(Health health) {
healthRef.set(health);
}
@Override
public Health health() {
return healthRef.get();
}
}

View File

@@ -0,0 +1,10 @@
package com.ticketing.server.global.health;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
public interface MutableHealthIndicator extends HealthIndicator {
void setHealth(Health health);
}

View File

@@ -0,0 +1,8 @@
package com.ticketing.server.movie.application;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MovieController {
}

View File

@@ -0,0 +1,8 @@
package com.ticketing.server.movie.application;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MovieTimesController {
}

View File

@@ -0,0 +1,8 @@
package com.ticketing.server.movie.application;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SeatController {
}

View File

@@ -0,0 +1,8 @@
package com.ticketing.server.movie.application;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TheaterController {
}

View File

@@ -0,0 +1,8 @@
package com.ticketing.server.movie.application;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TicketController {
}

View File

@@ -0,0 +1,18 @@
package com.ticketing.server.movie.domain;
import com.ticketing.server.global.dto.repository.AbstractEntity;
import javax.persistence.Entity;
import javax.validation.constraints.NotNull;
import lombok.Getter;
@Entity
@Getter
public class Movie extends AbstractEntity {
@NotNull
private String title;
@NotNull
private Integer runningTime;
}

View File

@@ -0,0 +1,38 @@
package com.ticketing.server.movie.domain;
import com.ticketing.server.global.dto.repository.AbstractEntity;
import java.time.LocalDate;
import java.time.LocalDateTime;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.validation.constraints.NotNull;
import lombok.Getter;
@Entity
@Getter
public class MovieTimes extends AbstractEntity {
@NotNull
@ManyToOne
@JoinColumn(name = "movie_id", referencedColumnName = "id", updatable = false)
private Movie movie;
@NotNull
@ManyToOne
@JoinColumn(name = "theater_id", referencedColumnName = "id", updatable = false)
private Theater theater;
@NotNull
private LocalDate runningDate;
@NotNull
private Integer round;
@NotNull
private LocalDateTime startAt;
@NotNull
private LocalDateTime endAt;
}

View File

@@ -0,0 +1,25 @@
package com.ticketing.server.movie.domain;
import com.ticketing.server.global.dto.repository.AbstractEntity;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.validation.constraints.NotNull;
import lombok.Getter;
@Entity
@Getter
public class Seat extends AbstractEntity {
@NotNull
@ManyToOne
@JoinColumn(name = "theater_id", referencedColumnName = "id", updatable = false)
private Theater theater;
@NotNull
private Integer seatColumn;
@NotNull
private Integer seatRow;
}

View File

@@ -0,0 +1,17 @@
package com.ticketing.server.movie.domain;
import com.ticketing.server.global.dto.repository.AbstractEntity;
import javax.persistence.Entity;
import javax.validation.constraints.NotNull;
import lombok.Getter;
@Entity
@Getter
public class Theater extends AbstractEntity {
@NotNull
private Integer theaterNumber;
private Integer seatCount;
}

View File

@@ -0,0 +1,35 @@
package com.ticketing.server.movie.domain;
import com.ticketing.server.global.dto.repository.AbstractEntity;
import com.ticketing.server.payment.domain.Payment;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.validation.constraints.NotNull;
import lombok.Getter;
@Entity
@Getter
public class Ticket extends AbstractEntity {
@NotNull
@ManyToOne
@JoinColumn(name = "seat_id", referencedColumnName = "id", updatable = false)
private Seat seat;
@NotNull
@ManyToOne
@JoinColumn(name = "movie_times_id", referencedColumnName = "id", updatable = false)
private MovieTimes movieTimes;
@ManyToOne
@JoinColumn(name = "payment_id", referencedColumnName = "id", updatable = false)
private Payment payment;
@NotNull
private TicketStatus status;
@NotNull
private Integer ticketPrice;
}

View File

@@ -0,0 +1,15 @@
package com.ticketing.server.movie.domain;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum TicketStatus {
SALE("판매가능"),
SCHEDULED("환불"),
SOLD("판매완료");
private String name;
}

View File

@@ -0,0 +1,10 @@
package com.ticketing.server.movie.domain.repository;
import com.ticketing.server.movie.domain.Movie;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface MovieRepository extends JpaRepository<Movie, Long> {
}

View File

@@ -0,0 +1,10 @@
package com.ticketing.server.movie.domain.repository;
import com.ticketing.server.movie.domain.MovieTimes;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface MovieTimesRepository extends JpaRepository<MovieTimes, Long> {
}

View File

@@ -0,0 +1,10 @@
package com.ticketing.server.movie.domain.repository;
import com.ticketing.server.movie.domain.Seat;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface SeatRepository extends JpaRepository<Seat, Long> {
}

View File

@@ -0,0 +1,10 @@
package com.ticketing.server.movie.domain.repository;
import com.ticketing.server.movie.domain.Theater;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface TheaterRepository extends JpaRepository<Theater, Long> {
}

View File

@@ -0,0 +1,10 @@
package com.ticketing.server.movie.domain.repository;
import com.ticketing.server.movie.domain.Ticket;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface TicketRepository extends JpaRepository<Ticket, Long> {
}

View File

@@ -0,0 +1,9 @@
package com.ticketing.server.movie.service;
import com.ticketing.server.movie.service.interfaces.MovieService;
import org.springframework.stereotype.Service;
@Service
public class MovieServiceImpl implements MovieService {
}

View File

@@ -0,0 +1,9 @@
package com.ticketing.server.movie.service;
import com.ticketing.server.movie.service.interfaces.MovieTimesService;
import org.springframework.stereotype.Service;
@Service
public class MovieTimesServiceImpl implements MovieTimesService {
}

View File

@@ -0,0 +1,9 @@
package com.ticketing.server.movie.service;
import com.ticketing.server.movie.service.interfaces.SeatService;
import org.springframework.stereotype.Service;
@Service
public class SeatServiceImpl implements SeatService {
}

View File

@@ -0,0 +1,9 @@
package com.ticketing.server.movie.service;
import com.ticketing.server.movie.service.interfaces.TheaterService;
import org.springframework.stereotype.Service;
@Service
public class TheaterServiceImpl implements TheaterService {
}

View File

@@ -0,0 +1,9 @@
package com.ticketing.server.movie.service;
import com.ticketing.server.movie.service.interfaces.TicketService;
import org.springframework.stereotype.Service;
@Service
public class TicketServiceImpl implements TicketService {
}

View File

@@ -0,0 +1,5 @@
package com.ticketing.server.movie.service.interfaces;
public interface MovieService {
}

View File

@@ -0,0 +1,5 @@
package com.ticketing.server.movie.service.interfaces;
public interface MovieTimesService {
}

View File

@@ -0,0 +1,5 @@
package com.ticketing.server.movie.service.interfaces;
public interface SeatService {
}

View File

@@ -0,0 +1,5 @@
package com.ticketing.server.movie.service.interfaces;
public interface TheaterService {
}

View File

@@ -0,0 +1,5 @@
package com.ticketing.server.movie.service.interfaces;
public interface TicketService {
}

View File

@@ -0,0 +1,8 @@
package com.ticketing.server.payment.application;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class PaymentController {
}

View File

@@ -0,0 +1,38 @@
package com.ticketing.server.payment.domain;
import com.ticketing.server.global.dto.repository.AbstractEntity;
import com.ticketing.server.user.domain.User;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.validation.constraints.NotNull;
import lombok.Getter;
@Entity
@Getter
public class Payment extends AbstractEntity {
@NotNull
@ManyToOne
@JoinColumn(name = "user_id", referencedColumnName = "id", updatable = false)
private User user;
@NotNull
@Enumerated(value = EnumType.STRING)
private PaymentType type;
@NotNull
@Enumerated(value = EnumType.STRING)
private PaymentStatus status;
private String failedMessage;
@NotNull
private String paymentNumber;
@NotNull
private Integer totalPrice;
}

View File

@@ -0,0 +1,5 @@
package com.ticketing.server.payment.domain;
public enum PaymentStatus {
COMPLETED, REFUNDED, FAILED
}

View File

@@ -0,0 +1,5 @@
package com.ticketing.server.payment.domain;
public enum PaymentType {
KAKAO_PAY
}

View File

@@ -0,0 +1,10 @@
package com.ticketing.server.payment.domain.repository;
import com.ticketing.server.payment.domain.Payment;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface PaymentRepository extends JpaRepository<Payment, Long> {
}

View File

@@ -0,0 +1,9 @@
package com.ticketing.server.payment.service;
import com.ticketing.server.payment.service.interfaces.PaymentService;
import org.springframework.stereotype.Service;
@Service
public class PaymentServiceImpl implements PaymentService {
}

View File

@@ -0,0 +1,5 @@
package com.ticketing.server.payment.service.interfaces;
public interface PaymentService {
}

View File

@@ -0,0 +1,8 @@
package com.ticketing.server.user.application;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
}

View File

@@ -0,0 +1,47 @@
package com.ticketing.server.user.domain;
import com.ticketing.server.global.dto.repository.AbstractEntity;
import java.time.LocalDateTime;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.validation.constraints.NotNull;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Entity
@Getter
@NoArgsConstructor
public class User extends AbstractEntity {
@NotNull
private String name;
@NotNull
private String email;
@NotNull
private String password;
@NotNull
@Enumerated(value = EnumType.STRING)
private UserGrade grade;
@NotNull
private String phone;
private boolean isDeleted = false;
private LocalDateTime deletedAt;
@Builder
protected User(String name, String email, String password, UserGrade grade, String phone) {
this.name = name;
this.email = email;
this.password = password;
this.grade = grade;
this.phone = phone;
}
}

View File

@@ -0,0 +1,5 @@
package com.ticketing.server.user.domain;
public enum UserGrade {
GUEST, STAFF
}

View File

@@ -0,0 +1,10 @@
package com.ticketing.server.user.domain.repository;
import com.ticketing.server.user.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}

View File

@@ -0,0 +1,9 @@
package com.ticketing.server.user.service;
import com.ticketing.server.user.service.interfaces.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
}

View File

@@ -0,0 +1,5 @@
package com.ticketing.server.user.service.interfaces;
public interface UserService {
}

View File

@@ -14,17 +14,6 @@ spring:
connection-timeout: 30000 # default 30s
maximum-pool-size: 10 # default 10
max-lifetime: 1800000 # default 30 minutes
data-source-properties:
cachePrepStmts: true
prepStmtCacheSize: 250
prepStmtCacheSqlLimit: 2048
useServerPrepStmts: true
useLocalSessionState: true
rewriteBatchedStatements: true
cacheResultSetMetadata: true
cacheServerConfiguration: true
elideSetAutoCommits: true
maintainTimeStats: false
jasypt:
encryptor:

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Configuration status="INFO" monitorInterval="30">
<Properties>
<Property name="LOG_PATH">./logs/log</Property>
<Property name="LOG_PATTERN">%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Property>
</Properties>
<Appenders>
<Console name="ConsoleLog" target="SYSTEM_OUT">
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
</Console>
<RollingFile name="FileLog"
fileName="${LOG_PATH}.log"
filePattern="${LOG_PATH}-%d{yyyy-MM-dd}-%i.log">
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8" />
<Policies>
<SizeBasedTriggeringPolicy size="15MB" />
<TimeBasedTriggeringPolicy modulate="true" interval="1" />
</Policies>
<DefaultRolloverStrategy>
<Delete basePath="${LOG_PATH}" maxDepth="1">
<IfLastModified age="30d"/>
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="ConsoleLog" />
<AppenderRef ref="FileLog" />
</Root>
</Loggers>
</Configuration>

View File

@@ -2,8 +2,10 @@ package com.ticketing.server;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
@SpringBootTest
@EnableJpaAuditing
class ServerApplicationTests {
@Test

View File

@@ -0,0 +1,51 @@
package com.ticketing.server.global.health;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class L7checkControllerTest {
private static final String L7CHECK = "/l7check";
private static final String HEALTH = "/actuator/health";
@Autowired
TestRestTemplate restTemplate;
@Test
void downAndUp() throws InterruptedException {
// before
expectUrlStatus(L7CHECK, HttpStatus.OK);
expectUrlStatus(HEALTH, HttpStatus.OK);
// down
restTemplate.delete(L7CHECK);
// then down
TimeUnit.MILLISECONDS.sleep(1000);
expectUrlStatus(L7CHECK, HttpStatus.SERVICE_UNAVAILABLE);
expectUrlStatus(HEALTH, HttpStatus.SERVICE_UNAVAILABLE);
// up
restTemplate.postForEntity(L7CHECK, null, Object.class);
// then up
TimeUnit.MILLISECONDS.sleep(1000);
expectUrlStatus(L7CHECK, HttpStatus.OK);
expectUrlStatus(HEALTH, HttpStatus.OK);
}
private void expectUrlStatus(String url, HttpStatus status) {
ResponseEntity<Object> res = restTemplate.getForEntity(url, Object.class);
assertThat(res.getStatusCode()).isEqualTo(status);
}
}

View File

@@ -0,0 +1,37 @@
package com.ticketing.server.user.domain.repository;
import static org.assertj.core.api.Assertions.assertThat;
import com.ticketing.server.user.domain.User;
import com.ticketing.server.user.domain.UserGrade;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
@SpringBootTest
@Transactional
class UserRepositoryTest {
@Autowired
UserRepository userRepository;
@Test
void 유저레포지토리테스트() {
// given
User user = User.builder()
.name("동효")
.password("test")
.email("test@test.com")
.grade(UserGrade.GUEST)
.phone("010-1234-5678")
.build();
// when
userRepository.save(user);
// then
assertThat(user).isNotNull();
}
}

View File

@@ -1,8 +1,8 @@
spring:
datasource:
url: jdbc:mysql://localhost:3306/ticketing_test?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
username: ticketing
password: ticketing
username: ENC(LowN1n4w0Ep/DqLD8+q5Bq6AXM4b8e3V)
password: ENC(OMvGcpZLpggFTiGNkqNe66Zq/SmJXF6o)
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
@@ -12,3 +12,7 @@ spring:
format_sql: true
hibernate:
ddl-auto: create
jasypt:
encryptor:
bean: jasyptStringEncryptor