Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion .github/workflows/cd-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ jobs:
--arg EUREKA_HOST "${{ vars.EUREKA_HOST }}" \
--arg EUREKA_PORT "${{ vars.EUREKA_PORT }}" \
--arg JWT_SECRET "${{ secrets.JWT_SECRET }}" \
--arg POSTGRES_HOST "${{ vars.POSTGRES_HOST }}" \
--arg POSTGRES_PORT "${{ vars.POSTGRES_PORT }}" \
--arg POSTGRES_DB "${{ vars.POSTGRES_DB }}" \
--arg POSTGRES_USER "${{ vars.POSTGRES_USER }}" \
--arg POSTGRES_PASSWORD "${{ secrets.POSTGRES_PASSWORD }}" \
--arg REDIS_HOST "${{ vars.REDIS_HOST }}" \
--arg REDIS_PORT "${{ vars.REDIS_PORT }}" \
--arg INTERNAL_AUTH_SECRET "${{ secrets.INTERNAL_AUTH_SECRET }}" \
'
.containerDefinitions |= map(
if .name == $CN then
Expand All @@ -81,10 +89,19 @@ jobs:
[
{"name":"SPRING_PROFILES_ACTIVE","value":$SPRING_PROFILES_ACTIVE},
{"name":"SERVER_PORT","value":$SERVER_PORT},
{"name":"TZ","value":"Asia/Seoul"},
{"name":"EUREKA_ENABLED","value":$EUREKA_ENABLED},
{"name":"EUREKA_HOST","value":$EUREKA_HOST},
{"name":"EUREKA_PORT","value":$EUREKA_PORT},
{"name":"JWT_SECRET","value":$JWT_SECRET}
{"name":"JWT_SECRET","value":$JWT_SECRET},
{"name":"POSTGRES_HOST","value":$POSTGRES_HOST},
{"name":"POSTGRES_PORT","value":$POSTGRES_PORT},
{"name":"POSTGRES_DB","value":$POSTGRES_DB},
{"name":"POSTGRES_USER","value":$POSTGRES_USER},
{"name":"POSTGRES_PASSWORD","value":$POSTGRES_PASSWORD},
{"name":"REDIS_HOST","value":$REDIS_HOST},
{"name":"REDIS_PORT","value":$REDIS_PORT},
{"name":"INTERNAL_AUTH_SECRET","value":$INTERNAL_AUTH_SECRET}
Comment on lines +99 to +104
] + (.environment // [])
| unique_by(.name)
)
Expand Down
50 changes: 28 additions & 22 deletions .github/workflows/ci-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,63 @@ name: CI-PROD

on:
pull_request:
branches: [ main, dev ] # main 브랜치에 pr 했을때 트리거
branches: [ main ]
push:
branches: [ main, dev ] # main 브랜치에 push 했을때 트리거

permissions:
contents: read # 내 코드를 읽을 수 있게(코드이동) 접근 권한을 준다.
id-token: write # OIDC 토큰을 발급할 수 있게 한다.
branches: [ main ]

jobs:
build:
test:
runs-on: ubuntu-latest
permissions:
contents: read

steps:
- name: Checkout # 내 코드 이동 깃허브 컴퓨터로
- name: Checkout
uses: actions/checkout@v4

- name: Set up JDK 17 # jdk 환경 설치
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"

- name: Set up Gradle => gradle 설치
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4

- name: Grant execute permission for gradlew # 실행 권한 설정
- name: Grant execute permission for gradlew
run: chmod +x ./gradlew

- name: Run tests # 테스트 실행
- name: Run tests
run: ./gradlew test --no-daemon -Dspring.profiles.active=test

- name: Generate image tag # pr이 아닌 push 일때 이미지 태그 생성
if: github.event_name == 'push' # 커밋 기반으로 어떤 코드 버전이 배포됐는지 정확히 추적 가능
run: echo "IMAGE_TAG=$(git rev-parse --short HEAD)" >> $GITHUB_ENV # 태그란? 이미지 버전 이름
build-and-push:
if: github.event_name == 'push'
needs: test
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Generate image tag
run: echo "IMAGE_TAG=$(git rev-parse --short HEAD)" >> $GITHUB_ENV

- name: Configure AWS credentials (OIDC) # OIDC 기반으로 역할 자격증명
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ vars.AWS_REGION }}

- name: Login to Amazon ECR # 위에서 자격증명하고 ecr 로그인
if: github.event_name == 'push'
- name: Login to Amazon ECR
uses: aws-actions/amazon-ecr-login@v2

- name: Build Docker image # 이미지 빌드해서
if: github.event_name == 'push'
- name: Build Docker image
run: |
docker build -t ${{ vars.ECR_REGISTRY }}/${{ vars.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }} .

- name: Push Docker image to ECR # 이미지 ecr로 업로드
if: github.event_name == 'push'
- name: Push Docker image to ECR
run: |
docker push ${{ vars.ECR_REGISTRY }}/${{ vars.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }}
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ RUN chown -R app:app /app
USER app
EXPOSE 19500

ENTRYPOINT ["java", "-jar", "/app/app.jar"]
ENTRYPOINT ["java", "-Duser.timezone=Asia/Seoul", "-jar", "/app/app.jar"]
Original file line number Diff line number Diff line change
Expand Up @@ -113,18 +113,9 @@ public void complete(LocalDateTime now) {
if (this.status == ReservationStatus.COMPLETED) {
return; // 이미 완료된 경우 멱등 처리
}
if (state.status() != ReservationStatus.CONFIRMED) {
throw new BusinessException(ReservationErrorCode.INVALID_STATUS_TRANSITION);
}
LocalDateTime windowStart = noshowDeadline.minusMinutes(60); // noshowDeadline - 60min (= slotStart - 30min)
if (now.isBefore(windowStart)) {
throw new BusinessException(ReservationErrorCode.CHECK_IN_TOO_EARLY);
}
if (now.isAfter(noshowDeadline)) {
throw new BusinessException(ReservationErrorCode.CHECK_IN_TOO_LATE);
}
this.status = state.complete();
this.state = ReservationStateFactory.from(this.status);
state.assertCompletable(now, noshowDeadline);
this.status = state.complete();
this.state = ReservationStateFactory.from(this.status);
this.checkedInAt = now;
}

Expand All @@ -138,13 +129,11 @@ public boolean requiresSlotReturn() {
}

public boolean isCancellable() {
return state.status() == ReservationStatus.CONFIRMED
&& !LocalDate.now().isAfter(cancelDeadline);
return state.isCancellable(cancelDeadline);
}

public boolean isModifiable() {
return state.status() == ReservationStatus.CONFIRMED
&& !LocalDate.now().isAfter(modifyDeadline);
return state.isModifiable(modifyDeadline);
}

public boolean requiresRefund() {
Expand All @@ -157,18 +146,13 @@ public void modify(
GuestCount newGuestCount,
LocalDateTime newNoshowDeadline
) {
if (state.status() != ReservationStatus.CONFIRMED) {
throw new BusinessException(ReservationErrorCode.INVALID_STATUS_TRANSITION);
}
if (LocalDate.now().isAfter(modifyDeadline)) {
throw new BusinessException(ReservationErrorCode.MODIFY_DEADLINE_EXCEEDED);
}
this.timeSlotId = newTimeSlotId;
this.reservedDate = newReservedDate;
this.guestCount = newGuestCount;
this.cancelDeadline = newReservedDate.minusDays(2);
this.modifyDeadline = newReservedDate.minusDays(2);
this.noshowDeadline = newNoshowDeadline;
state.assertModifiable(modifyDeadline);
this.timeSlotId = newTimeSlotId;
this.reservedDate = newReservedDate;
this.guestCount = newGuestCount;
this.cancelDeadline = newReservedDate.minusDays(2);
this.modifyDeadline = newReservedDate.minusDays(2);
this.noshowDeadline = newNoshowDeadline;
}

private static void validateCreateInput(
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.michelet.reservation.domain.enums.ReservationStatus;
import com.michelet.reservation.domain.exception.ReservationErrorCode;
import java.time.LocalDate;
import java.time.LocalDateTime;

public class ConfirmedState implements ReservationState {

Expand All @@ -25,6 +26,34 @@ public ReservationStatus markNoShow() {
return ReservationStatus.NO_SHOW;
}

@Override
public boolean isCancellable(LocalDate cancelDeadline) {
return !LocalDate.now().isAfter(cancelDeadline);
}

@Override
public boolean isModifiable(LocalDate modifyDeadline) {
return !LocalDate.now().isAfter(modifyDeadline);
}

@Override
public void assertModifiable(LocalDate modifyDeadline) {
if (LocalDate.now().isAfter(modifyDeadline)) {
throw new BusinessException(ReservationErrorCode.MODIFY_DEADLINE_EXCEEDED);
}
}

@Override
public void assertCompletable(LocalDateTime now, LocalDateTime noshowDeadline) {
LocalDateTime windowStart = noshowDeadline.minusMinutes(60);
if (now.isBefore(windowStart)) {
throw new BusinessException(ReservationErrorCode.CHECK_IN_TOO_EARLY);
}
if (now.isAfter(noshowDeadline)) {
throw new BusinessException(ReservationErrorCode.CHECK_IN_TOO_LATE);
}
}

@Override
public boolean requiresSlotReturn() { return true; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.michelet.reservation.domain.enums.ReservationStatus;
import com.michelet.reservation.domain.exception.ReservationErrorCode;
import java.time.LocalDate;
import java.time.LocalDateTime;

public interface ReservationState {

Expand All @@ -28,6 +29,23 @@ default ReservationStatus markNoShow() {
throw new BusinessException(ReservationErrorCode.INVALID_STATUS_TRANSITION);
}

// 조건 판단 메서드 — CONFIRMED 외 상태는 false / 예외
default boolean isCancellable(LocalDate cancelDeadline) {
return false;
}

default boolean isModifiable(LocalDate modifyDeadline) {
return false;
}

default void assertModifiable(LocalDate modifyDeadline) {
throw new BusinessException(ReservationErrorCode.INVALID_STATUS_TRANSITION);
}

default void assertCompletable(LocalDateTime now, LocalDateTime noshowDeadline) {
throw new BusinessException(ReservationErrorCode.INVALID_STATUS_TRANSITION);
}

// 정책 메서드 — 상태별 구현 필수
boolean requiresSlotReturn();
boolean requiresRefund();
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ spring:
application:
name: reservation-service

jackson:
time-zone: Asia/Seoul

Comment on lines +17 to +19
cache:
type: redis

Expand Down
Loading
Loading