-
Notifications
You must be signed in to change notification settings - Fork 2
MOSU-153 refactor: 신청 조회시 신청 상태 구조 변경 #186
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,110 +7,115 @@ | |
| import java.util.Set; | ||
| import java.util.function.Function; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| import life.mosu.mosuserver.domain.application.ApplicationJpaEntity; | ||
| import life.mosu.mosuserver.domain.exam.ExamJpaEntity; | ||
| import life.mosu.mosuserver.domain.examapplication.ExamApplicationJpaEntity; | ||
| import life.mosu.mosuserver.domain.examapplication.ExamSubjectJpaEntity; | ||
| import life.mosu.mosuserver.domain.payment.PaymentJpaEntity; | ||
| import life.mosu.mosuserver.domain.refund.RefundJpaEntity; | ||
| import life.mosu.mosuserver.presentation.application.dto.ApplicationResponse; | ||
| import life.mosu.mosuserver.presentation.application.dto.ExamApplicationResponse; | ||
| import life.mosu.mosuserver.presentation.examapplication.dto.ExamApplicationWithStatus; | ||
|
|
||
| public record ApplicationContext( | ||
| List<ApplicationJpaEntity> applications, | ||
| List<ExamApplicationJpaEntity> examApplications, | ||
| List<ExamApplicationWithStatus> examApplications, | ||
| Map<Long, ExamJpaEntity> examMap, | ||
| Map<Long, List<ExamSubjectJpaEntity>> subjectMap, | ||
| Map<Long, PaymentJpaEntity> paymentMap | ||
| Map<Long, PaymentJpaEntity> paymentMap, | ||
| Map<Long, RefundJpaEntity> refundMap | ||
| ) { | ||
|
|
||
| public ApplicationContext(List<ApplicationJpaEntity> applications, | ||
| List<ExamApplicationJpaEntity> examApplications) { | ||
| this(applications, examApplications, Map.of(), Map.of(), Map.of()); | ||
| public ApplicationContext( | ||
| List<ApplicationJpaEntity> applications, | ||
| List<ExamApplicationWithStatus> examApplications | ||
| ) { | ||
| this(applications, examApplications, Map.of(), Map.of(), Map.of(), Map.of()); | ||
| } | ||
|
|
||
| public ApplicationContext fetchExams(Function<List<Long>, List<ExamJpaEntity>> fetcher) { | ||
| List<Long> examIds = this.examApplications.stream().map(ExamApplicationJpaEntity::getExamId) | ||
| .distinct().toList(); | ||
| Map<Long, ExamJpaEntity> newExamMap = fetcher.apply(examIds).stream() | ||
| .collect(Collectors.toMap(ExamJpaEntity::getId, Function.identity())); | ||
| return new ApplicationContext(this.applications, this.examApplications, newExamMap, | ||
| this.subjectMap, this.paymentMap); | ||
| Map<Long, ExamJpaEntity> newExamMap = fetcher.apply( | ||
| examApplications.stream() | ||
| .map(e -> e.examApplication().getExamId()) | ||
| .distinct() | ||
| .toList() | ||
| ).stream().collect(Collectors.toMap(ExamJpaEntity::getId, Function.identity())); | ||
|
|
||
| return new ApplicationContext(applications, examApplications, newExamMap, subjectMap, paymentMap, refundMap); | ||
| } | ||
|
|
||
| public ApplicationContext fetchSubjects( | ||
| Function<List<Long>, List<ExamSubjectJpaEntity>> fetcher) { | ||
| List<Long> examApplicationIds = this.examApplications.stream() | ||
| .map(ExamApplicationJpaEntity::getId).toList(); | ||
| Map<Long, List<ExamSubjectJpaEntity>> newSubjectMap = fetcher.apply(examApplicationIds) | ||
| .stream() | ||
| .collect(Collectors.groupingBy(ExamSubjectJpaEntity::getExamApplicationId)); | ||
| return new ApplicationContext(this.applications, this.examApplications, this.examMap, | ||
| newSubjectMap, this.paymentMap); | ||
| public ApplicationContext fetchSubjects(Function<List<Long>, List<ExamSubjectJpaEntity>> fetcher) { | ||
| Map<Long, List<ExamSubjectJpaEntity>> newSubjectMap = fetcher.apply( | ||
| examApplications.stream() | ||
| .map(e -> e.examApplication().getId()) | ||
| .toList() | ||
| ).stream().collect(Collectors.groupingBy(ExamSubjectJpaEntity::getExamApplicationId)); | ||
|
|
||
| return new ApplicationContext(applications, examApplications, examMap, newSubjectMap, paymentMap, refundMap); | ||
| } | ||
|
|
||
| public ApplicationContext fetchPayments(Function<List<Long>, List<PaymentJpaEntity>> fetcher) { | ||
| List<Long> examApplicationIds = this.examApplications.stream() | ||
| .map(ExamApplicationJpaEntity::getId).toList(); | ||
| Map<Long, PaymentJpaEntity> newPaymentMap = fetcher.apply(examApplicationIds).stream() | ||
| .collect(Collectors.toMap( | ||
| PaymentJpaEntity::getExamApplicationId, | ||
| Function.identity())); | ||
|
|
||
| return new ApplicationContext(this.applications, this.examApplications, this.examMap, | ||
| this.subjectMap, newPaymentMap); | ||
| Map<Long, PaymentJpaEntity> newPaymentMap = fetcher.apply( | ||
| examApplications.stream() | ||
| .map(e -> e.examApplication().getId()) | ||
| .toList() | ||
| ).stream().collect(Collectors.toMap(PaymentJpaEntity::getExamApplicationId, Function.identity())); | ||
|
|
||
| return new ApplicationContext(applications, examApplications, examMap, subjectMap, newPaymentMap, refundMap); | ||
| } | ||
|
|
||
| public ApplicationContext fetchRefunds(Function<List<Long>, List<RefundJpaEntity>> fetcher) { | ||
| Map<Long, RefundJpaEntity> newRefundMap = fetcher.apply( | ||
| examApplications.stream() | ||
| .map(e -> e.examApplication().getId()) | ||
| .toList() | ||
| ).stream().collect(Collectors.toMap(RefundJpaEntity::getExamApplicationId, Function.identity())); | ||
|
|
||
| return new ApplicationContext(applications, examApplications, examMap, subjectMap, paymentMap, newRefundMap); | ||
| } | ||
|
|
||
| public List<ApplicationResponse> assemble() { | ||
| Map<Long, List<ExamApplicationResponse>> groupedExamResponses = this.examApplications.stream() | ||
| Map<Long, List<ExamApplicationResponse>> grouped = examApplications.stream() | ||
| .map(this::createExamApplicationResponse) | ||
| .filter(Objects::nonNull) | ||
| .collect(Collectors.groupingBy(Map.Entry::getKey, | ||
| Collectors.mapping(Map.Entry::getValue, Collectors.toList()))); | ||
|
|
||
| return this.applications.stream() | ||
| return applications.stream() | ||
| .map(app -> ApplicationResponse.of(app.getId(), | ||
| groupedExamResponses.getOrDefault(app.getId(), List.of()))) | ||
| grouped.getOrDefault(app.getId(), List.of()))) | ||
| .toList(); | ||
| } | ||
|
|
||
| private Map.Entry<Long, ExamApplicationResponse> createExamApplicationResponse( | ||
| ExamApplicationJpaEntity examApp) throws RuntimeException { | ||
|
|
||
| ExamJpaEntity exam = this.examMap.get(examApp.getExamId()); | ||
| if (exam == null) { | ||
| return null; | ||
| } | ||
| private Map.Entry<Long, ExamApplicationResponse> createExamApplicationResponse(ExamApplicationWithStatus item) { | ||
| ExamApplicationJpaEntity examApp = item.examApplication(); | ||
| ExamJpaEntity exam = examMap.get(examApp.getExamId()); | ||
| if (exam == null) return null; | ||
|
|
||
| List<ExamSubjectJpaEntity> subjectEntities = this.subjectMap.getOrDefault(examApp.getId(), | ||
| List.of()); | ||
| Set<String> subjects = subjectEntities.stream().map(es -> es.getSubject().getSubjectName()) | ||
| .collect(Collectors.toSet()); | ||
| Set<String> subjects = subjectMap.getOrDefault(examApp.getId(), List.of()).stream() | ||
| .map(s -> s.getSubject().getSubjectName()).collect(Collectors.toSet()); | ||
|
|
||
| PaymentJpaEntity payment = this.paymentMap.getOrDefault(examApp.getId(), null); | ||
| String paymentStatus = Optional.ofNullable(payment) | ||
| .map(p -> p.getPaymentStatus().name()) | ||
| .orElse(null); | ||
| PaymentJpaEntity payment = paymentMap.get(examApp.getId()); | ||
| RefundJpaEntity refund = refundMap.get(examApp.getId()); | ||
|
|
||
| Integer totalAmount = Optional.ofNullable(payment) | ||
| .map(p -> p.getPaymentAmount().getTotalAmount()) | ||
| .orElse(0); | ||
|
|
||
| String lunchName; | ||
| if (examApp.getIsLunchChecked()) { | ||
| lunchName = exam.getLunchName(); | ||
| } else { | ||
| lunchName = null; | ||
| } | ||
| String lunchName = examApp.getIsLunchChecked() ? exam.getLunchName() : null; | ||
| String status = item.status(); | ||
|
|
||
| ExamApplicationResponse response = ExamApplicationResponse.of( | ||
| examApp.getId(), | ||
| examApp.getCreatedAt(), | ||
| paymentStatus, totalAmount, | ||
| status, | ||
| totalAmount, | ||
| exam.getSchoolName(), | ||
| exam.getExamDate(), | ||
| subjects, | ||
| lunchName | ||
| ); | ||
|
|
||
| return Map.entry(examApp.getApplicationId(), response); | ||
| } | ||
|
Comment on lines
+91
to
120
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainConsider utilizing the refund entity in the response. The refund entity is fetched but not used in the response creation. Since the PR aims to integrate refund information, consider:
Let me check if 🏁 Script executed: #!/bin/bash
# Check the structure of ExamApplicationResponse
fd -e java -x grep -l "ExamApplicationResponse" {} \; | xargs grep -A 10 "class ExamApplicationResponse\|record ExamApplicationResponse"Length of output: 1448 Include refund information in the response creation You’re currently fetching
Locations to update:
🤖 Prompt for AI Agents |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,8 +9,10 @@ | |
| import life.mosu.mosuserver.domain.examapplication.ExamApplicationJpaRepository; | ||
| import life.mosu.mosuserver.domain.examapplication.ExamSubjectJpaRepository; | ||
| import life.mosu.mosuserver.domain.payment.PaymentJpaRepository; | ||
| import life.mosu.mosuserver.domain.refund.RefundJpaRepository; | ||
| import life.mosu.mosuserver.global.processor.StepProcessor; | ||
| import life.mosu.mosuserver.presentation.application.dto.ApplicationResponse; | ||
| import life.mosu.mosuserver.presentation.examapplication.dto.ExamApplicationWithStatus; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.stereotype.Component; | ||
|
|
@@ -26,6 +28,7 @@ public class GetApplicationsStepProcessor implements | |
| private final ExamApplicationJpaRepository examApplicationJpaRepository; | ||
| private final ExamJpaRepository examJpaRepository; | ||
| private final PaymentJpaRepository paymentJpaRepository; | ||
| private final RefundJpaRepository refundJpaRepository; | ||
|
|
||
| @Override | ||
| public List<ApplicationResponse> process(Long userId) { | ||
|
|
@@ -37,13 +40,14 @@ public List<ApplicationResponse> process(Long userId) { | |
| } | ||
|
|
||
| List<Long> applicationIds = applications.stream().map(ApplicationJpaEntity::getId).toList(); | ||
| List<ExamApplicationJpaEntity> examApplications = examApplicationJpaRepository.findByApplicationIdIn( | ||
| List<ExamApplicationWithStatus> examApplications = examApplicationJpaRepository.findByApplicationIdIn( | ||
| applicationIds); | ||
|
Comment on lines
+43
to
44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The query in return new ApplicationContext(applications, examApplications)
.fetchExams(examJpaRepository::findByIdIn)
.fetchSubjects(examSubjectJpaRepository::findByExamApplicationIdIn)
.fetchPayments(paymentJpaRepository::findByExamApplicationIdIn)
.assemble(); |
||
|
|
||
| return new ApplicationContext(applications, examApplications) | ||
| .fetchExams(examJpaRepository::findByIdIn) | ||
| .fetchSubjects(examSubjectJpaRepository::findByExamApplicationIdIn) | ||
| .fetchPayments(paymentJpaRepository::findByExamApplicationIdIn) | ||
| .fetchRefunds(refundJpaRepository::findByExamApplicationIdIn) | ||
| .assemble(); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ | |
| import life.mosu.mosuserver.domain.examapplication.projection.ExamApplicationNotifyProjection; | ||
| import life.mosu.mosuserver.domain.examapplication.projection.ExamInfoWithExamNumberProjection; | ||
| import life.mosu.mosuserver.domain.examapplication.projection.ExamTicketInfoProjection; | ||
| import life.mosu.mosuserver.presentation.examapplication.dto.ExamApplicationWithStatus; | ||
| import org.springframework.data.jpa.repository.JpaRepository; | ||
| import org.springframework.data.jpa.repository.Modifying; | ||
| import org.springframework.data.jpa.repository.Query; | ||
|
|
@@ -71,7 +72,22 @@ Optional<ExamTicketInfoProjection> findExamTicketInfoProjectionById( | |
|
|
||
| boolean existsByApplicationId(Long applicationId); | ||
|
|
||
| List<ExamApplicationJpaEntity> findByApplicationIdIn(List<Long> applicationIds); | ||
| @Query(""" | ||
| SELECT new life.mosu.mosuserver.presentation.examapplication.dto.ExamApplicationWithStatus( | ||
| ea, | ||
| CASE | ||
| WHEN r.refundStatus = 'DONE' THEN '환불완료' | ||
| ELSE '결제완료' | ||
| END | ||
| ) | ||
|
Comment on lines
+76
to
+82
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The JPQL query returns presentation-layer strings ( @Query("""
SELECT new life.mosu.mosuserver.presentation.examapplication.dto.ExamApplicationWithStatus(
ea,
CASE
WHEN r.refundStatus = 'DONE' THEN true
ELSE false
END
)
FROM ExamApplicationJpaEntity ea
JOIN PaymentJpaEntity p ON p.examApplicationId = ea.id
LEFT JOIN RefundJpaEntity r ON r.examApplicationId = ea.id
WHERE p.paymentStatus = 'DONE'
AND (r IS NULL OR r.refundStatus = 'DONE')
AND ea.applicationId IN :applicationIds
""")
List<ExamApplicationWithStatus> findByApplicationIdIn(List<Long> applicationIds); |
||
| FROM ExamApplicationJpaEntity ea | ||
| JOIN PaymentJpaEntity p ON p.examApplicationId = ea.id | ||
| LEFT JOIN RefundJpaEntity r ON r.examApplicationId = ea.id | ||
| WHERE p.paymentStatus = 'DONE' | ||
| AND (r IS NULL OR r.refundStatus = 'DONE') | ||
| AND ea.applicationId IN :applicationIds | ||
| """) | ||
| List<ExamApplicationWithStatus> findByApplicationIdIn(List<Long> applicationIds); | ||
|
|
||
|
|
||
| @Query(""" | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,9 +1,10 @@ | ||||||
| package life.mosu.mosuserver.domain.refund; | ||||||
|
|
||||||
| import io.lettuce.core.dynamic.annotation.Param; | ||||||
| import java.util.List; | ||||||
| import java.util.Optional; | ||||||
| import org.springframework.data.jpa.repository.JpaRepository; | ||||||
| import org.springframework.data.jpa.repository.Query; | ||||||
| import org.springframework.data.repository.query.Param; | ||||||
|
|
||||||
| public interface RefundJpaRepository extends JpaRepository<RefundJpaEntity, Long> { | ||||||
|
|
||||||
|
|
@@ -32,4 +33,11 @@ Optional<RefundNotifyProjection> findRefundByExamApplicationId( | |||||
| @Param("examApplicationId") Long examApplicationId); | ||||||
|
|
||||||
| Optional<RefundJpaEntity> findByTransactionKey(String transactionKey); | ||||||
|
|
||||||
| @Query(""" | ||||||
| SELECT r | ||||||
| FROM RefundJpaEntity r | ||||||
| WHERE r.examApplicationId IN :examApplicationIds | ||||||
| """) | ||||||
| List<RefundJpaEntity> findByExamApplicationIdIn(@Param("examApplicationIds") List<Long> examApplicationId); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The parameter name
Suggested change
|
||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package life.mosu.mosuserver.presentation.examapplication.dto; | ||
|
|
||
| import life.mosu.mosuserver.domain.examapplication.ExamApplicationJpaEntity; | ||
|
|
||
| public record ExamApplicationWithStatus( | ||
| ExamApplicationJpaEntity examApplication, | ||
| String status | ||
| ) { } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential data loss if multiple refunds exist for the same exam application.
The
toMapcollector will throw an exception if there are duplicate keys (multiple refunds for the same examApplicationId). Consider using a merge function or grouping if multiple refunds per application are possible.If multiple refunds are not possible by design, this is fine. Otherwise, consider:
📝 Committable suggestion
🤖 Prompt for AI Agents