Skip to content

Commit

Permalink
[DEV-18] Completed Credit (#247)
Browse files Browse the repository at this point in the history
* refactor: 전공 필드명 변경에 따른 코드 수정

major -> primaryMajor

* feat: 복수전공에 따른 전공이수 학점 Type

* refactor: 복수전공 로직 추가에 따른 매개변수 변경

* refactor: 복수전공에 따른 졸업 요건 학점수 변경

* refactor: 복수전공에 따른 parsing 로직 수정

* refactor: 부전공 졸업 학점 변경 로직 시점 변경

졸업요건 계산 시점에서 생성 시점으로 변경

* refactor: 유저 정보 수정시 복수전공 수정 추가

* refactor: user domain에 복수전공 필드 추 및 주전공 변수명 변경

* test: 부전공 졸업학점 조정 test로 수정

* test: 복수전공 parsing test

* refactor: 졸업요건 계산 로직에 복수전공 추가

* refactor: 필요없는 import 제거

* refactor: GraduationCategory 타입 추가 및 수정

* feat: CompletedCredit 도메인 모델 작성

* feat: CompletedCredit jpa entity 작성

* feat: CompletedCredit jpa entity 작성

* feat: CompletedCreditPersistenceMapper 구현

* feat: CompletedCreditRepository - 이수 학점 조회 구현

* feat: FindCompletedCreditPort 구현

* feat: FindCompletedCreditUseCase 구현

* refactor: 불필요 컨트롤러 삭제

* refactor: CalculateGraduationUseCase 리턴 타입 변경

* feat: GenerateOrModifyCompletedCreditPort 구현

* feat: GenerateOrModifyCompletedCreditUseCase 구현

* refactor: 성적표 업로드로 인한 이수 학점 업데이트 구현

* feat: CompletedCreditController 구현

* test: 테스트 오류 수정

* build: 테스트 코드 롬복 의존성 추가

* refactor: 불필요 의존성 제거

* refactor: 부정확 로직 제거

* refactor: swagger 코드 추가

* refactor: endpoint 수정

* refactor: 불필요 코드 삭제

* refactor: 채플 이수 학점 오류 수정

* refactor: GenerateOrModifyCompletedCreditUseCase 사용 위치 수정

* refactor: 채플 기이수 학점 산정 방식 수정

* refactor: GenerateOrModifyCompletedCredit 모델 생성 위치 수정

---------

Co-authored-by: 나경호 <[email protected]>
  • Loading branch information
5uhwann and Hoya324 authored Apr 9, 2024
1 parent 4d92c2c commit 1ebe900
Show file tree
Hide file tree
Showing 83 changed files with 1,200 additions and 382 deletions.
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ dependencies {
implementation "com.google.guava:guava:32.1.3-jre"
implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
implementation "com.querydsl:querydsl-apt:${queryDslVersion}"
//implementation 'org.flywaydb:flyway-core:6.4.2'
// implementation 'org.flywaydb:flyway-core:6.4.2'
implementation 'org.springdoc:springdoc-openapi-ui:1.6.9'
implementation 'io.sentry:sentry-logback:1.7.30'

Expand All @@ -56,6 +56,8 @@ dependencies {
testImplementation 'org.testcontainers:testcontainers:1.19.0'
testImplementation 'org.testcontainers:mysql:1.19.0'
testImplementation 'org.testcontainers:junit-jupiter:1.13.0'
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'


}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.plzgraduate.myongjigraduatebe.completedcredit.api;

import java.util.List;

import com.plzgraduate.myongjigraduatebe.completedcredit.api.dto.CompletedCreditResponse;
import com.plzgraduate.myongjigraduatebe.core.meta.LoginUser;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;

@Tag(name = "CompletdCredit")
public interface FindCompletedCreditApiPresentation {

@Operation(summary = "기이수 학점 조회", description = "유저의 기이수 학점 조회 API")
@Parameter(name = "userId", description = "로그인한 유저의 PK값 - 자동 적용")
List<CompletedCreditResponse> getCompletedCredits(@LoginUser Long userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.plzgraduate.myongjigraduatebe.completedcredit.api;

import java.util.List;
import java.util.stream.Collectors;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.plzgraduate.myongjigraduatebe.completedcredit.api.dto.CompletedCreditResponse;
import com.plzgraduate.myongjigraduatebe.completedcredit.application.usecase.FindCompletedCreditUseCase;
import com.plzgraduate.myongjigraduatebe.core.meta.LoginUser;
import com.plzgraduate.myongjigraduatebe.core.meta.WebAdapter;

import lombok.RequiredArgsConstructor;

@WebAdapter
@RequestMapping(value = "/api/v1/graduations/credits")
@RequiredArgsConstructor
public class FindCompletedCreditsController implements FindCompletedCreditApiPresentation{

private final FindCompletedCreditUseCase findCompletedCreditUseCase;

@GetMapping()
public List<CompletedCreditResponse> getCompletedCredits(@LoginUser Long userId) {
return findCompletedCreditUseCase.findCompletedCredits(userId).stream()
.map(CompletedCreditResponse::from)
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.plzgraduate.myongjigraduatebe.completedcredit.api.dto;

import com.plzgraduate.myongjigraduatebe.completedcredit.domain.model.CompletedCredit;

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class CompletedCreditResponse {

private String category;
private int totalCredit;
private double takenCredit;
private boolean completed;

@Builder
private CompletedCreditResponse(String category, int totalCredit, double takenCredit, boolean completed) {
this.category = category;
this.totalCredit = totalCredit;
this.takenCredit = takenCredit;
this.completed = completed;
}

public static CompletedCreditResponse from(CompletedCredit completedCredit) {
return CompletedCreditResponse.builder()
.category(completedCredit.getGraduationCategory().name())
.totalCredit(completedCredit.getTotalCredit())
.takenCredit(completedCredit.getTakenCredit())
.completed(completedCredit.getTakenCredit() >= completedCredit.getTotalCredit())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.plzgraduate.myongjigraduatebe.completedcredit.application.port;

import java.util.List;

import com.plzgraduate.myongjigraduatebe.completedcredit.domain.model.CompletedCredit;
import com.plzgraduate.myongjigraduatebe.user.domain.model.User;

public interface FindCompletedCreditPort {

List<CompletedCredit> findCompletedCredit(User user);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.plzgraduate.myongjigraduatebe.completedcredit.application.port;

import java.util.List;

import com.plzgraduate.myongjigraduatebe.completedcredit.domain.model.CompletedCredit;
import com.plzgraduate.myongjigraduatebe.user.domain.model.User;

public interface GenerateOrModifyCompletedCreditPort {

void generateOrModifyCompletedCredits(User user, List<CompletedCredit> completedCredits);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.plzgraduate.myongjigraduatebe.completedcredit.application.service;

import java.util.List;

import org.springframework.transaction.annotation.Transactional;

import com.plzgraduate.myongjigraduatebe.completedcredit.application.port.FindCompletedCreditPort;
import com.plzgraduate.myongjigraduatebe.completedcredit.application.usecase.FindCompletedCreditUseCase;
import com.plzgraduate.myongjigraduatebe.completedcredit.domain.model.CompletedCredit;
import com.plzgraduate.myongjigraduatebe.core.meta.UseCase;
import com.plzgraduate.myongjigraduatebe.user.application.usecase.find.FindUserUseCase;
import com.plzgraduate.myongjigraduatebe.user.domain.model.User;

import lombok.RequiredArgsConstructor;

@UseCase
@RequiredArgsConstructor
@Transactional(readOnly = true)
class FindCompletedCreditService implements FindCompletedCreditUseCase {

private final FindUserUseCase findUserUseCase;
private final FindCompletedCreditPort findCompletedCreditPort;

@Override
public List<CompletedCredit> findCompletedCredits(Long userId) {
User user = findUserUseCase.findUserById(userId);
return findCompletedCreditPort.findCompletedCredit(user);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package com.plzgraduate.myongjigraduatebe.completedcredit.application.service;

import static com.plzgraduate.myongjigraduatebe.graduation.domain.model.ChapelResult.GRADUATION_COUNT;
import static com.plzgraduate.myongjigraduatebe.graduation.domain.model.GraduationCategory.CHAPEL;
import static com.plzgraduate.myongjigraduatebe.graduation.domain.model.GraduationCategory.FREE_ELECTIVE;
import static com.plzgraduate.myongjigraduatebe.graduation.domain.model.GraduationCategory.NORMAL_CULTURE;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.springframework.transaction.annotation.Transactional;

import com.plzgraduate.myongjigraduatebe.completedcredit.application.port.FindCompletedCreditPort;
import com.plzgraduate.myongjigraduatebe.completedcredit.application.port.GenerateOrModifyCompletedCreditPort;
import com.plzgraduate.myongjigraduatebe.completedcredit.application.usecase.GenerateOrModifyCompletedCreditUseCase;
import com.plzgraduate.myongjigraduatebe.completedcredit.domain.model.CompletedCredit;
import com.plzgraduate.myongjigraduatebe.core.meta.UseCase;
import com.plzgraduate.myongjigraduatebe.graduation.application.usecase.CalculateGraduationUseCase;
import com.plzgraduate.myongjigraduatebe.graduation.domain.model.DetailGraduationResult;
import com.plzgraduate.myongjigraduatebe.graduation.domain.model.GraduationCategory;
import com.plzgraduate.myongjigraduatebe.graduation.domain.model.GraduationResult;
import com.plzgraduate.myongjigraduatebe.user.domain.model.User;

import lombok.RequiredArgsConstructor;

@UseCase
@Transactional
@RequiredArgsConstructor
class GenerateOrModifyCompletedCreditService implements GenerateOrModifyCompletedCreditUseCase {

private final FindCompletedCreditPort findCompletedCreditPort;
private final GenerateOrModifyCompletedCreditPort generateOrModifyCompletedCreditPort;

private final CalculateGraduationUseCase calculateGraduationUseCase;

@Override
public void generateOrModifyCompletedCredit(User user) {
List<CompletedCredit> completedCredits = findCompletedCreditPort.findCompletedCredit(user);
GraduationResult graduationResult = calculateGraduationUseCase.calculateGraduation(user);
List<DetailGraduationResult> detailGraduationResults = graduationResult.getDetailGraduationResults();

List<CompletedCredit> updatedCompletedCredits = createGeneralCompletedCreditModel(completedCredits,
detailGraduationResults);
CompletedCredit chapelCompletedCreditModel = createOrUpdateChapelCompletedCreditModel(completedCredits,
graduationResult);
CompletedCredit normalCultureCompletedCreditModel = createOrUpdateNormalCultureCompletedCreditModel(
completedCredits, graduationResult);
CompletedCredit freeElectiveCompletedCreditModel = createOrUpdateFreeElectiveCompletedCreditModel(
completedCredits, graduationResult);

ArrayList<CompletedCredit> allCompletedCreditModels = new ArrayList<>(updatedCompletedCredits);
allCompletedCreditModels.addAll(
List.of(chapelCompletedCreditModel, normalCultureCompletedCreditModel, freeElectiveCompletedCreditModel));
generateOrModifyCompletedCreditPort.generateOrModifyCompletedCredits(user, allCompletedCreditModels);
}

private List<CompletedCredit> createGeneralCompletedCreditModel(
List<CompletedCredit> completedCredits, List<DetailGraduationResult> detailGraduationResults) {
Map<DetailGraduationResult, Optional<CompletedCredit>> resultMap = detailGraduationResults.stream()
.collect(Collectors.toMap(
Function.identity(),
detailGraduationResult -> completedCredits.stream()
.filter(completedCredit -> completedCredit.getGraduationCategory()
.equals(detailGraduationResult.getGraduationCategory()))
.findFirst()
));
return resultMap.keySet().stream()
.map(detailGraduationResult -> createCompletedCreditModel(detailGraduationResult,
resultMap.get(detailGraduationResult)))
.collect(Collectors.toList());
}

private CompletedCredit createCompletedCreditModel(DetailGraduationResult detailGraduationResult,
Optional<CompletedCredit> completedCredit) {
return CompletedCredit.builder()
.id(completedCredit.map(CompletedCredit::getId).orElse(null))
.totalCredit(detailGraduationResult.getTotalCredit())
.takenCredit(detailGraduationResult.getTakenCredit())
.graduationCategory(detailGraduationResult.getGraduationCategory())
.build();
}

private CompletedCredit createOrUpdateChapelCompletedCreditModel(List<CompletedCredit> completedCredits,
GraduationResult graduationResult) {
Optional<CompletedCredit> chapelCompletedCredit = findCompletedCreditByCategory(completedCredits, CHAPEL);
return chapelCompletedCredit.map(
completedCredit -> updateCompletedCredit(completedCredit, GRADUATION_COUNT / 2,
graduationResult.getChapelResult().getTakenChapelCredit()))
.orElseGet(() -> CompletedCredit.createChapelCompletedCreditModel(graduationResult.getChapelResult()));
}

private CompletedCredit createOrUpdateNormalCultureCompletedCreditModel(List<CompletedCredit> completedCredits,
GraduationResult graduationResult) {
Optional<CompletedCredit> normalCultureCompletedCredit = findCompletedCreditByCategory(completedCredits,
NORMAL_CULTURE);
return normalCultureCompletedCredit.map(completedCredit -> updateCompletedCredit(completedCredit,
graduationResult.getNormalCultureGraduationResult().getTotalCredit(),
graduationResult.getNormalCultureGraduationResult().getTakenCredit()))
.orElseGet(() -> CompletedCredit.createNormalCultureCompletedCreditModel(
graduationResult.getNormalCultureGraduationResult()));
}

private CompletedCredit createOrUpdateFreeElectiveCompletedCreditModel(List<CompletedCredit> completedCredits,
GraduationResult graduationResult) {
Optional<CompletedCredit> freeElectiveCompletedCredit = findCompletedCreditByCategory(completedCredits,
FREE_ELECTIVE);
return freeElectiveCompletedCredit.map(completedCredit -> updateCompletedCredit(completedCredit,
graduationResult.getFreeElectiveGraduationResult().getTotalCredit(),
graduationResult.getFreeElectiveGraduationResult().getTakenCredit()))
.orElseGet(() -> CompletedCredit.createFreeElectiveCompletedCreditModel(
graduationResult.getFreeElectiveGraduationResult()));
}

private Optional<CompletedCredit> findCompletedCreditByCategory(List<CompletedCredit> completedCredits,
GraduationCategory category) {
return completedCredits.stream()
.filter(completedCredit -> completedCredit.getGraduationCategory() == category)
.findFirst();
}

private CompletedCredit updateCompletedCredit(CompletedCredit completedCredit,
int totalCredit, double takenCredit) {
completedCredit.updateCredit(totalCredit, takenCredit);
return completedCredit;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.plzgraduate.myongjigraduatebe.completedcredit.application.usecase;

import java.util.List;

import com.plzgraduate.myongjigraduatebe.completedcredit.domain.model.CompletedCredit;

public interface FindCompletedCreditUseCase {

List<CompletedCredit> findCompletedCredits(Long userId);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.plzgraduate.myongjigraduatebe.completedcredit.application.usecase;

import com.plzgraduate.myongjigraduatebe.user.domain.model.User;

public interface GenerateOrModifyCompletedCreditUseCase {

void generateOrModifyCompletedCredit(User user);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.plzgraduate.myongjigraduatebe.completedcredit.domain.model;

import static com.plzgraduate.myongjigraduatebe.graduation.domain.model.GraduationCategory.*;

import com.plzgraduate.myongjigraduatebe.graduation.domain.model.ChapelResult;
import com.plzgraduate.myongjigraduatebe.graduation.domain.model.DetailGraduationResult;
import com.plzgraduate.myongjigraduatebe.graduation.domain.model.FreeElectiveGraduationResult;
import com.plzgraduate.myongjigraduatebe.graduation.domain.model.GraduationCategory;
import com.plzgraduate.myongjigraduatebe.graduation.domain.model.NormalCultureGraduationResult;

import lombok.Builder;
import lombok.Getter;

@Getter
public class CompletedCredit {

private final Long id;
private final GraduationCategory graduationCategory;
private int totalCredit;
private double takenCredit;

@Builder
private CompletedCredit(Long id, GraduationCategory graduationCategory, int totalCredit,
double takenCredit) {
this.id = id;
this.graduationCategory = graduationCategory;
this.totalCredit = totalCredit;
this.takenCredit = takenCredit;
}

public static CompletedCredit from(DetailGraduationResult detailGraduationResults) {
return CompletedCredit.builder()
.graduationCategory(detailGraduationResults.getGraduationCategory())
.totalCredit(detailGraduationResults.getTotalCredit())
.takenCredit(detailGraduationResults.getTakenCredit()).build();
}

public static CompletedCredit createChapelCompletedCreditModel(ChapelResult chapelResult) {
return CompletedCredit.builder()
.graduationCategory(CHAPEL)
.totalCredit(ChapelResult.GRADUATION_COUNT / 2)
.takenCredit(chapelResult.getTakenChapelCredit()).build();
}

public static CompletedCredit createNormalCultureCompletedCreditModel(
NormalCultureGraduationResult normalCultureGraduationResult) {
return CompletedCredit.builder()
.graduationCategory(NORMAL_CULTURE)
.totalCredit(normalCultureGraduationResult.getTotalCredit())
.takenCredit(normalCultureGraduationResult.getTakenCredit()).build();
}

public static CompletedCredit createFreeElectiveCompletedCreditModel(
FreeElectiveGraduationResult freeElectiveGraduationResult) {
return CompletedCredit.builder()
.graduationCategory(FREE_ELECTIVE)
.totalCredit(freeElectiveGraduationResult.getTotalCredit())
.takenCredit(freeElectiveGraduationResult.getTakenCredit()).build();
}

public void updateCredit(int totalCredit, double takenCredit) {
this.totalCredit = totalCredit;
this.takenCredit = takenCredit;
}
}
Loading

0 comments on commit 1ebe900

Please sign in to comment.