diff --git a/.github/workflows/CD.yml b/.github/workflows/CD.yml new file mode 100644 index 00000000..2873106e --- /dev/null +++ b/.github/workflows/CD.yml @@ -0,0 +1,117 @@ +name: ClassLog CD + +on: + push: + branches: ["dev"] + +permissions: + contents: read + packages: write + +env: + IMAGE_TAG: ${{ github.sha }} + BACKEND_IMAGE: ${{ secrets.DOCKER_USERNAME }}/classlog-backend + FRONTEND_IMAGE: ${{ secrets.DOCKER_USERNAME }}/classlog-frontend +jobs: + build-and-push: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name : Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Grant execute permission for gradlew + working-directory: ./backend + run: chmod +x gradlew + + - name: Build with Gradle + working-directory: ./backend + run: ./gradlew clean bootJar + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build & Push Backend Image + run: | + docker build -t ${BACKEND_IMAGE}:latest -t ${BACKEND_IMAGE}:${IMAGE_TAG} ./backend + docker push ${BACKEND_IMAGE}:latest + docker push ${BACKEND_IMAGE}:${IMAGE_TAG} + + - name: Build & Push Frontend Image + run: | + docker build -t ${FRONTEND_IMAGE}:latest -t ${FRONTEND_IMAGE}:${IMAGE_TAG} ./frontend + docker push ${FRONTEND_IMAGE}:latest + docker push ${FRONTEND_IMAGE}:${IMAGE_TAG} + deploy: + needs: build-and-push + runs-on: ubuntu-latest + + steps: + - name: Create backend/.env on server + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.SSH_HOST }} + username: ${{ secrets.SSH_USER }} + key: ${{ secrets.SSH_KEY }} + script: | + mkdir -p /home/ubuntu/classlog/backend + cat > /home/ubuntu/classlog/backend/.env < /home/ubuntu/classlog/frontend/.env <> submitQuiz( .status(e.getErrorCode().getReasonHttpStatus().getHttpStatus()) .body(ApiResponse.onFailure(e.getErrorCode())); } catch (Exception e) { - e.printStackTrace(); // 콘솔에 전체 스택트레이스 출력 + return ResponseEntity + .status(FailureCode._INTERNAL_SERVER_ERROR.getReasonHttpStatus().getHttpStatus()) + .body(ApiResponse.onFailure(FailureCode._INTERNAL_SERVER_ERROR)); + } + } + + // 학생 별 퀴즈 선택 결과 + @GetMapping("/{lectureId}/result/student") + public ResponseEntity> getQuizResult(@PathVariable("lectureId") UUID lectureId) { + try{ + QuizResultStudentResponseDTO result = quizResultStudentService.getQuizResult(lectureId); + return ResponseEntity.ok(ApiResponse.onSuccess(result)); + } catch (QuizException e) { + return ResponseEntity + .status(e.getErrorCode().getReasonHttpStatus().getHttpStatus()) + .body(ApiResponse.onFailure(e.getErrorCode())); + } catch (Exception e) { return ResponseEntity .status(FailureCode._INTERNAL_SERVER_ERROR.getReasonHttpStatus().getHttpStatus()) .body(ApiResponse.onFailure(FailureCode._INTERNAL_SERVER_ERROR)); diff --git a/backend/src/main/java/org/example/backend/domain/quizAnswer/converter/QuizAnswerConverter.java b/backend/src/main/java/org/example/backend/domain/quizAnswer/converter/QuizAnswerConverter.java index 20667e9f..6070edcf 100644 --- a/backend/src/main/java/org/example/backend/domain/quizAnswer/converter/QuizAnswerConverter.java +++ b/backend/src/main/java/org/example/backend/domain/quizAnswer/converter/QuizAnswerConverter.java @@ -1,12 +1,18 @@ package org.example.backend.domain.quizAnswer.converter; +import org.example.backend.domain.option.entity.Option; import org.example.backend.domain.quiz.entity.Quiz; +import org.example.backend.domain.quizAnswer.dto.response.QuizResultStudentResponseDTO; import org.example.backend.domain.quizAnswer.dto.response.QuizSubmitResponseDTO; import org.example.backend.domain.quizAnswer.entity.QuizAnswer; import org.example.backend.domain.user.repository.UserRepository; import org.springframework.stereotype.Component; +import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.UUID; +import java.util.stream.Collectors; @Component public class QuizAnswerConverter { @@ -26,6 +32,4 @@ public static QuizSubmitResponseDTO toSubmitResponse(UUID userId, int savedCount .savedCount(savedCount) .build(); } - - } \ No newline at end of file diff --git a/backend/src/main/java/org/example/backend/domain/quizAnswer/converter/QuizResultStudentConverter.java b/backend/src/main/java/org/example/backend/domain/quizAnswer/converter/QuizResultStudentConverter.java new file mode 100644 index 00000000..6e7ad294 --- /dev/null +++ b/backend/src/main/java/org/example/backend/domain/quizAnswer/converter/QuizResultStudentConverter.java @@ -0,0 +1,68 @@ +package org.example.backend.domain.quizAnswer.converter; + +import org.example.backend.domain.option.entity.Option; +import org.example.backend.domain.quiz.entity.Quiz; +import org.example.backend.domain.quizAnswer.dto.response.QuizResultStudentResponseDTO; +import org.example.backend.domain.quizAnswer.entity.QuizAnswer; +import org.springframework.stereotype.Component; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@Component +public class QuizResultStudentConverter { + public static QuizResultStudentResponseDTO toResultStudentResponse( + UUID lectureId, + List quizzes + ) { + return QuizResultStudentResponseDTO.builder() + .lectureId(lectureId) + .quizzes(quizzes) + .build(); + } + + public static QuizResultStudentResponseDTO.QuizDTO toQuizDTO( + Quiz quiz, + List