From 6de00c6812d2fb0b3d6d347f3baf3b8c57d6366a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9E=AC=ED=98=81?= Date: Wed, 27 Nov 2024 22:00:09 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20ElastiCache=20=EC=84=B8=ED=8C=85=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C=20(#165)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 장바구니 추가 기능 구현 * feat: 장바구니 조회 기능 구현 * feat: 상품 주문과 식권 사용 기능 구현 * style: 포인트트랜잭션 레포지토리명 변경 * deploy: 재배포 * deploy: 재배포2 * deploy: 로그인 로직 수정에 따른 재배포 * deploy: 로그인 로직 수정에 따른 재배포2 * feat: 식권 사용 기능 구현 * feat: yml 수정 사항 반영 * feat: 식권 사용 로직 수정 * feat: 주문 로직 수정 완료 * fix: 장바구니에 아무것도 없을 때 주문 시 active 되는 현상 수정 * feat: 주문 완료 시 리턴 값 수정 * feat: 내 그룹 조회 기능 구현 * feat: 기본 프로필 이미지 url 추가 * feat: 그룹 멤버 전체 조회 기능 구현 * feat: 1인당 사용 가능 금액 설정 컬럼 추가 * feat: 내 지갑 조회 기능 구현 * feat: 선결제 기능 구현 * feat: 유저 홈 화면 조회 기능 구현 * feat: 그룹 상세 조회 기능 구현 * feat: 가게 상세 조회 쿼리 수정 * deploy: 변경사항 반영을 위한 커밋 * deploy: 수정 사항 반영 재배포 * deploy: rds 교체로 인한 재배포 * deploy: rds 교체로 인한 재배포2 * feat: 홈 화면 소속팀 조회 쿼리 제대로 안되던 현상 해결, 대표이미지 반환 추가 * feat: 그룹 상세 조회 응답 컬럼명 변경 * feat: memberLimit 컬럼 삭제 * feat: 선결제 시 기존 결제 내역과 멱등성 문제 해결 * fix: 팀 상세 조회 시 이미지 쿼리가 의미 없이 join 되던 쿼리 수정 * feat: 가게 검색 기능 대폭 수정 * feat: 프론트 요구사 반영 * feat: searchcondition 삭제 * feat: 팀타입 desription 리턴 하도록 수정 * feat: 카테고리별 팀 조회 시 팀 생성일 컬럼 추가 * feat: 매장찾기_상세 페이지 조회 기능 구현 * feat: 비밀 코드 조회 기능 엔드 포인트 수정 * fix: conflict fix * hotfix: 서버 복구 * hotfix: 서버 복구2 * feat: Barobill api 연동 * fix: 홈 화면에 같은 데이터가 중복되는 현상 수정 * infra: ElastiCache 연결을 위한 docker-compose 설정 * infra: cicd script 수정 * infra: cicd script ec2 주소 설정 * infra: cicd script 수정2 * infra: cicd script 수정3 * infra: cicd script 수정4 * infra: cicd script 수정5 * infra: cicd script 수정6 * infra: cicd script 수정7 * infra: cicd script 수정7 * infra: cicd script 수정8 * infra: cicd script 수정9 * infra: cicd script 수정10 * infra: cicd script 수정11 * infra: cicd script 수정12 * infra: cicd script 수정13 * infra: cicd script 수정14 * infra: cicd script 수정15 * infra: cicd script 수정16 * infra: cicd script 수정17 * infra: cicd script 수정18 * infra: cicd script 수정19 * infra: cicd script 수정20 * infra: cicd script 수정21 * infra: cicd script 수정22 --- .github/workflows/deploy.yml | 52 ++++++++++++++++++++------ compose-prod.yml => docker-compose.yml | 8 ++-- 2 files changed, 43 insertions(+), 17 deletions(-) rename compose-prod.yml => docker-compose.yml (73%) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e76c3b0..603d67b 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -65,18 +65,46 @@ jobs: runs-on: self-hosted steps: - # 1. 최신 이미지를 풀받습니다 - - name: docker pull - run: sudo docker pull ${{ secrets.DOCKERHUB_USERNAME }}/jangburich2 + - uses: actions/checkout@v3 + + - name: Setup SSH key + uses: webfactory/ssh-agent@v0.5.4 + with: + ssh-private-key: ${{ secrets.EC2_SSH_KEY }} + + - name: Add EC2 to known_hosts + run: | + ssh-keyscan -H ip-172-31-3-107 >> ~/.ssh/known_hosts - # 2. 기존의 컨테이너를 중지시킵니다 - - name: docker stop container - run: sudo docker stop $(sudo docker ps -q) 2>/dev/null || true + - name: Copy docker-compose.yml to EC2 + run: | + scp docker-compose.yml ubuntu@ip-172-31-3-107:/home/ubuntu/ - # 3. 최신 이미지를 컨테이너화하여 실행시킵니다 - - name: docker run new container - run: sudo docker run --name jangburich2 --rm -d -p 8080:8080 ${{ secrets.DOCKERHUB_USERNAME }}/jangburich2 + - name: Login to Docker Hub on EC2 + run: | + ssh ubuntu@ip-172-31-3-107 ' + echo "${{ secrets.DOCKERHUB_PASSWORD }}" | sudo docker login -u "${{ secrets.DOCKERHUB_USERNAME }}" --password-stdin + ' - # 4. 미사용 이미지를 정리합니다 - - name: delete old docker image - run: sudo docker system prune -f \ No newline at end of file + - name: Stop existing Redis process + run: | + if pgrep redis-server; then + sudo systemctl stop redis || echo "Redis service not managed by systemctl" + pkill redis-server || echo "No Redis process to kill" + fi + + # 0. 최신 이미지를 풀받습니다 + - name: docker pull + run: sudo docker pull ${{ secrets.DOCKERHUB_USERNAME }}/jangburich2 + + # 1. docker-compose 종료 + - name: docker-compose down + run: sudo docker-compose down + + # 2. docker-compose 실행 + - name: docker-compose up + run: sudo docker-compose up -d + + # 3. 안쓰는 이미지 제거 + - name: Remove unused Docker images + run: sudo docker image prune -f diff --git a/compose-prod.yml b/docker-compose.yml similarity index 73% rename from compose-prod.yml rename to docker-compose.yml index aac2ff8..3adc64d 100644 --- a/compose-prod.yml +++ b/docker-compose.yml @@ -1,8 +1,6 @@ services: - api-server: - build: - context: . - dockerfile: ./Dockerfile + app: + image: rookie97/jangburich2:latest ports: - 8080:8080 depends_on: @@ -15,4 +13,4 @@ services: healthcheck: test: [ "CMD", "redis-cli", "ping" ] interval: 5s - retries: 10 \ No newline at end of file + retries: 10 From b6c9a32e926fcc78fbcf1bb34e469abb8e5dfcef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9E=AC=ED=98=81?= Date: Thu, 28 Nov 2024 01:25:57 +0900 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20=EB=B3=B4=EC=9C=A0=EA=B8=88=EC=95=A1?= =?UTF-8?q?=EC=9D=B4=20=EC=84=A0=EA=B2=B0=EC=A0=9C=20=EA=B8=88=EC=95=A1?= =?UTF-8?q?=EB=B3=B4=EB=8B=A4=20=EC=9E=91=EC=9D=84=20=EA=B2=BD=EC=9A=B0=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=EA=B0=92=20=EB=B3=80=EA=B2=BD(200->400)=20(#?= =?UTF-8?q?167)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 장바구니 추가 기능 구현 * feat: 장바구니 조회 기능 구현 * feat: 상품 주문과 식권 사용 기능 구현 * style: 포인트트랜잭션 레포지토리명 변경 * deploy: 재배포 * deploy: 재배포2 * deploy: 로그인 로직 수정에 따른 재배포 * deploy: 로그인 로직 수정에 따른 재배포2 * feat: 식권 사용 기능 구현 * feat: yml 수정 사항 반영 * feat: 식권 사용 로직 수정 * feat: 주문 로직 수정 완료 * fix: 장바구니에 아무것도 없을 때 주문 시 active 되는 현상 수정 * feat: 주문 완료 시 리턴 값 수정 * feat: 내 그룹 조회 기능 구현 * feat: 기본 프로필 이미지 url 추가 * feat: 그룹 멤버 전체 조회 기능 구현 * feat: 1인당 사용 가능 금액 설정 컬럼 추가 * feat: 내 지갑 조회 기능 구현 * feat: 선결제 기능 구현 * feat: 유저 홈 화면 조회 기능 구현 * feat: 그룹 상세 조회 기능 구현 * feat: 가게 상세 조회 쿼리 수정 * deploy: 변경사항 반영을 위한 커밋 * deploy: 수정 사항 반영 재배포 * deploy: rds 교체로 인한 재배포 * deploy: rds 교체로 인한 재배포2 * feat: 홈 화면 소속팀 조회 쿼리 제대로 안되던 현상 해결, 대표이미지 반환 추가 * feat: 그룹 상세 조회 응답 컬럼명 변경 * feat: memberLimit 컬럼 삭제 * feat: 선결제 시 기존 결제 내역과 멱등성 문제 해결 * fix: 팀 상세 조회 시 이미지 쿼리가 의미 없이 join 되던 쿼리 수정 * feat: 가게 검색 기능 대폭 수정 * feat: 프론트 요구사 반영 * feat: searchcondition 삭제 * feat: 팀타입 desription 리턴 하도록 수정 * feat: 카테고리별 팀 조회 시 팀 생성일 컬럼 추가 * feat: 매장찾기_상세 페이지 조회 기능 구현 * feat: 비밀 코드 조회 기능 엔드 포인트 수정 * fix: conflict fix * hotfix: 서버 복구 * hotfix: 서버 복구2 * feat: Barobill api 연동 * fix: 홈 화면에 같은 데이터가 중복되는 현상 수정 * infra: ElastiCache 연결을 위한 docker-compose 설정 * infra: cicd script 수정 * infra: cicd script ec2 주소 설정 * infra: cicd script 수정2 * infra: cicd script 수정3 * infra: cicd script 수정4 * infra: cicd script 수정5 * infra: cicd script 수정6 * infra: cicd script 수정7 * infra: cicd script 수정7 * infra: cicd script 수정8 * infra: cicd script 수정9 * infra: cicd script 수정10 * infra: cicd script 수정11 * infra: cicd script 수정12 * infra: cicd script 수정13 * infra: cicd script 수정14 * infra: cicd script 수정15 * infra: cicd script 수정16 * infra: cicd script 수정17 * infra: cicd script 수정18 * infra: cicd script 수정19 * infra: cicd script 수정20 * infra: cicd script 수정21 * infra: cicd script 수정22 * fix: 보유금액이 선결제 금액보다 작을 경우 상태값 변경(200->400) --- .../domain/store/service/PrepayService.java | 12 ++---------- .../java/com/jangburich/domain/team/domain/Team.java | 6 ++++++ .../java/com/jangburich/domain/user/domain/User.java | 6 ++++++ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/jangburich/domain/store/service/PrepayService.java b/src/main/java/com/jangburich/domain/store/service/PrepayService.java index c1fc839..d66958e 100644 --- a/src/main/java/com/jangburich/domain/store/service/PrepayService.java +++ b/src/main/java/com/jangburich/domain/store/service/PrepayService.java @@ -42,17 +42,9 @@ public Message prepay(String userId, PrepayRequest prepayRequest) { Store store = storeRepository.findById(prepayRequest.storeId()) .orElseThrow(() -> new IllegalArgumentException("유효하지 않은 가게 id 입니다.")); - if (!team.getTeamLeader().getUser_id().equals(user.getUserId())) { - return Message.builder() - .message("팀의 리더가 아닌 사람은 선결제를 할 수 없습니다.") - .build(); - } + team.validateIsTeamLeader(team.getTeamLeader().getUser_id(), user.getUserId()); - if (prepayRequest.prepayAmount() > user.getPoint()) { - return Message.builder() - .message("보유하고 있는 금액이 선결제 하려는 금액보다 적습니다.") - .build(); - } + user.validateHasPointWithPrepayAmount(prepayRequest.prepayAmount(), user.getPoint()); user.usePoint(prepayRequest.prepayAmount()); PointTransaction pointTransaction = PointTransaction diff --git a/src/main/java/com/jangburich/domain/team/domain/Team.java b/src/main/java/com/jangburich/domain/team/domain/Team.java index 369d13f..79ef3ca 100644 --- a/src/main/java/com/jangburich/domain/team/domain/Team.java +++ b/src/main/java/com/jangburich/domain/team/domain/Team.java @@ -66,4 +66,10 @@ public Team(String name, String description, String secretCode, TeamLeader teamL this.point = point; this.teamType = teamType; } + + public void validateIsTeamLeader(Long userId, Long userId1) { + if (!userId.equals(userId1)) { + throw new IllegalArgumentException("팀의 리더가 아닌 사람은 선결제를 할 수 없습니다."); + } + } } diff --git a/src/main/java/com/jangburich/domain/user/domain/User.java b/src/main/java/com/jangburich/domain/user/domain/User.java index b609ff1..e2e9c18 100644 --- a/src/main/java/com/jangburich/domain/user/domain/User.java +++ b/src/main/java/com/jangburich/domain/user/domain/User.java @@ -80,4 +80,10 @@ public static User create(String userId, String nickname, String email, String i newUser.setPoint(0); return newUser; } + + public void validateHasPointWithPrepayAmount(int prepayAmount, Integer point) { + if (prepayAmount > point) { + throw new IllegalArgumentException("보유하고 있는 금액이 선결제 하려는 금액보다 적습니다."); + } + } } From 0d0d7b4a3ed7ee25b33401cc7659625c9c5f513c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9E=AC=ED=98=81?= Date: Thu, 28 Nov 2024 02:07:06 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=ED=99=88=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EA=B0=80=EA=B2=8C=20=EB=B3=84=20=EC=84=A0=EA=B2=B0=EC=A0=9C=20?= =?UTF-8?q?=EB=94=94=EB=8D=B0=EC=9D=B4=20=EC=B6=94=EA=B0=80,=20=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=EB=AC=B8=20=EC=A1=B0=EC=A0=95=20(#169)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 장바구니 추가 기능 구현 * feat: 장바구니 조회 기능 구현 * feat: 상품 주문과 식권 사용 기능 구현 * style: 포인트트랜잭션 레포지토리명 변경 * deploy: 재배포 * deploy: 재배포2 * deploy: 로그인 로직 수정에 따른 재배포 * deploy: 로그인 로직 수정에 따른 재배포2 * feat: 식권 사용 기능 구현 * feat: yml 수정 사항 반영 * feat: 식권 사용 로직 수정 * feat: 주문 로직 수정 완료 * fix: 장바구니에 아무것도 없을 때 주문 시 active 되는 현상 수정 * feat: 주문 완료 시 리턴 값 수정 * feat: 내 그룹 조회 기능 구현 * feat: 기본 프로필 이미지 url 추가 * feat: 그룹 멤버 전체 조회 기능 구현 * feat: 1인당 사용 가능 금액 설정 컬럼 추가 * feat: 내 지갑 조회 기능 구현 * feat: 선결제 기능 구현 * feat: 유저 홈 화면 조회 기능 구현 * feat: 그룹 상세 조회 기능 구현 * feat: 가게 상세 조회 쿼리 수정 * deploy: 변경사항 반영을 위한 커밋 * deploy: 수정 사항 반영 재배포 * deploy: rds 교체로 인한 재배포 * deploy: rds 교체로 인한 재배포2 * feat: 홈 화면 소속팀 조회 쿼리 제대로 안되던 현상 해결, 대표이미지 반환 추가 * feat: 그룹 상세 조회 응답 컬럼명 변경 * feat: memberLimit 컬럼 삭제 * feat: 선결제 시 기존 결제 내역과 멱등성 문제 해결 * fix: 팀 상세 조회 시 이미지 쿼리가 의미 없이 join 되던 쿼리 수정 * feat: 가게 검색 기능 대폭 수정 * feat: 프론트 요구사 반영 * feat: searchcondition 삭제 * feat: 팀타입 desription 리턴 하도록 수정 * feat: 카테고리별 팀 조회 시 팀 생성일 컬럼 추가 * feat: 매장찾기_상세 페이지 조회 기능 구현 * feat: 비밀 코드 조회 기능 엔드 포인트 수정 * fix: conflict fix * hotfix: 서버 복구 * hotfix: 서버 복구2 * feat: Barobill api 연동 * fix: 홈 화면에 같은 데이터가 중복되는 현상 수정 * infra: ElastiCache 연결을 위한 docker-compose 설정 * infra: cicd script 수정 * infra: cicd script ec2 주소 설정 * infra: cicd script 수정2 * infra: cicd script 수정3 * infra: cicd script 수정4 * infra: cicd script 수정5 * infra: cicd script 수정6 * infra: cicd script 수정7 * infra: cicd script 수정7 * infra: cicd script 수정8 * infra: cicd script 수정9 * infra: cicd script 수정10 * infra: cicd script 수정11 * infra: cicd script 수정12 * infra: cicd script 수정13 * infra: cicd script 수정14 * infra: cicd script 수정15 * infra: cicd script 수정16 * infra: cicd script 수정17 * infra: cicd script 수정18 * infra: cicd script 수정19 * infra: cicd script 수정20 * infra: cicd script 수정21 * infra: cicd script 수정22 * fix: 보유금액이 선결제 금액보다 작을 경우 상태값 변경(200->400) * feat: 홈화면 가게 별 선결제 디데이 추가, 쿼리문 조정 --- .../jangburich/domain/store/domain/StoreTeam.java | 9 ++++++++- .../domain/store/service/PrepayService.java | 4 ++++ .../domain/user/dto/response/TeamsResponse.java | 5 ++++- .../user/repository/UserQueryDslRepositoryImpl.java | 12 +++++++----- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/jangburich/domain/store/domain/StoreTeam.java b/src/main/java/com/jangburich/domain/store/domain/StoreTeam.java index b546b88..e5d8f48 100644 --- a/src/main/java/com/jangburich/domain/store/domain/StoreTeam.java +++ b/src/main/java/com/jangburich/domain/store/domain/StoreTeam.java @@ -10,6 +10,7 @@ import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; +import java.time.LocalDate; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -44,6 +45,9 @@ public class StoreTeam extends BaseEntity { @Column(name = "remain_point") private Integer remainPoint; + @Column(name = "prepaid_expiration_date") + private LocalDate prepaidExpirationDate; + public void updatePersonalAllocatedPoint(Integer point) { this.personalAllocatedPoint = point; } @@ -71,13 +75,16 @@ public void subRemainPoint(Integer point) { this.remainPoint -= point; } + @Builder - public StoreTeam(Store store, Team team, Integer point, Integer personalAllocatedPoint, Integer remainPoint) { + public StoreTeam(Store store, Team team, Integer point, Integer personalAllocatedPoint, Integer remainPoint, + LocalDate prepaidExpirationDate) { this.store = store; this.team = team; this.point = point; this.personalAllocatedPoint = personalAllocatedPoint; this.remainPoint = remainPoint; + this.prepaidExpirationDate = prepaidExpirationDate; } public static StoreTeam create(Team team, Store store, Integer point) { diff --git a/src/main/java/com/jangburich/domain/store/service/PrepayService.java b/src/main/java/com/jangburich/domain/store/service/PrepayService.java index d66958e..87e7cbe 100644 --- a/src/main/java/com/jangburich/domain/store/service/PrepayService.java +++ b/src/main/java/com/jangburich/domain/store/service/PrepayService.java @@ -15,6 +15,7 @@ import com.jangburich.global.error.DefaultNullPointerException; import com.jangburich.global.payload.ErrorCode; import com.jangburich.global.payload.Message; +import java.time.LocalDate; import java.util.Optional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -57,6 +58,8 @@ public Message prepay(String userId, PrepayRequest prepayRequest) { pointTransactionRepository.save(pointTransaction); + LocalDate expirationDate = LocalDate.now().plusDays(store.getPrepaymentDuration()); + StoreTeam buildedStoreTeam = StoreTeam .builder() .team(team) @@ -64,6 +67,7 @@ public Message prepay(String userId, PrepayRequest prepayRequest) { .point(prepayRequest.prepayAmount()) .personalAllocatedPoint(prepayRequest.personalAllocatedAmount()) .remainPoint(prepayRequest.prepayAmount()) + .prepaidExpirationDate(expirationDate) .build(); Optional storeAndTeam = storeTeamRepository.findByStoreAndTeam(store, team); diff --git a/src/main/java/com/jangburich/domain/user/dto/response/TeamsResponse.java b/src/main/java/com/jangburich/domain/user/dto/response/TeamsResponse.java index 856f28c..5949054 100644 --- a/src/main/java/com/jangburich/domain/user/dto/response/TeamsResponse.java +++ b/src/main/java/com/jangburich/domain/user/dto/response/TeamsResponse.java @@ -5,6 +5,7 @@ public record TeamsResponse( Long teamId, Long storeId, + int dDay, String storeImgUrl, Boolean isLikedAtStore, String teamName, @@ -14,10 +15,12 @@ public record TeamsResponse( ) { @QueryProjection - public TeamsResponse(Long teamId, Long storeId, String storeImgUrl, Boolean isLikedAtStore, String teamName, + public TeamsResponse(Long teamId, Long storeId, int dDay, String storeImgUrl, Boolean isLikedAtStore, + String teamName, String storeName, int totalAmount, int currentAmount) { this.teamId = teamId; this.storeId = storeId; + this.dDay = dDay; this.storeImgUrl = storeImgUrl; this.isLikedAtStore = isLikedAtStore; this.teamName = teamName; diff --git a/src/main/java/com/jangburich/domain/user/repository/UserQueryDslRepositoryImpl.java b/src/main/java/com/jangburich/domain/user/repository/UserQueryDslRepositoryImpl.java index cdfe8cc..b76d8f9 100644 --- a/src/main/java/com/jangburich/domain/user/repository/UserQueryDslRepositoryImpl.java +++ b/src/main/java/com/jangburich/domain/user/repository/UserQueryDslRepositoryImpl.java @@ -33,8 +33,11 @@ public UserHomeResponse findUserHomeData(Long userId) { List teamsResponses = queryFactory .selectDistinct(new QTeamsResponse( - storeTeam.team.id, + userTeam.team.id, storeTeam.store.id, + Expressions.numberTemplate(Integer.class, + "DATEDIFF({0}, CURRENT_DATE)", + storeTeam.prepaidExpirationDate), storeTeam.store.representativeImage, Expressions.constant(false), storeTeam.team.name, @@ -42,15 +45,14 @@ public UserHomeResponse findUserHomeData(Long userId) { storeTeam.point, storeTeam.remainPoint )) - .from(storeTeam) - .leftJoin(team).on(team.id.eq(storeTeam.team.id)) - .leftJoin(userTeam).on(storeTeam.team.eq(team)) + .from(userTeam) + .leftJoin(team).on(team.id.eq(userTeam.team.id)) + .leftJoin(storeTeam).on(storeTeam.team.eq(team)) .where(storeTeam.status.eq(Status.ACTIVE), userTeam.user.userId.eq(userId)) .orderBy(storeTeam.createdAt.desc()) .fetch(); - UserHomeResponse userHomeResponse = queryFactory.select(new QUserHomeResponse( user.userId, Expressions.constant(formattedDate), From e28ee3e2d69b25e50d02b78fc28ce5bf9a61c113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9E=AC=ED=98=81?= Date: Thu, 28 Nov 2024 11:51:02 +0900 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=EC=9E=A5=EB=B0=94=EA=B5=AC?= =?UTF-8?q?=EB=8B=88=20=EB=B9=84=EC=9A=B0=EA=B8=B0=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#171)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 장바구니 추가 기능 구현 * feat: 장바구니 조회 기능 구현 * feat: 상품 주문과 식권 사용 기능 구현 * style: 포인트트랜잭션 레포지토리명 변경 * deploy: 재배포 * deploy: 재배포2 * deploy: 로그인 로직 수정에 따른 재배포 * deploy: 로그인 로직 수정에 따른 재배포2 * feat: 식권 사용 기능 구현 * feat: yml 수정 사항 반영 * feat: 식권 사용 로직 수정 * feat: 주문 로직 수정 완료 * fix: 장바구니에 아무것도 없을 때 주문 시 active 되는 현상 수정 * feat: 주문 완료 시 리턴 값 수정 * feat: 내 그룹 조회 기능 구현 * feat: 기본 프로필 이미지 url 추가 * feat: 그룹 멤버 전체 조회 기능 구현 * feat: 1인당 사용 가능 금액 설정 컬럼 추가 * feat: 내 지갑 조회 기능 구현 * feat: 선결제 기능 구현 * feat: 유저 홈 화면 조회 기능 구현 * feat: 그룹 상세 조회 기능 구현 * feat: 가게 상세 조회 쿼리 수정 * deploy: 변경사항 반영을 위한 커밋 * deploy: 수정 사항 반영 재배포 * deploy: rds 교체로 인한 재배포 * deploy: rds 교체로 인한 재배포2 * feat: 홈 화면 소속팀 조회 쿼리 제대로 안되던 현상 해결, 대표이미지 반환 추가 * feat: 그룹 상세 조회 응답 컬럼명 변경 * feat: memberLimit 컬럼 삭제 * feat: 선결제 시 기존 결제 내역과 멱등성 문제 해결 * fix: 팀 상세 조회 시 이미지 쿼리가 의미 없이 join 되던 쿼리 수정 * feat: 가게 검색 기능 대폭 수정 * feat: 프론트 요구사 반영 * feat: searchcondition 삭제 * feat: 팀타입 desription 리턴 하도록 수정 * feat: 카테고리별 팀 조회 시 팀 생성일 컬럼 추가 * feat: 매장찾기_상세 페이지 조회 기능 구현 * feat: 비밀 코드 조회 기능 엔드 포인트 수정 * fix: conflict fix * hotfix: 서버 복구 * hotfix: 서버 복구2 * feat: Barobill api 연동 * fix: 홈 화면에 같은 데이터가 중복되는 현상 수정 * infra: ElastiCache 연결을 위한 docker-compose 설정 * infra: cicd script 수정 * infra: cicd script ec2 주소 설정 * infra: cicd script 수정2 * infra: cicd script 수정3 * infra: cicd script 수정4 * infra: cicd script 수정5 * infra: cicd script 수정6 * infra: cicd script 수정7 * infra: cicd script 수정7 * infra: cicd script 수정8 * infra: cicd script 수정9 * infra: cicd script 수정10 * infra: cicd script 수정11 * infra: cicd script 수정12 * infra: cicd script 수정13 * infra: cicd script 수정14 * infra: cicd script 수정15 * infra: cicd script 수정16 * infra: cicd script 수정17 * infra: cicd script 수정18 * infra: cicd script 수정19 * infra: cicd script 수정20 * infra: cicd script 수정21 * infra: cicd script 수정22 * fix: 보유금액이 선결제 금액보다 작을 경우 상태값 변경(200->400) * feat: 홈화면 가게 별 선결제 디데이 추가, 쿼리문 조정 * feat: 주문하기 로직 정상화 * feat: 장바구니에 다른 가게 물건이 있는지 검증 구현 * feat: 장바구니 비우기 기능 구현 --- .../order/application/OrderService.java | 134 +++++++++--------- .../jangburich/domain/order/domain/Cart.java | 10 ++ .../domain/repository/CartRepository.java | 3 + .../OrdersQueryDslRepositoryImpl.java | 4 +- .../order/presentation/OrderController.java | 9 ++ 5 files changed, 92 insertions(+), 68 deletions(-) diff --git a/src/main/java/com/jangburich/domain/order/application/OrderService.java b/src/main/java/com/jangburich/domain/order/application/OrderService.java index dba3f3d..dd7ca52 100644 --- a/src/main/java/com/jangburich/domain/order/application/OrderService.java +++ b/src/main/java/com/jangburich/domain/order/application/OrderService.java @@ -60,6 +60,9 @@ public Message addCart(String userProviderId, AddCartRequest addCartRequest) { Menu menu = menuRepository.findById(addCartRequest.menuId()) .orElseThrow(() -> new IllegalArgumentException("등록된 메뉴를 찾을 수 없습니다. ")); + List allByUserAndStatus = cartRepository.findAllByUserAndStatus(user, Status.ACTIVE); + Cart.validateHasAnotherStoreItem(user, addCartRequest, allByUserAndStatus); + Optional optionalCart = cartRepository.findByUserIdAndMenuIdAndStatus(user.getUserId(), menu.getId(), Status.ACTIVE); @@ -126,16 +129,6 @@ public OrderResponse order(String userProviderId, OrderRequest orderRequest) { Team team = teamRepository.findById(orderRequest.teamId()) .orElseThrow(() -> new IllegalArgumentException("유효하지 않은 그룹 id 입니다.")); - List existingCarts = cartRepository.findAllByUserAndStoreAndStatus(user, store, Status.ACTIVE); - - List mergedCarts = mergeCarts(existingCarts, orderRequest.items(), user, store); - - Orders orders = saveOrder(user, store, team, orderRequest); - - associateCartsWithOrder(mergedCarts, orders); - - cartRepository.saveAll(mergedCarts); - List menuIds = orderRequest.items() .stream() .map(item -> item.menuId()) @@ -169,8 +162,6 @@ public OrderResponse order(String userProviderId, OrderRequest orderRequest) { .store(store) .build(); - System.out.println("totalAmount = " + totalAmount); - pointTransactionRepository.save(pointTransaction); StoreTeam storeTeam = storeTeamRepository.findByStoreIdAndTeamId(store.getId(), team.getId()) @@ -178,64 +169,63 @@ public OrderResponse order(String userProviderId, OrderRequest orderRequest) { storeTeam.useRemainPoint(totalAmount); - return ordersRepository.findTicket(orders.getId()); - } + Orders orders = saveOrder(user, store, team, orderRequest); - private List mergeCarts(List existingCarts, List items, User user, - Store store) { - if (existingCarts.isEmpty()) { - for (OrderRequest.OrderItemRequest item : items) { - Cart newCartAfterOrder = createNewCartAfterOrder(item, user, store); - existingCarts.add(newCartAfterOrder); - } - return existingCarts; - } + List allByUserAndStatus = cartRepository.findAllByUserAndStatus(user, Status.ACTIVE); + System.out.println("allByUserAndStatus = " + allByUserAndStatus); - for (OrderRequest.OrderItemRequest item : items) { - Optional existingCart = findCartByMenuId(existingCarts, item.menuId()); - if (existingCart.isPresent()) { - existingCart.get().updateQuantity(item.quantity()); - existingCart.get().updateStatus(Status.INACTIVE); - continue; - } - Cart newCart = createNewCart(item, user, store); - existingCarts.add(newCart); - } - return existingCarts; - } + syncCart(orderRequest, allByUserAndStatus, orders, user); + OrderResponse ticket = ordersRepository.findTicket(orders.getId()); - private Optional findCartByMenuId(List carts, Long menuId) { - return carts.stream() - .filter(cart -> cart.getMenu().getId().equals(menuId)) - .findFirst(); - } + List cartsForStatusManage = cartRepository.findAllByUserAndStatus(user, Status.ACTIVE); - private Cart createNewCart(OrderRequest.OrderItemRequest item, User user, Store store) { - Menu menu = menuRepository.findById(item.menuId()) - .orElseThrow(() -> new IllegalArgumentException("유효하지 않은 메뉴 ID입니다.")); - return Cart.builder() - .quantity(item.quantity()) - .menu(menu) - .user(user) - .store(store) - .orders(null) - .build(); - } + cartsForStatusManage.stream() + .forEach(cart -> cart.updateStatus(Status.INACTIVE)); - private Cart createNewCartAfterOrder(OrderRequest.OrderItemRequest item, User user, Store store) { - Menu menu = menuRepository.findById(item.menuId()) - .orElseThrow(() -> new IllegalArgumentException("유효하지 않은 메뉴 ID입니다.")); - Cart cart = Cart.builder() - .quantity(item.quantity()) - .menu(menu) - .user(user) - .store(store) - .orders(null) - .build(); + return ticket; + } - cart.updateStatus(Status.INACTIVE); + private void syncCart(OrderRequest orderRequest, List carts, Orders orders, User user) { + if (carts.isEmpty()) { + for (OrderRequest.OrderItemRequest orderItemRequest : orderRequest.items()) { + Menu menu = menuRepository.findById(orderItemRequest.menuId()) + .orElseThrow(() -> new IllegalArgumentException("해당 메뉴를 찾을 수 없습니다.")); + + Cart newCart = Cart.builder() + .quantity(orderItemRequest.quantity()) + .orders(orders) + .menu(menu) + .user(user) + .store(orders.getStore()) + .build(); + + cartRepository.save(newCart); + cartRepository.flush(); + } + } else { + for (Cart cart : carts) { + for (OrderRequest.OrderItemRequest orderItemRequest : orderRequest.items()) { + if (orderItemRequest.menuId().equals(cart.getMenu().getId())) { + cart.updateQuantity(orderItemRequest.quantity()); + cart.updateOrders(orders); + } else { + Menu menu = menuRepository.findById(orderItemRequest.menuId()) + .orElseThrow(() -> new IllegalArgumentException("해당 메뉴를 찾을 수 없습니다.")); + + Cart newCart = Cart.builder() + .quantity(orderItemRequest.quantity()) + .orders(orders) + .menu(menu) + .user(user) + .store(cart.getStore()) + .build(); + + cartRepository.save(newCart); + } + } - return cart; + } + } } private Orders saveOrder(User user, Store store, Team team, OrderRequest orderRequest) { @@ -248,10 +238,6 @@ private Orders saveOrder(User user, Store store, Team team, OrderRequest orderRe return ordersRepository.save(orders); } - private void associateCartsWithOrder(List carts, Orders orders) { - carts.forEach(cart -> cart.updateOrders(orders)); - } - @Transactional public Message useMealTicket(String userProviderId, Long orderId) { User user = userRepository.findByProviderId(userProviderId) @@ -279,4 +265,18 @@ public Message useMealTicket(String userProviderId, Long orderId) { .message("식권을 사용했습니다.") .build(); } + + @Transactional + public Message removeCart(String userProviderId) { + User user = userRepository.findByProviderId(userProviderId) + .orElseThrow(() -> new NullPointerException()); + + List allByUserAndStatus = cartRepository.findAllByUserAndStatus(user, Status.ACTIVE); + + cartRepository.deleteAll(allByUserAndStatus); + + return Message.builder() + .message("장바구니를 비웠습니다.") + .build(); + } } diff --git a/src/main/java/com/jangburich/domain/order/domain/Cart.java b/src/main/java/com/jangburich/domain/order/domain/Cart.java index abbd1c3..bc717d4 100644 --- a/src/main/java/com/jangburich/domain/order/domain/Cart.java +++ b/src/main/java/com/jangburich/domain/order/domain/Cart.java @@ -2,6 +2,7 @@ import com.jangburich.domain.common.BaseEntity; import com.jangburich.domain.menu.domain.Menu; +import com.jangburich.domain.order.dto.request.AddCartRequest; import com.jangburich.domain.store.domain.Store; import com.jangburich.domain.user.domain.User; import jakarta.persistence.Column; @@ -57,6 +58,15 @@ public Cart(Integer quantity, Orders orders, Menu menu, User user, Store store) this.store = store; } + public static void validateHasAnotherStoreItem(User user, AddCartRequest addCartRequest, + List allByUserAndStatus) { + for (Cart cart : allByUserAndStatus) { + if (!cart.getStore().getId().equals(addCartRequest.storeId())) { + throw new IllegalArgumentException("서로 다른 가게의 물건을 장바구니에 함께 담을 수 없습니다."); + } + } + } + public void updateQuantity(int quantity) { this.quantity = quantity; } diff --git a/src/main/java/com/jangburich/domain/order/domain/repository/CartRepository.java b/src/main/java/com/jangburich/domain/order/domain/repository/CartRepository.java index 1120175..dbbff00 100644 --- a/src/main/java/com/jangburich/domain/order/domain/repository/CartRepository.java +++ b/src/main/java/com/jangburich/domain/order/domain/repository/CartRepository.java @@ -22,4 +22,7 @@ public interface CartRepository extends JpaRepository { List findAllByUserAndStoreAndStatus(User user, Store store, Status status); List findAllByOrders(Orders orders); + + Optional> findByUser(User user); + } diff --git a/src/main/java/com/jangburich/domain/order/domain/repository/OrdersQueryDslRepositoryImpl.java b/src/main/java/com/jangburich/domain/order/domain/repository/OrdersQueryDslRepositoryImpl.java index 72069bb..223e402 100644 --- a/src/main/java/com/jangburich/domain/order/domain/repository/OrdersQueryDslRepositoryImpl.java +++ b/src/main/java/com/jangburich/domain/order/domain/repository/OrdersQueryDslRepositoryImpl.java @@ -4,6 +4,7 @@ import static com.jangburich.domain.order.domain.QCart.cart; import static com.jangburich.domain.order.domain.QOrders.orders; +import com.jangburich.domain.common.Status; import com.jangburich.domain.order.dto.response.OrderItemResponse; import com.jangburich.domain.order.dto.response.OrderResponse; import com.jangburich.domain.order.dto.response.QOrderItemResponse; @@ -30,7 +31,8 @@ public OrderResponse findTicket(Long ordersId) { .from(cart) .leftJoin(orders).on(cart.orders.id.eq(ordersId)) .leftJoin(menu).on(cart.menu.id.eq(menu.id)) - .where((cart.orders.id.eq(ordersId))) + .where((cart.orders.id.eq(ordersId)), + cart.status.eq(Status.ACTIVE)) .fetch(); int totalPrice = orderItemResponses.stream() diff --git a/src/main/java/com/jangburich/domain/order/presentation/OrderController.java b/src/main/java/com/jangburich/domain/order/presentation/OrderController.java index da41f33..e7675ba 100644 --- a/src/main/java/com/jangburich/domain/order/presentation/OrderController.java +++ b/src/main/java/com/jangburich/domain/order/presentation/OrderController.java @@ -12,6 +12,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -36,6 +37,14 @@ public ResponseCustom addCart( return ResponseCustom.OK(orderService.addCart(AuthenticationParser.parseUserId(authentication), addCartRequest)); } + @Operation(summary = "장바구니 비우기", description = "장바구니를 완전히 비웁니다.") + @DeleteMapping + public ResponseCustom removeCart( + Authentication authentication + ) { + return ResponseCustom.OK(orderService.removeCart(AuthenticationParser.parseUserId(authentication))); + } + @Operation(summary = "장바구니 조회", description = "장바구니에 담은 상품을 조회합니다.") @GetMapping("/carts") public ResponseCustom getCartItems(