diff --git a/src/main/java/life/mosu/mosuserver/application/application/ApplicationContext.java b/src/main/java/life/mosu/mosuserver/application/application/ApplicationContext.java index 03b731bc..2816e3f1 100644 --- a/src/main/java/life/mosu/mosuserver/application/application/ApplicationContext.java +++ b/src/main/java/life/mosu/mosuserver/application/application/ApplicationContext.java @@ -12,7 +12,6 @@ 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.payment.PaymentStatus; import life.mosu.mosuserver.presentation.application.dto.ApplicationResponse; import life.mosu.mosuserver.presentation.application.dto.ExamApplicationResponse; @@ -53,7 +52,6 @@ public ApplicationContext fetchPayments(Function, List examApplicationIds = this.examApplications.stream() .map(ExamApplicationJpaEntity::getId).toList(); Map newPaymentMap = fetcher.apply(examApplicationIds).stream() - .filter(payment -> payment.getPaymentStatus().equals(PaymentStatus.DONE)) .collect(Collectors.toMap( PaymentJpaEntity::getExamApplicationId, Function.identity())); diff --git a/src/main/java/life/mosu/mosuserver/application/application/ApplicationService.java b/src/main/java/life/mosu/mosuserver/application/application/ApplicationService.java index 8b41f340..f6ba81ad 100644 --- a/src/main/java/life/mosu/mosuserver/application/application/ApplicationService.java +++ b/src/main/java/life/mosu/mosuserver/application/application/ApplicationService.java @@ -42,9 +42,9 @@ public CreateApplicationResponse apply(Long userId, ApplicationRequest request) Set subjects = request.getSubjects(); validator.ExamDateNotPassed(examIds); - validator.RequestNoDuplicateExams(examIds); - validator.ExamIdsAndLunchSelection(request.examApplication()); - validator.NoDuplicateApplication(userId, examIds); + validator.requestNoDuplicateExams(examIds); + validator.examIdsAndLunchSelection(request.examApplication()); + validator.noDuplicateApplication(userId, examIds); ApplicationJpaEntity application = request.toApplicationJpaEntity(userId); ApplicationJpaEntity savedApplication = applicationJpaRepository.save(application); @@ -52,7 +52,7 @@ public CreateApplicationResponse apply(Long userId, ApplicationRequest request) Long applicationId = savedApplication.getId(); registerApplicationStepProcessor.process( - RegisterApplicationCommand.of(applicationId, request, subjects)); + RegisterApplicationCommand.of(userId, applicationId, request, subjects)); saveExamTicketStepProcessor.process( ApplicationProcessingContext.of(applicationId, request.admissionTicket())); diff --git a/src/main/java/life/mosu/mosuserver/application/application/dto/RegisterApplicationCommand.java b/src/main/java/life/mosu/mosuserver/application/application/dto/RegisterApplicationCommand.java index 70dcc6b6..bfbd31e6 100644 --- a/src/main/java/life/mosu/mosuserver/application/application/dto/RegisterApplicationCommand.java +++ b/src/main/java/life/mosu/mosuserver/application/application/dto/RegisterApplicationCommand.java @@ -5,16 +5,18 @@ import life.mosu.mosuserver.presentation.application.dto.ApplicationRequest; public record RegisterApplicationCommand( + Long userId, Long applicationId, ApplicationRequest applicationRequest, Set subjects ) { public static RegisterApplicationCommand of( + Long userId, Long applicationId, ApplicationRequest applicationRequest, Set subjects ) { - return new RegisterApplicationCommand(applicationId, applicationRequest, subjects); + return new RegisterApplicationCommand(userId, applicationId, applicationRequest, subjects); } } diff --git a/src/main/java/life/mosu/mosuserver/application/application/processor/GetApplicationsStepProcessor.java b/src/main/java/life/mosu/mosuserver/application/application/processor/GetApplicationsStepProcessor.java index 77f54cd2..a9f0b7ac 100644 --- a/src/main/java/life/mosu/mosuserver/application/application/processor/GetApplicationsStepProcessor.java +++ b/src/main/java/life/mosu/mosuserver/application/application/processor/GetApplicationsStepProcessor.java @@ -12,8 +12,10 @@ import life.mosu.mosuserver.global.processor.StepProcessor; import life.mosu.mosuserver.presentation.application.dto.ApplicationResponse; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +@Slf4j @Component @RequiredArgsConstructor public class GetApplicationsStepProcessor implements @@ -29,6 +31,7 @@ public class GetApplicationsStepProcessor implements public List process(Long userId) { List applications = applicationJpaRepository.findAllByUserId(userId); + log.info("applications info: {}", applications.size()); if (applications.isEmpty()) { return List.of(); } diff --git a/src/main/java/life/mosu/mosuserver/application/application/processor/RegisterApplicationStepProcessor.java b/src/main/java/life/mosu/mosuserver/application/application/processor/RegisterApplicationStepProcessor.java index dd5c040b..04e440a0 100644 --- a/src/main/java/life/mosu/mosuserver/application/application/processor/RegisterApplicationStepProcessor.java +++ b/src/main/java/life/mosu/mosuserver/application/application/processor/RegisterApplicationStepProcessor.java @@ -28,10 +28,11 @@ public RegisterApplicationCommand process(RegisterApplicationCommand command) { final List examApplicationRequests = command.applicationRequest() .examApplication(); final Long applicationId = command.applicationId(); + final Long userId = command.userId(); List examApplicationEntities = examApplicationService.register( RegisterExamApplicationEvent.of(examApplicationRequests, - applicationId) + applicationId, userId) ); // 시험 신청 목록과 과목 multi-insert diff --git a/src/main/java/life/mosu/mosuserver/application/application/vaildator/ApplicationValidator.java b/src/main/java/life/mosu/mosuserver/application/application/vaildator/ApplicationValidator.java index 720d64e3..e94d729d 100644 --- a/src/main/java/life/mosu/mosuserver/application/application/vaildator/ApplicationValidator.java +++ b/src/main/java/life/mosu/mosuserver/application/application/vaildator/ApplicationValidator.java @@ -21,14 +21,14 @@ public class ApplicationValidator { private final ExamJpaRepository examJpaRepository; private final ApplicationJpaRepository applicationJpaRepository; - public void RequestNoDuplicateExams(List examIds) { + public void requestNoDuplicateExams(List examIds) { Set examIdSet = new HashSet<>(examIds); if (examIds.size() != examIdSet.size()) { throw new CustomRuntimeException(ErrorCode.EXAM_DUPLICATED); } } - public void ExamIdsAndLunchSelection(List requests) { + public void examIdsAndLunchSelection(List requests) { if (requests == null || requests.isEmpty()) { throw new CustomRuntimeException(ErrorCode.EXAM_APPLICATION_NOT_FOUND); } @@ -43,10 +43,10 @@ public void ExamIdsAndLunchSelection(List requests) { throw new CustomRuntimeException(ErrorCode.EXAM_NOT_FOUND); } - LunchSelection(requests, existingExams); + lunchSelection(requests, existingExams); } - private void LunchSelection(List requests, + private void lunchSelection(List requests, List exams) { Set examsWithoutLunch = exams.stream() .filter(ExamJpaEntity::hasNotLunch) @@ -61,7 +61,7 @@ private void LunchSelection(List requests, } } - public void NoDuplicateApplication(Long userId, List examIds) { + public void noDuplicateApplication(Long userId, List examIds) { boolean alreadyApplied = applicationJpaRepository.existsByUserIdAndExamIds(userId, examIds); if (alreadyApplied) { throw new CustomRuntimeException(ErrorCode.APPLICATION_SCHOOL_DUPLICATED); diff --git a/src/main/java/life/mosu/mosuserver/application/examapplication/ExamApplicationService.java b/src/main/java/life/mosu/mosuserver/application/examapplication/ExamApplicationService.java index ccce52a4..6907809c 100644 --- a/src/main/java/life/mosu/mosuserver/application/examapplication/ExamApplicationService.java +++ b/src/main/java/life/mosu/mosuserver/application/examapplication/ExamApplicationService.java @@ -1,6 +1,7 @@ package life.mosu.mosuserver.application.examapplication; -import java.util.HashSet; +import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -15,6 +16,7 @@ import life.mosu.mosuserver.domain.examapplication.projection.ExamApplicationInfoProjection; import life.mosu.mosuserver.domain.examapplication.projection.ExamTicketInfoProjection; import life.mosu.mosuserver.domain.examapplication.service.ExamNumberGenerationService; +import life.mosu.mosuserver.domain.payment.PaymentJpaRepository; import life.mosu.mosuserver.global.exception.CustomRuntimeException; import life.mosu.mosuserver.global.exception.ErrorCode; import life.mosu.mosuserver.infra.persistence.s3.S3Service; @@ -23,10 +25,12 @@ import life.mosu.mosuserver.presentation.examapplication.dto.ExamApplicationInfoResponse; import life.mosu.mosuserver.presentation.examapplication.dto.UpdateSubjectRequest; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; +@Slf4j @Service @RequiredArgsConstructor public class ExamApplicationService { @@ -35,6 +39,7 @@ public class ExamApplicationService { private final ApplicationJpaRepository applicationJpaRepository; private final ExamSubjectJpaRepository examSubjectJpaRepository; private final ExamNumberGenerationService examNumberGenerationService; + private final PaymentJpaRepository paymentJpaRepository; private final S3Service s3Service; private final FixedQuantityDiscountCalculator calculator; @@ -47,62 +52,44 @@ public List register(RegisterExamApplicationEvent even } @Transactional - public ExamApplicationInfoResponse updateSubjects(Long examApplicationId, + public void updateSubjects(Long userId, Long examApplicationId, UpdateSubjectRequest request) { - examSubjectJpaRepository.deleteByExamApplicationId(examApplicationId); + validateUser(userId, examApplicationId); + examSubjectJpaRepository.deleteExamSubjectsWithDonePayment(examApplicationId); List examSubjects = request.toEntityList(examApplicationId); examSubjectJpaRepository.saveAll(examSubjects); - ExamApplicationInfoProjection examApplicationInfo = examApplicationJpaRepository - .findExamApplicationInfoById(examApplicationId) - .orElseThrow( - () -> new CustomRuntimeException(ErrorCode.EXAM_APPLICATION_NOT_FOUND)); - - Integer totalAmount = examApplicationInfo.paymentAmount().getTotalAmount(); - Integer discountAmount = getAppliedDiscountAmount(totalAmount); - AddressResponse address = AddressResponse.from(examApplicationInfo.address()); - Set subjectSet = new HashSet<>(request.subjects()); - - return ExamApplicationInfoResponse.of( - examApplicationInfo.examApplicationId(), - examApplicationInfo.paymentKey(), - examApplicationInfo.examDate(), - examApplicationInfo.schoolName(), - address, - subjectSet, - examApplicationInfo.lunchName(), - totalAmount, - discountAmount, - examApplicationInfo.paymentMethod().getName() - ); } @Transactional(propagation = Propagation.REQUIRES_NEW) - public void deleteExamApplication(Long examApplicationId) { + public void deleteExamApplication(Long userId, Long examApplicationId) { + validateUser(userId, examApplicationId); + ExamApplicationJpaEntity examApplication = examApplicationJpaRepository.findById( examApplicationId) .orElseThrow( () -> new CustomRuntimeException(ErrorCode.EXAM_APPLICATION_NOT_FOUND)); Long applicationId = examApplication.getApplicationId(); - examApplicationJpaRepository.deleteById(examApplicationId); + examApplicationJpaRepository.updateDeleteById(examApplicationId); if (!examApplicationJpaRepository.existsByApplicationId(applicationId)) { - applicationJpaRepository.deleteById(applicationId); + applicationJpaRepository.deleteWithExamTicketById(applicationId); } examSubjectJpaRepository.deleteByExamApplicationId(examApplicationId); - } @Transactional - public ExamTicketResponse getExamTicket(Long examApplicationId) { + public ExamTicketResponse getExamTicket(Long userId, Long examApplicationId) { ExamTicketInfoProjection examTicketInfo = examApplicationJpaRepository.findExamTicketInfoProjectionById( - examApplicationId) + userId, examApplicationId) .orElseThrow( - () -> new CustomRuntimeException(ErrorCode.EXAM_APPLICATION_NOT_FOUND)); + () -> new CustomRuntimeException(ErrorCode.EXAM_RESOURCE_ACCESS_DENIED)); + + validateExamTicketOpenDate(examTicketInfo.examDate(), examTicketInfo.examNumber()); List examSubjects = examSubjectJpaRepository.findByExamApplicationId( examApplicationId); @@ -112,18 +99,35 @@ public ExamTicketResponse getExamTicket(Long examApplicationId) { .map(Subject::getSubjectName) .toList(); - String examTicketImgUrl = s3Service.getPreSignedUrl(examTicketInfo.s3Key()); + String s3Key = examTicketInfo.s3Key(); + String examTicketImgUrl = null; + + if (s3Key != null) { + examTicketImgUrl = s3Service.getPreSignedUrl(s3Key); + } + return ExamTicketResponse.of(examTicketImgUrl, examTicketInfo.userName(), examTicketInfo.birth(), examTicketInfo.examNumber(), subjects, examTicketInfo.schoolName()); } - //TODO: 테스트 필요 - public ExamApplicationInfoResponse getApplication(Long examApplicationId) { + + public ExamApplicationInfoResponse getApplication(Long userId, Long examApplicationId, + Long applicationId) { + validateUser(userId, examApplicationId); + + //상세 조회는 done 만 가능 +// Integer examApplicationCount = paymentJpaRepository.countByExamApplicationId( +// examApplicationId); + List examApplicationEntities = examApplicationJpaRepository.findByApplicationId( + applicationId); + Integer lunchCount = (int) examApplicationEntities.stream() + .filter(ExamApplicationJpaEntity::getIsLunchChecked) + .count(); ExamApplicationInfoProjection examApplicationInfo = examApplicationJpaRepository - .findExamApplicationInfoById(examApplicationId) + .findExamApplicationInfoById(userId, examApplicationId) .orElseThrow( () -> new CustomRuntimeException(ErrorCode.EXAM_APPLICATION_NOT_FOUND)); @@ -131,12 +135,15 @@ public ExamApplicationInfoResponse getApplication(Long examApplicationId) { examSubjectJpaRepository.findByExamApplicationId(examApplicationId); Set subjects = examSubjects.stream() - .map(examSubject -> examSubject.getSubject() - .getSubjectName()) + .map(ExamSubjectJpaEntity::getSubjectName) .collect(Collectors.toSet()); + //totalAmount 는 Lunch 가격이 포함되었을 수도 있음 + //totalAmount - Lunch 가격으로 getAppliedDiscountAmount() 메소드에 넣어야함. Integer totalAmount = examApplicationInfo.paymentAmount().getTotalAmount(); - Integer discountAmount = getAppliedDiscountAmount(totalAmount); + Integer discountAmount = getAppliedDiscountAmount( + lunchCount > 0 ? totalAmount - (9000 * lunchCount) + : totalAmount); Integer paymentAmount = examApplicationInfo.paymentAmount().getTotalAmount() + discountAmount; @@ -155,7 +162,28 @@ public ExamApplicationInfoResponse getApplication(Long examApplicationId) { ); } + private void validateUser(Long userId, Long examApplicationId) { + boolean check = examApplicationJpaRepository.existByUserIdAndExamApplicationId( + userId, + examApplicationId); + + if (!check) { + throw new CustomRuntimeException(ErrorCode.USER_NOT_ACCESS_FORBIDDEN); + } + } + private int getAppliedDiscountAmount(Integer totalAmount) { + log.info("total amount: {}", totalAmount); return calculator.getAppliedDiscountAmount(totalAmount); } + + private void validateExamTicketOpenDate(LocalDate examDate, String examNumber) { + + LocalDateTime openDateTime = examDate.minusDays(3).atTime(8, 0); + LocalDateTime now = LocalDateTime.now(); + + if (examNumber == null || now.isBefore(openDateTime)) { + throw new CustomRuntimeException(ErrorCode.EXAM_TICKET_NOT_OPEN); + } + } } diff --git a/src/main/java/life/mosu/mosuserver/application/examapplication/dto/RegisterExamApplicationEvent.java b/src/main/java/life/mosu/mosuserver/application/examapplication/dto/RegisterExamApplicationEvent.java index 593f8dd6..ad6b4898 100644 --- a/src/main/java/life/mosu/mosuserver/application/examapplication/dto/RegisterExamApplicationEvent.java +++ b/src/main/java/life/mosu/mosuserver/application/examapplication/dto/RegisterExamApplicationEvent.java @@ -6,23 +6,26 @@ public record RegisterExamApplicationEvent( List targetExams, - Long applicationId + Long applicationId, + Long userId ) { public static RegisterExamApplicationEvent of( List examApplicationRequests, - Long applicationId + Long applicationId, + Long userId ) { List targetExams = examApplicationRequests.stream() .map(request -> new TargetExam(request.examId(), request.isLunchChecked())) .toList(); - return new RegisterExamApplicationEvent(targetExams, applicationId); + return new RegisterExamApplicationEvent(targetExams, applicationId, userId); } public List toEntity() { return targetExams.stream() .map(targetExam -> ExamApplicationJpaEntity.create( applicationId, + userId, targetExam.examId(), targetExam.isLunchChecked() )) diff --git a/src/main/java/life/mosu/mosuserver/application/payment/PaymentService.java b/src/main/java/life/mosu/mosuserver/application/payment/PaymentService.java index 7e54ceea..f38ef30e 100644 --- a/src/main/java/life/mosu/mosuserver/application/payment/PaymentService.java +++ b/src/main/java/life/mosu/mosuserver/application/payment/PaymentService.java @@ -49,6 +49,7 @@ public void confirm(PaymentRequest request) { List applications = getValidApplications(request.applicationId()); String orderId = request.orderId(); int lunchAmount = amountCalculator.calculateLunchAmount(applications); + Long applicationId = request.applicationId(); List examApplicationIds = applications.stream() .map(ExamApplicationJpaEntity::getId) .toList(); @@ -60,6 +61,7 @@ public void confirm(PaymentRequest request) { ConfirmTossPaymentResponse tossResponse = tossProcessor.process(request); List payments = paymentMapper.toEntities( + applicationId, examApplicationIds, tossResponse ); diff --git a/src/main/java/life/mosu/mosuserver/domain/application/ApplicationJpaEntity.java b/src/main/java/life/mosu/mosuserver/domain/application/ApplicationJpaEntity.java index 58bb855f..4161aa84 100644 --- a/src/main/java/life/mosu/mosuserver/domain/application/ApplicationJpaEntity.java +++ b/src/main/java/life/mosu/mosuserver/domain/application/ApplicationJpaEntity.java @@ -11,11 +11,13 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.hibernate.annotations.SoftDelete; @Entity @Table(name = "application") @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) +@SoftDelete public class ApplicationJpaEntity extends BaseTimeEntity { @Id diff --git a/src/main/java/life/mosu/mosuserver/domain/application/ApplicationJpaRepository.java b/src/main/java/life/mosu/mosuserver/domain/application/ApplicationJpaRepository.java index 2d6b39d6..ed1a8520 100644 --- a/src/main/java/life/mosu/mosuserver/domain/application/ApplicationJpaRepository.java +++ b/src/main/java/life/mosu/mosuserver/domain/application/ApplicationJpaRepository.java @@ -2,12 +2,20 @@ import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; public interface ApplicationJpaRepository extends JpaRepository { - List findAllByUserId(Long userId); + @Query(""" + SELECT a + FROM ApplicationJpaEntity a + JOIN PaymentJpaEntity p ON p.applicationId=a.id + AND a.userId = :userId + AND p.deleted = false + """) + List findAllByUserId(@Param("userId") Long userId); @Query( """ @@ -23,4 +31,14 @@ SELECT CASE WHEN COUNT(a) > 0 THEN true ELSE false END ) boolean existsByUserIdAndExamIds(@Param("userId") Long userId, @Param("examIds") List examIds); + + + @Modifying + @Query(value = """ + UPDATE application a + JOIN exam_ticket_image et ON a.application_id = et.application_id + SET a.deleted = true, et.deleted = true + WHERE a.application_id = :applicationId + """, nativeQuery = true) + void deleteWithExamTicketById(Long applicationId); } diff --git a/src/main/java/life/mosu/mosuserver/domain/application/ExamTicketImageJpaEntity.java b/src/main/java/life/mosu/mosuserver/domain/application/ExamTicketImageJpaEntity.java index 1aca9ed4..1fdef756 100644 --- a/src/main/java/life/mosu/mosuserver/domain/application/ExamTicketImageJpaEntity.java +++ b/src/main/java/life/mosu/mosuserver/domain/application/ExamTicketImageJpaEntity.java @@ -11,14 +11,12 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import org.hibernate.annotations.SoftDelete; @Getter @Entity @Table(name = "exam_ticket_image") @NoArgsConstructor(access = lombok.AccessLevel.PROTECTED) -@SoftDelete public class ExamTicketImageJpaEntity extends File { @Id diff --git a/src/main/java/life/mosu/mosuserver/domain/banner/BannerJpaEntity.java b/src/main/java/life/mosu/mosuserver/domain/banner/BannerJpaEntity.java index 77f94c3b..03f3f921 100644 --- a/src/main/java/life/mosu/mosuserver/domain/banner/BannerJpaEntity.java +++ b/src/main/java/life/mosu/mosuserver/domain/banner/BannerJpaEntity.java @@ -7,18 +7,20 @@ import jakarta.persistence.Id; import jakarta.persistence.Table; import java.time.LocalDateTime; -import life.mosu.mosuserver.domain.file.File; +import life.mosu.mosuserver.domain.file.FileWithTime; import life.mosu.mosuserver.domain.file.Visibility; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.hibernate.annotations.SoftDelete; @Entity @Table(name = "banner") @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class BannerJpaEntity extends File { +@SoftDelete +public class BannerJpaEntity extends FileWithTime { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/life/mosu/mosuserver/domain/base/BaseDeleteEntity.java b/src/main/java/life/mosu/mosuserver/domain/base/BaseDeleteEntity.java new file mode 100644 index 00000000..d9c9a125 --- /dev/null +++ b/src/main/java/life/mosu/mosuserver/domain/base/BaseDeleteEntity.java @@ -0,0 +1,15 @@ +package life.mosu.mosuserver.domain.base; + + +import jakarta.persistence.Column; +import jakarta.persistence.MappedSuperclass; +import lombok.Getter; + +@Getter +@MappedSuperclass +public abstract class BaseDeleteEntity { + + @Column(name = "deleted") + private Boolean deleted = false; + +} \ No newline at end of file diff --git a/src/main/java/life/mosu/mosuserver/domain/base/BaseTimeEntity.java b/src/main/java/life/mosu/mosuserver/domain/base/BaseTimeEntity.java index 8862c638..1711cb58 100644 --- a/src/main/java/life/mosu/mosuserver/domain/base/BaseTimeEntity.java +++ b/src/main/java/life/mosu/mosuserver/domain/base/BaseTimeEntity.java @@ -23,7 +23,10 @@ public abstract class BaseTimeEntity { @Column(name = "updated_at") private LocalDateTime updatedAt; - public static String formatDate(java.time.LocalDateTime dateTime) { + @Column(name = "is_deleted", nullable = false) + private Boolean deleted = false; + + public static String formatDate(LocalDateTime dateTime) { return dateTime != null ? dateTime.toLocalDate().toString() : null; } diff --git a/src/main/java/life/mosu/mosuserver/domain/exam/ExamJpaRepository.java b/src/main/java/life/mosu/mosuserver/domain/exam/ExamJpaRepository.java index 64787517..cfb510ed 100644 --- a/src/main/java/life/mosu/mosuserver/domain/exam/ExamJpaRepository.java +++ b/src/main/java/life/mosu/mosuserver/domain/exam/ExamJpaRepository.java @@ -3,17 +3,33 @@ import io.lettuce.core.dynamic.annotation.Param; import java.time.LocalDate; import java.util.List; +import java.util.Optional; import life.mosu.mosuserver.domain.exam.projection.SchoolExamCountProjection; +import life.mosu.mosuserver.domain.examapplication.ExamInfoProjection; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; public interface ExamJpaRepository extends JpaRepository { - List findByArea(Area area); +// List findByArea(Area area); + @Query("SELECT DISTINCT e.area FROM ExamJpaEntity e") List findDistinctAreas(); + @Query(""" + SELECT new life.mosu.mosuserver.domain.examapplication.ExamInfoProjection( + e.examDate, + ea.examNumber, + e.schoolName + ) + FROM ExamApplicationJpaEntity ea + JOIN ExamJpaEntity e ON ea.examId = e.id + WHERE ea.examId = :examId + """) + Optional findExamInfo(@Param("examId") Long examId); + + @Query(""" SELECT e FROM ExamJpaEntity e @@ -33,4 +49,6 @@ public interface ExamJpaRepository extends JpaRepository { List countApplicationsGroupedBySchoolName(); List findByIdIn(List examIds); + + List findByArea(Area area); } diff --git a/src/main/java/life/mosu/mosuserver/domain/examapplication/ExamApplicationJpaEntity.java b/src/main/java/life/mosu/mosuserver/domain/examapplication/ExamApplicationJpaEntity.java index 2067cf35..5ceb84e4 100644 --- a/src/main/java/life/mosu/mosuserver/domain/examapplication/ExamApplicationJpaEntity.java +++ b/src/main/java/life/mosu/mosuserver/domain/examapplication/ExamApplicationJpaEntity.java @@ -27,6 +27,9 @@ public class ExamApplicationJpaEntity extends BaseTimeEntity { @Column(name = "application_id") private Long applicationId; + @Column(name = "user_id") + private Long userId; + @Column(name = "exam_id") private Long examId; @@ -36,26 +39,32 @@ public class ExamApplicationJpaEntity extends BaseTimeEntity { @Column(name = "exam_number") private String examNumber; - @Column(name = "deleted") - private Boolean isDeleted = false; - @Builder - public ExamApplicationJpaEntity(Long applicationId, Long examId, + public ExamApplicationJpaEntity( + Long applicationId, + Long userId, + Long examId, Boolean isLunchChecked) { this.applicationId = applicationId; + this.userId = userId; this.examId = examId; this.isLunchChecked = isLunchChecked; } public static ExamApplicationJpaEntity create( Long applicationId, + Long userId, Long examId, Boolean isLunchChecked) { - return ExamApplicationJpaEntity.builder() + ExamApplicationJpaEntity e = ExamApplicationJpaEntity.builder() .applicationId(applicationId) + .userId(userId) .examId(examId) .isLunchChecked(isLunchChecked) .build(); + + log.info("check : {}", e.getDeleted()); + return e; } public void grantExamNumber(String examNumber) { diff --git a/src/main/java/life/mosu/mosuserver/domain/examapplication/ExamApplicationJpaRepository.java b/src/main/java/life/mosu/mosuserver/domain/examapplication/ExamApplicationJpaRepository.java index a6baf14c..bf683038 100644 --- a/src/main/java/life/mosu/mosuserver/domain/examapplication/ExamApplicationJpaRepository.java +++ b/src/main/java/life/mosu/mosuserver/domain/examapplication/ExamApplicationJpaRepository.java @@ -8,11 +8,19 @@ import life.mosu.mosuserver.domain.examapplication.projection.ExamInfoWithExamNumberProjection; import life.mosu.mosuserver.domain.examapplication.projection.ExamTicketInfoProjection; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; public interface ExamApplicationJpaRepository extends JpaRepository { + @Modifying + @Query(""" + UPDATE ExamApplicationJpaEntity e + SET e.deleted = true + WHERE e.id = :examApplicationId + """) + void updateDeleteById(@Param("examApplicationId") Long examApplicationId); List findByApplicationId(Long applicationId); @@ -34,7 +42,8 @@ public interface ExamApplicationJpaRepository extends WHERE ea.id = :examApplicationId AND p.paymentStatus = 'DONE' """) - Optional findExamApplicationInfoById(Long examApplicationId); + Optional findExamApplicationInfoById(Long userId, + Long examApplicationId); @Query(""" @@ -43,17 +52,22 @@ public interface ExamApplicationJpaRepository extends u.name, u.birth, ea.examNumber, - e.schoolName + e.schoolName, + e.examDate ) FROM ExamApplicationJpaEntity ea LEFT JOIN ExamJpaEntity e on ea.examId = e.id LEFT JOIN ApplicationJpaEntity a on a.id = ea.applicationId LEFT JOIN ExamTicketImageJpaEntity et on et.applicationId = a.id LEFT JOIN UserJpaEntity u on a.userId = u.id + LEFT JOIN PaymentJpaEntity p on p.examApplicationId = ea.id WHERE ea.id = :examApplicationId + AND u.id = :userId + AND p.paymentStatus = 'DONE' """) Optional findExamTicketInfoProjectionById( - Long examApplicationId); + @Param("userId") Long userId, + @Param("examApplicationId") Long examApplicationId); boolean existsByApplicationId(Long applicationId); @@ -108,4 +122,14 @@ Optional findExamTicketInfoProjectionById( Optional findExamInfoWithExamNumber( @Param("examApplicationId") Long examApplicationId); + @Query(""" + SELECT case when COUNT(ea) > 0 then true else false end + FROM ExamApplicationJpaEntity ea + JOIN PaymentJpaEntity p ON p.examApplicationId = ea.id + WHERE ea.id = :examApplicationId + AND ea.userId = :userId + AND p.paymentStatus = 'DONE' + """) + boolean existByUserIdAndExamApplicationId(@Param("userId") Long userId, + @Param("examApplicationId") Long examApplicationId); } \ No newline at end of file diff --git a/src/main/java/life/mosu/mosuserver/domain/examapplication/ExamSubjectJpaEntity.java b/src/main/java/life/mosu/mosuserver/domain/examapplication/ExamSubjectJpaEntity.java index df6eb494..869cc4ea 100644 --- a/src/main/java/life/mosu/mosuserver/domain/examapplication/ExamSubjectJpaEntity.java +++ b/src/main/java/life/mosu/mosuserver/domain/examapplication/ExamSubjectJpaEntity.java @@ -9,6 +9,7 @@ import jakarta.persistence.Id; import jakarta.persistence.Table; import life.mosu.mosuserver.domain.application.Subject; +import life.mosu.mosuserver.domain.base.BaseDeleteEntity; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -18,7 +19,7 @@ @Entity @Table(name = "exam_subject") @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class ExamSubjectJpaEntity { +public class ExamSubjectJpaEntity extends BaseDeleteEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -39,4 +40,8 @@ public ExamSubjectJpaEntity(Long examApplicationId, Subject subject) { public static ExamSubjectJpaEntity create(Long examApplicationId, Subject subject) { return new ExamSubjectJpaEntity(examApplicationId, subject); } + + public String getSubjectName() { + return subject.getSubjectName(); + } } diff --git a/src/main/java/life/mosu/mosuserver/domain/examapplication/ExamSubjectJpaRepository.java b/src/main/java/life/mosu/mosuserver/domain/examapplication/ExamSubjectJpaRepository.java index cd953daf..d758c641 100644 --- a/src/main/java/life/mosu/mosuserver/domain/examapplication/ExamSubjectJpaRepository.java +++ b/src/main/java/life/mosu/mosuserver/domain/examapplication/ExamSubjectJpaRepository.java @@ -1,13 +1,35 @@ package life.mosu.mosuserver.domain.examapplication; +import io.lettuce.core.dynamic.annotation.Param; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; public interface ExamSubjectJpaRepository extends JpaRepository { List findByExamApplicationId(Long examApplicationId); - void deleteByExamApplicationId(Long examApplicationId); + @Modifying + @Query(value = """ + UPDATE exam_subject es + JOIN payment p ON es.exam_application_id = p.exam_application_id + SET es.deleted = true + WHERE p.status = 'DONE' + AND p.exam_application_id = :examApplicationId + """, nativeQuery = true) + void deleteByExamApplicationId(@Param("examApplicationId") Long examApplicationId); List findByExamApplicationIdIn(List examApplicationIds); + + @Modifying + @Query(value = """ + DELETE es + FROM exam_subject es + JOIN payment p ON es.exam_application_id = p.exam_application_id + WHERE p.status = 'DONE' + AND p.exam_application_id = :examApplicationId + """, nativeQuery = true) + void deleteExamSubjectsWithDonePayment(@Param("examApplicationId") Long examApplicationId); + } diff --git a/src/main/java/life/mosu/mosuserver/domain/examapplication/projection/ExamTicketInfoProjection.java b/src/main/java/life/mosu/mosuserver/domain/examapplication/projection/ExamTicketInfoProjection.java index a3b2686b..399dab68 100644 --- a/src/main/java/life/mosu/mosuserver/domain/examapplication/projection/ExamTicketInfoProjection.java +++ b/src/main/java/life/mosu/mosuserver/domain/examapplication/projection/ExamTicketInfoProjection.java @@ -7,7 +7,8 @@ public record ExamTicketInfoProjection( String userName, LocalDate birth, String examNumber, - String schoolName + String schoolName, + LocalDate examDate ) { } diff --git a/src/main/java/life/mosu/mosuserver/domain/file/File.java b/src/main/java/life/mosu/mosuserver/domain/file/File.java index b53bf14e..47fce8b3 100644 --- a/src/main/java/life/mosu/mosuserver/domain/file/File.java +++ b/src/main/java/life/mosu/mosuserver/domain/file/File.java @@ -4,7 +4,7 @@ import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; import jakarta.persistence.MappedSuperclass; -import life.mosu.mosuserver.domain.base.BaseTimeEntity; +import life.mosu.mosuserver.domain.base.BaseDeleteEntity; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -12,7 +12,7 @@ @Getter @MappedSuperclass @NoArgsConstructor(access = AccessLevel.PROTECTED) -public abstract class File extends BaseTimeEntity { +public abstract class File extends BaseDeleteEntity { @Column private String fileName; diff --git a/src/main/java/life/mosu/mosuserver/domain/file/FileWithTime.java b/src/main/java/life/mosu/mosuserver/domain/file/FileWithTime.java new file mode 100644 index 00000000..18618214 --- /dev/null +++ b/src/main/java/life/mosu/mosuserver/domain/file/FileWithTime.java @@ -0,0 +1,36 @@ +package life.mosu.mosuserver.domain.file; + +import jakarta.persistence.Column; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.MappedSuperclass; +import life.mosu.mosuserver.domain.base.BaseTimeEntity; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@MappedSuperclass +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public abstract class FileWithTime extends BaseTimeEntity { + + @Column + private String fileName; + + @Column + private String s3Key; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private Visibility visibility; + + protected FileWithTime(String fileName, String s3Key, Visibility visibility) { + this.fileName = fileName; + this.s3Key = s3Key; + this.visibility = visibility; + } + + public boolean isPublic() { + return this.visibility.equals(Visibility.PUBLIC); + } +} \ No newline at end of file diff --git a/src/main/java/life/mosu/mosuserver/domain/payment/PaymentJpaEntity.java b/src/main/java/life/mosu/mosuserver/domain/payment/PaymentJpaEntity.java index 0e7ace3b..c8d1c4e1 100644 --- a/src/main/java/life/mosu/mosuserver/domain/payment/PaymentJpaEntity.java +++ b/src/main/java/life/mosu/mosuserver/domain/payment/PaymentJpaEntity.java @@ -29,6 +29,9 @@ public class PaymentJpaEntity extends BaseTimeEntity { @Column(name = "exam_application_id") private Long examApplicationId; + @Column(name = "application_id") + private Long applicationId; + @Column(name = "payment_key") private String paymentKey; @@ -49,6 +52,7 @@ public class PaymentJpaEntity extends BaseTimeEntity { @Builder(access = AccessLevel.PRIVATE) private PaymentJpaEntity( Long examApplicationId, + Long applicationId, String paymentKey, String orderId, PaymentAmountVO paymentAmount, @@ -56,6 +60,7 @@ private PaymentJpaEntity( PaymentMethod paymentMethod ) { this.examApplicationId = examApplicationId; + this.applicationId = applicationId; this.paymentKey = paymentKey; this.orderId = orderId; this.paymentAmount = paymentAmount; @@ -65,6 +70,7 @@ private PaymentJpaEntity( public static PaymentJpaEntity of( Long examApplicationId, + Long applicationId, String paymentKey, String orderId, PaymentStatus paymentStatus, @@ -73,6 +79,7 @@ public static PaymentJpaEntity of( ) { return PaymentJpaEntity.builder() .examApplicationId(examApplicationId) + .applicationId(applicationId) .paymentKey(paymentKey) .orderId(orderId) .paymentStatus(paymentStatus) diff --git a/src/main/java/life/mosu/mosuserver/domain/payment/PaymentJpaRepository.java b/src/main/java/life/mosu/mosuserver/domain/payment/PaymentJpaRepository.java index c8053337..817a797f 100644 --- a/src/main/java/life/mosu/mosuserver/domain/payment/PaymentJpaRepository.java +++ b/src/main/java/life/mosu/mosuserver/domain/payment/PaymentJpaRepository.java @@ -11,8 +11,6 @@ public interface PaymentJpaRepository extends JpaRepository findByOrderId(String orderId); - PaymentJpaEntity findByExamApplicationId(Long examApplicationId); - List findByExamApplicationIdIn(List examApplicationIds); @Query( diff --git a/src/main/java/life/mosu/mosuserver/domain/payment/service/PaymentMapper.java b/src/main/java/life/mosu/mosuserver/domain/payment/service/PaymentMapper.java index 3e33032b..f0ca5197 100644 --- a/src/main/java/life/mosu/mosuserver/domain/payment/service/PaymentMapper.java +++ b/src/main/java/life/mosu/mosuserver/domain/payment/service/PaymentMapper.java @@ -8,10 +8,10 @@ @Component public class PaymentMapper { - public List toEntities(List examApplicationIds, + public List toEntities(Long applicationId, List examApplicationIds, ConfirmTossPaymentResponse response) { return examApplicationIds.stream() - .map(response::toEntity) + .map(examApplicationId -> response.toEntity(applicationId, examApplicationId)) .toList(); } } diff --git a/src/main/java/life/mosu/mosuserver/domain/refund/RefundJpaEntity.java b/src/main/java/life/mosu/mosuserver/domain/refund/RefundJpaEntity.java index 6b2307df..8a6ccbdd 100644 --- a/src/main/java/life/mosu/mosuserver/domain/refund/RefundJpaEntity.java +++ b/src/main/java/life/mosu/mosuserver/domain/refund/RefundJpaEntity.java @@ -2,11 +2,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; -import java.time.LocalDateTime; import life.mosu.mosuserver.domain.base.BaseTimeEntity; import lombok.AccessLevel; import lombok.Builder; @@ -34,6 +35,7 @@ public class RefundJpaEntity extends BaseTimeEntity { private String reason; @Column(name = "refund_status") + @Enumerated(EnumType.STRING) private RefundStatus refundStatus; @Column(name = "refunded_amount") @@ -59,6 +61,7 @@ public RefundJpaEntity( this.refundedAmount = refundedAmount; this.refundableAmount = refundableAmount; } + public static RefundJpaEntity of( final Long examApplicationId, final String transactionKey, diff --git a/src/main/java/life/mosu/mosuserver/global/exception/ErrorCode.java b/src/main/java/life/mosu/mosuserver/global/exception/ErrorCode.java index c595fc51..8ed3c012 100644 --- a/src/main/java/life/mosu/mosuserver/global/exception/ErrorCode.java +++ b/src/main/java/life/mosu/mosuserver/global/exception/ErrorCode.java @@ -32,13 +32,17 @@ public enum ErrorCode { USER_ALREADY_EXISTS(HttpStatus.CONFLICT, "이미 존재하는 사용자입니다."), USER_NOT_FOUND(HttpStatus.NOT_FOUND, "사용자를 찾을 수 없습니다."), USER_INFO_INVALID(HttpStatus.BAD_REQUEST, "유효하지 않은 사용자 정보입니다."), + USER_NOT_ACCESS_FORBIDDEN(HttpStatus.BAD_REQUEST, "접근 권한이 없는 사용자입니다"), // 신청 관련 에러 WRONG_SUBJECT_TYPE(HttpStatus.BAD_REQUEST, "잘못된 과목명 입니다."), WRONG_LUNCH_TYPE(HttpStatus.BAD_REQUEST, "잘못된 도시락명 입니다."), WRONG_AREA_TYPE(HttpStatus.BAD_REQUEST, "잘못된 지역명 입니다."), WRONG_SUBJECT_COUNT(HttpStatus.BAD_REQUEST, "응시과목은 반드시 다른 과목 2개를 신청해야 합니다."), + // 수험표 관련 에러 + EXAM_TICKET_NOT_OPEN(HttpStatus.BAD_REQUEST, "수험표 조회 기간이 아닙니다."), + EXAM_RESOURCE_ACCESS_DENIED(HttpStatus.BAD_REQUEST, "수험표 접근을 허용할 수 없습니다."), // 신청 학교 관련 에러 EXAM_APPLICATION_NOT_FOUND(HttpStatus.NOT_FOUND, "신청한 학교 정보를 찾을 수 없습니다."), @@ -50,6 +54,7 @@ public enum ErrorCode { APPLICATION_SCHOOL_ALREADY_APPLIED(HttpStatus.CONFLICT, "해당 학교를 이미 예약하였습니다."), APPLICATION_SCHOOL_DUPLICATED(HttpStatus.BAD_REQUEST, "동일 일자의 같은 학교를 신청할 수 없습니다."), EXAM_DUPLICATED(HttpStatus.BAD_REQUEST, "동일한 시험을 신청할 수 없습니다."), + WRONG_APPLICATION_ID_TYPE(HttpStatus.BAD_REQUEST, "신청 ID 타입이 올바르지 않습니다."), // 프로필 관련 에러 PROFILE_ALREADY_EXISTS(HttpStatus.CONFLICT, "프로필이 이미 존재합니다."), PROFILE_NOT_FOUND(HttpStatus.NOT_FOUND, "프로필을 찾을 수 없습니다."), @@ -124,6 +129,7 @@ public enum ErrorCode { //결제 API 실패 PAYMENT_API_ERROR(HttpStatus.BAD_REQUEST, "결제 API 호출에 실패하였습니다."); + private final HttpStatus status; private final String message; diff --git a/src/main/java/life/mosu/mosuserver/global/initializer/DatabaseInitializer.java b/src/main/java/life/mosu/mosuserver/global/initializer/DatabaseInitializer.java index aa933940..bf50ecaa 100644 --- a/src/main/java/life/mosu/mosuserver/global/initializer/DatabaseInitializer.java +++ b/src/main/java/life/mosu/mosuserver/global/initializer/DatabaseInitializer.java @@ -2,7 +2,6 @@ import jakarta.annotation.PostConstruct; import java.time.LocalDate; -import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -46,6 +45,7 @@ import life.mosu.mosuserver.domain.refund.RefundJpaEntity; import life.mosu.mosuserver.domain.refund.RefundJpaRepository; import life.mosu.mosuserver.domain.refund.RefundStatus; +import life.mosu.mosuserver.domain.user.AuthProvider; import life.mosu.mosuserver.domain.user.UserJpaEntity; import life.mosu.mosuserver.domain.user.UserJpaRepository; import life.mosu.mosuserver.domain.user.UserRole; @@ -59,8 +59,8 @@ @RequiredArgsConstructor public class DatabaseInitializer { - // 모의고사 1회당 기본 응시료 - private static final int BASE_EXAM_FEE = 50000; + private static final int BASE_EXAM_FEE = 49_000; + private static final int LUNCH_PRICE = 9_000; private final UserJpaRepository userRepository; private final ProfileJpaRepository profileRepository; @@ -80,132 +80,97 @@ public class DatabaseInitializer { @PostConstruct public void init() { if (userRepository.count() > 0 || examRepository.count() > 0) { - log.info("이미 더미 데이터가 존재하여 전체 초기화를 건너뜁니다."); + log.info("이미 더미 데이터가 존재하여 초기화를 건너뜁니다."); return; } - log.info("전체 더미 데이터 초기화를 시작합니다... 🚀"); + log.info("데이터 초기화 시작 🚀"); Random random = new Random(); - // 1. 유저 및 프로필 생성 - List users = initializeUsersAndProfiles(random); + List users = createUsersAndProfiles(random); + List exams = createExams(); + createApplicationsAndPayments(users, exams, random); + createBoardItems(users, random); + createRefunds(); + createRecommendations(); - // 2. 시험 정보 생성 - List exams = initializeExams(); - - // 3. 유저별 시험 신청 및 결제 정보 생성 - initializeApplicationsAndPayments(users, exams, random); - - // 4. 게시판 관련 데이터 생성 (공지, 문의, 이벤트) - initializeBoardItems(users, random); - - initializePayments(); - - initializeRecommendation(); - - log.info("✅ 모든 더미 데이터 초기화가 완료되었습니다."); + log.info("✅ 데이터 초기화 완료"); } - private List initializeUsersAndProfiles(Random random) { - List createdUsers = new ArrayList<>(); + private List createUsersAndProfiles(Random random) { + List users = new ArrayList<>(); + for (int i = 1; i <= 10; i++) { UserJpaEntity user = UserJpaEntity.builder() - .loginId("user" + i) - .password(passwordEncoder.encode("password" + i + "!")) - .gender((i % 2 == 0) ? Gender.MALE : Gender.FEMALE) - .name((i % 2 == 0) ? "김모수" + i : "이모수" + i) - .birth(LocalDate.of(2005 + (i % 3), (i % 12) + 1, (i % 28) + 1)) + .loginId("userid" + i) + .password(passwordEncoder.encode("Password!" + i)) + .gender(i % 2 == 0 ? Gender.MALE : Gender.FEMALE) + .name("모수학생" + i) + .phoneNumber("0101234567" + i) + .birth(LocalDate.of(2005 + i % 3, (i % 12) + 1, (i % 28) + 1)) + .userRole(i == 1 ? UserRole.ROLE_ADMIN : UserRole.ROLE_USER) .agreedToTermsOfService(true) .agreedToPrivacyPolicy(true) .agreedToMarketing(random.nextBoolean()) - .userRole((i == 1) ? UserRole.ROLE_ADMIN : UserRole.ROLE_USER) + .provider(AuthProvider.MOSU) .build(); userRepository.save(user); - createdUsers.add(user); + users.add(user); ProfileJpaEntity profile = ProfileJpaEntity.builder() .userId(user.getId()) .userName(user.getName()) .gender(user.getGender()) .birth(user.getBirth()) - .phoneNumber("010-9161-2960") + .phoneNumber(user.getPhoneNumber()) .email("user" + i + "@mosu.life") .education(Education.ENROLLED) - .schoolInfo( - new SchoolInfoJpaVO( - "모수고등학교", - "서울시 모수구 모수동 123-45", - "12345" - ) - ) + .schoolInfo(new SchoolInfoJpaVO("모수고등학교", "서울시 모수구 123", "12345")) .grade(Grade.values()[random.nextInt(Grade.values().length)]) .build(); profileRepository.save(profile); } - log.info("👤 User 및 Profile 데이터 {}건 생성 완료.", createdUsers.size()); - return createdUsers; - } - private List initializeExams() { - // 사용자가 제공한 확정된 시험 정보로 수정 - // 수정 가능한 리스트로 만들기 위해 new ArrayList<>()로 감싸기 - List exams = new ArrayList<>(List.of( - // 대치중학교 - createExam("대치중학교", Area.DAECHI, "06234", "강남구 대치동 987", - LocalDate.of(2025, 10, 19), 532, "제육김치덮밥", 8000), + return users; + } - // 목운중학교 + private List createExams() { + List exams = List.of( + createExam("대치중학교", Area.DAECHI, "06234", "강남구 대치동 987", LocalDate.of(2025, 10, 19), + 532), createExam("목운중학교", Area.MOKDONG, "07995", "양천구 목동서로 369", - LocalDate.of(2025, 10, 26), 896, "함박스테이크", 9500), - - // 신서중학교 - createExam("신서중학교", Area.MOKDONG, "08018", "양천구 신정로 250", - LocalDate.of(2025, 11, 2), 896, "돈까스카레", 9000), - - // 개원중학교 (10/26 시험) - createExam("개원중학교", Area.DAECHI, "06327", "강남구 개포로 619", - LocalDate.of(2025, 10, 26), 840, "치킨마요덮밥", 8500), - // 개원중학교 (11/2 시험) - createExam("개원중학교", Area.DAECHI, "06327", "강남구 개포로 619", - LocalDate.of(2025, 11, 2), 840, "소불고기덮밥", 9000), - - // 문래중학교 + LocalDate.of(2025, 10, 26), 896), + createExam("신서중학교", Area.MOKDONG, "08018", "양천구 신정로 250", LocalDate.of(2025, 11, 2), + 896), + createExam("개원중학교", Area.DAECHI, "06327", "강남구 개포로 619", LocalDate.of(2025, 10, 26), + 840), createExam("문래중학교", Area.MOKDONG, "07291", "영등포구 문래로 195", - LocalDate.of(2025, 10, 19), 558, null, null), // 도시락 미제공 - - // 온곡중학교 (10/19 시험) - createExam("온곡중학교", Area.NOWON, "01673", "노원구 덕릉로 70길 99", - LocalDate.of(2025, 10, 19), 448, "유부초밥&우동", 7500), - // 온곡중학교 (11/2 시험) + LocalDate.of(2025, 10, 19), 558), createExam("온곡중학교", Area.NOWON, "01673", "노원구 덕릉로 70길 99", - LocalDate.of(2025, 11, 2), 448, "참치김치찌개", 8000) - )); - examRepository.saveAll(exams); - log.info("🏫 Exam 데이터 {}건 생성 완료.", exams.size()); - return exams; + LocalDate.of(2025, 11, 2), 448) + ); + return examRepository.saveAll(exams); } - private ExamJpaEntity createExam(String schoolName, Area area, String zipcode, String street, - LocalDate examDate, int capacity, String lunchName, Integer lunchPrice) { + private ExamJpaEntity createExam(String name, Area area, String zipcode, String street, + LocalDate date, int cap) { return ExamJpaEntity.builder() - .schoolName(schoolName) + .schoolName(name) .area(area) .address(new AddressJpaVO(zipcode, "서울특별시", street)) - .examDate(examDate) - .capacity(capacity) - .deadlineTime(examDate.minusDays(7).atTime(23, 59, 59)) - .lunchName(lunchName) - .lunchPrice(lunchPrice) + .examDate(date) + .capacity(cap) + .deadlineTime(date.atTime(23, 59, 59).minusDays(7)) + .lunchName("고정 도시락") + .lunchPrice(LUNCH_PRICE) .build(); } - private void initializeApplicationsAndPayments(List users, - List exams, Random random) { - int successfulPayments = users.size() / 2; - int paymentCounter = 0; + private void createApplicationsAndPayments(List users, List exams, + Random random) { + int counter = 0; for (UserJpaEntity user : users) { - // 1. 유저별로 하나의 신청(Application) 묶음 생성 ApplicationJpaEntity application = applicationRepository.save( ApplicationJpaEntity.builder() .userId(user.getId()) @@ -215,184 +180,132 @@ private void initializeApplicationsAndPayments(List users, .build() ); - // 2. 해당 신청에 1~3개의 시험 응시(ExamApplication)를 추가 Collections.shuffle(exams); - int examsToApplyCount = random.nextInt(3) + 1; // 1~3개 시험 신청 - int totalAmount = 0; - List currentExamApplications = new ArrayList<>(); - - for (int j = 0; j < Math.min(examsToApplyCount, exams.size()); j++) { - ExamJpaEntity exam = exams.get(j); - boolean isLunchChecked = (exam.getLunchPrice() != null) && random.nextBoolean(); - - totalAmount += BASE_EXAM_FEE; + int applyCount = random.nextInt(3) + 1; + List applied = new ArrayList<>(); + int lunchCount = 0; + + for (int i = 0; i < applyCount; i++) { + ExamJpaEntity exam = exams.get(i); + boolean lunch = random.nextBoolean(); + if (lunch) { + lunchCount++; + } - ExamApplicationJpaEntity examApplication = examApplicationRepository.save( - ExamApplicationJpaEntity.builder() - .applicationId(application.getId()) - .examId(exam.getId()) -// .examNumber(String.format("MOSU-%d-%d", application.getId(), -// exam.getId())) - .isLunchChecked( - ((j % 2 == 0) ? true : false) - ) - .build() + ExamApplicationJpaEntity examApp = examApplicationRepository.save( + ExamApplicationJpaEntity.create( + application.getId(), + user.getId(), + exam.getId(), + lunch + ) ); - currentExamApplications.add(examApplication); + applied.add(examApp); } - // 3. 각 시험 응시(ExamApplication)에 대한 선택 과목(ExamSubject) 생성 - for (ExamApplicationJpaEntity examApp : currentExamApplications) { + for (ExamApplicationJpaEntity app : applied) { Set subjects = new HashSet<>(); - int subjectCount = random.nextInt(2) + 1; // 1~2개 과목 선택 - while (subjects.size() < subjectCount) { + while (subjects.size() < (random.nextBoolean() ? 2 : 1)) { subjects.add(Subject.values()[random.nextInt(Subject.values().length)]); } - subjects.forEach(subject -> - examSubjectRepository.save( - new ExamSubjectJpaEntity(examApp.getId(), subject) - ) - ); + subjects.forEach(subject -> examSubjectRepository.save( + new ExamSubjectJpaEntity(app.getId(), subject))); } - // 4. 생성된 신청(Application) 묶음에 대한 결제(Payment) 정보 생성 - String orderId = "order-" + UUID.randomUUID().toString().substring(0, 12); - if (paymentCounter < successfulPayments) { - // 결제 성공 케이스 - PaymentJpaEntity payment = PaymentJpaEntity.of( + int baseTotal = switch (applyCount) { + case 1 -> 49_000; + case 2 -> 89_000; + case 3 -> 129_000; + default -> applyCount * BASE_EXAM_FEE; + }; + int total = baseTotal + (lunchCount * LUNCH_PRICE); + + for (ExamApplicationJpaEntity app : applied) { + paymentRepository.save(PaymentJpaEntity.of( + app.getId(), application.getId(), - "pkey-" + UUID.randomUUID().toString().substring(0, 18), - orderId, + "pkey-" + UUID.randomUUID(), + "order-" + UUID.randomUUID().toString().substring(0, 12), PaymentStatus.DONE, - PaymentAmountVO.of( - totalAmount, totalAmount, totalAmount, 0, 0 - ), - PaymentMethod.CARD - ); - paymentRepository.save(payment); - } else { - // 결제 실패 케이스 (취소, 시간 초과 등) - PaymentStatus failureStatus = - random.nextBoolean() ? PaymentStatus.ABORTED : PaymentStatus.EXPIRED; - PaymentJpaEntity payment = PaymentJpaEntity.of( - application.getId(), - "pkey-" + UUID.randomUUID().toString().substring(0, 18), - orderId, - failureStatus, - PaymentAmountVO.of( - totalAmount, totalAmount, totalAmount, 0, 0 - ), + PaymentAmountVO.of(total, total, total, 0, 0), PaymentMethod.CARD - ); - paymentRepository.save(payment); + )); + counter++; } - paymentCounter++; } - log.info("📝 Application, ExamApplication, ExamSubject, Payment 데이터 생성 완료 (성공: {}, 실패: {}).", - successfulPayments, users.size() - successfulPayments); } - private void initializeBoardItems(List users, Random random) { + private void createBoardItems(List users, Random random) { UserJpaEntity admin = users.stream().filter(u -> u.getUserRole() == UserRole.ROLE_ADMIN) .findFirst().orElse(users.get(0)); - // 공지사항 생성 - for (int i = 1; i <= 10; i++) { + for (int i = 1; i <= 5; i++) { noticeRepository.save(NoticeJpaEntity.builder() - .title("중요 공지사항 #" + i) - .content("제 " + i + "차 모의고사 관련 안내입니다. 내용을 필히 숙지해주시기 바랍니다.") + .title("공지사항 " + i) + .content("제 " + i + "회 모의고사 공지") .userId(admin.getId()) .author(admin.getName()) .build()); - } - - // 문의 및 답변 생성 - List inquiries = new ArrayList<>(); - for (int i = 1; i <= 10; i++) { - UserJpaEntity author = users.get(random.nextInt(users.size())); - InquiryJpaEntity inquiry = inquiryRepository.save( - InquiryJpaEntity.builder() - .title("결제 관련 문의 드립니다 (" + i + ")") - .content("안녕하세요. " + i + "번째 문의 내용입니다. 확인 부탁드립니다.") - .userId(author.getId()) - .author(author.getName()) - .build() - ); - inquiries.add(inquiry); - } - // 7개의 문의에만 답변 달기 - Collections.shuffle(inquiries); - for (int i = 0; i < 7; i++) { - InquiryJpaEntity inquiryToAnswer = inquiries.get(i); - InquiryAnswerJpaEntity inquiryAnswer = InquiryAnswerJpaEntity.builder() - .title("Re: " + inquiryToAnswer.getTitle()) - .content("문의하신 내용에 대한 답변입니다. 확인 후 추가 문의사항이 있으시면 다시 글 남겨주세요.") - .inquiryId(inquiryToAnswer.getId()) + InquiryJpaEntity inquiry = inquiryRepository.save(InquiryJpaEntity.builder() + .title("문의합니다 " + i) + .content("내용입니다 " + i) .userId(admin.getId()) - .build(); - inquiryAnswerRepository.save(inquiryAnswer); - inquiryToAnswer.updateStatusToComplete(); - inquiryRepository.save(inquiryToAnswer); - } + .author(admin.getName()) + .build()); + + if (random.nextBoolean()) { + inquiryAnswerRepository.save(InquiryAnswerJpaEntity.builder() + .title("Re: 문의합니다 " + i) + .content("답변드립니다.") + .inquiryId(inquiry.getId()) + .userId(admin.getId()) + .build()); + inquiry.updateStatusToComplete(); + inquiryRepository.save(inquiry); + } - // 이벤트 생성 - for (int i = 1; i <= 10; i++) { - LocalDate startDate = LocalDate.now().plusDays(i * 5L); - LocalDate endDate = startDate.plusDays(random.nextInt(10) + 5); eventRepository.save(EventJpaEntity.builder() - .title("여름방학 맞이 특별 이벤트 #" + i) - .duration(new DurationJpaVO(startDate, endDate)) + .title("이벤트 " + i) + .duration(new DurationJpaVO(LocalDate.now().plusDays(i), + LocalDate.now().plusDays(i + 5))) .eventLink("https://mosu.life/event/" + i) .build()); } - log.info("📋 Board(Notice, Inquiry, Event) 데이터 생성 완료."); } - private void initializePayments() { - List allExamApplications = examApplicationRepository.findAll(); - Random random = new Random(); - - int refundCount = 0; - - for (ExamApplicationJpaEntity examApp : allExamApplications) { - // 30% 확률로 환불 생성 - if (random.nextDouble() < 0.3) { - PaymentJpaEntity payment = PaymentJpaEntity.of( - examApp.getId(), - "paymentKey" + refundCount, - "orderId" + refundCount, - PaymentStatus.DONE, - PaymentAmountVO.builder().build(), - PaymentMethod.CARD - ); - paymentRepository.save(payment); - refundCount++; + private void createRefunds() { + List all = examApplicationRepository.findAll(); + for (ExamApplicationJpaEntity app : all) { + if (Math.random() < 0.3) { + refundRepository.save(RefundJpaEntity.of( + app.getId(), + "tranaction-key", + "단순 변심", + RefundStatus.DONE, + 1000, + 1000 + )); } } - log.info("💸 Refund 데이터 {}건 생성 완료.", refundCount); } - private void initializeRecommendation() { - List recommendations = List.of( + private void createRecommendations() { + recommendationRepository.saveAll(List.of( RecommendationJpaEntity.builder() .userId(1L) - .name("이영희") + .name("홍길동") .phoneNumber("01012345678") - .bank("신한은행") - .accountNumber("110123456789") + .bank("신한") + .accountNumber("110-123-456789") .build(), - RecommendationJpaEntity.builder() .userId(2L) .name("김철수") .phoneNumber("01098765432") - .bank("국민은행") - .accountNumber("12345678901234") + .bank("국민") + .accountNumber("123-456-789012") .build() - ); - - recommendationRepository.saveAll(recommendations); + )); } - -} \ No newline at end of file +} diff --git a/src/main/java/life/mosu/mosuserver/infra/payment/dto/ConfirmTossPaymentResponse.java b/src/main/java/life/mosu/mosuserver/infra/payment/dto/ConfirmTossPaymentResponse.java index 97fdde2e..9aeb8375 100644 --- a/src/main/java/life/mosu/mosuserver/infra/payment/dto/ConfirmTossPaymentResponse.java +++ b/src/main/java/life/mosu/mosuserver/infra/payment/dto/ConfirmTossPaymentResponse.java @@ -27,9 +27,10 @@ public class ConfirmTossPaymentResponse { private Integer taxFreeAmount; private String method; - public PaymentJpaEntity toEntity(Long examApplicationId) { + public PaymentJpaEntity toEntity(Long applicationId, Long examApplicationId) { return PaymentJpaEntity.of( examApplicationId, + applicationId, paymentKey, orderId, toPaymentStatus(), diff --git a/src/main/java/life/mosu/mosuserver/infra/persistence/jpa/ExamApplicationBulkRepository.java b/src/main/java/life/mosu/mosuserver/infra/persistence/jpa/ExamApplicationBulkRepository.java index 33bd7fe1..0916a409 100644 --- a/src/main/java/life/mosu/mosuserver/infra/persistence/jpa/ExamApplicationBulkRepository.java +++ b/src/main/java/life/mosu/mosuserver/infra/persistence/jpa/ExamApplicationBulkRepository.java @@ -29,8 +29,8 @@ public class ExamApplicationBulkRepository { private static final String SQL_INSERT_EXAM_APPLICATION = """ INSERT INTO exam_application (created_at, updated_at, application_id, - exam_id, lunch_checked, exam_number) - VALUES (?, ?, ?, ?, ?, ?) + user_id, exam_id, lunch_checked, exam_number, is_deleted) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) """; private static final String SQL_INSERT_EXAM_SUBJECT = """ INSERT INTO exam_subject (exam_application_id, subject) VALUES (?, ?) @@ -49,10 +49,21 @@ public List saveAllExamApplicationsWithSubjects( ps.setTimestamp(1, Timestamp.valueOf(LocalDateTime.now())); ps.setTimestamp(2, Timestamp.valueOf(LocalDateTime.now())); ps.setLong(3, e.getApplicationId()); - ps.setLong(4, e.getExamId()); - ps.setBoolean(5, e.getIsLunchChecked()); - ps.setString(6, e.getExamNumber()); + ps.setLong(4, e.getUserId()); + ps.setLong(5, e.getExamId()); + ps.setBoolean(6, e.getIsLunchChecked()); + ps.setString(7, e.getExamNumber()); + ps.setBoolean(8, e.getDeleted()); ps.addBatch(); + + log.info( + "Saving ExamApplication - applicationId: {}, userId: {}, examId: {}, isLunchChecked: {}, examNumber: {}", + e.getApplicationId(), + e.getUserId(), + e.getExamId(), + e.getIsLunchChecked(), + e.getExamNumber() + ); } ps.executeBatch(); diff --git a/src/main/java/life/mosu/mosuserver/presentation/application/ApplicationController.java b/src/main/java/life/mosu/mosuserver/presentation/application/ApplicationController.java index fcd9fc47..918e2bd6 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/application/ApplicationController.java +++ b/src/main/java/life/mosu/mosuserver/presentation/application/ApplicationController.java @@ -47,7 +47,6 @@ public ResponseEntity> apply( .body(ApiResponseWrapper.success(HttpStatus.OK, "신청 성공", response)); } - // TODO: 테스트 필요 //전체 신청 내역 조회 @GetMapping @PreAuthorize("isAuthenticated() and hasRole('USER')") diff --git a/src/main/java/life/mosu/mosuserver/presentation/application/dto/ExamWithSubjects.java b/src/main/java/life/mosu/mosuserver/presentation/application/dto/ExamWithSubjects.java deleted file mode 100644 index 84277eaf..00000000 --- a/src/main/java/life/mosu/mosuserver/presentation/application/dto/ExamWithSubjects.java +++ /dev/null @@ -1,3 +0,0 @@ -public class ExamWithSubjects { - -} diff --git a/src/main/java/life/mosu/mosuserver/presentation/auth/AuthController.java b/src/main/java/life/mosu/mosuserver/presentation/auth/AuthController.java index af5d98fb..db7397d3 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/auth/AuthController.java +++ b/src/main/java/life/mosu/mosuserver/presentation/auth/AuthController.java @@ -58,12 +58,12 @@ public ResponseEntity> reissueAccessToken( private HttpHeaders applyTokenHeader(Token token) { HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.SET_COOKIE, CookieBuilderUtil.temporaryCookie( + headers.add(HttpHeaders.SET_COOKIE, CookieBuilderUtil.createCookie( ACCESS_TOKEN_COOKIE_NAME, token.accessToken(), token.accessTokenExpireTime() )); - headers.add(HttpHeaders.SET_COOKIE, CookieBuilderUtil.temporaryCookie( + headers.add(HttpHeaders.SET_COOKIE, CookieBuilderUtil.createCookie( REFRESH_TOKEN_COOKIE_NAME, token.refreshToken(), token.refreshTokenExpireTime() diff --git a/src/main/java/life/mosu/mosuserver/presentation/exam/dto/ExamResponse.java b/src/main/java/life/mosu/mosuserver/presentation/exam/dto/ExamResponse.java index 770915c7..cd81764c 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/exam/dto/ExamResponse.java +++ b/src/main/java/life/mosu/mosuserver/presentation/exam/dto/ExamResponse.java @@ -20,6 +20,7 @@ public record ExamResponse( public static ExamResponse of(ExamJpaEntity exam, Long currentQuota) { AddressResponse address = AddressResponse.from(exam.getAddress()); LunchResponse lunch = LunchResponse.of(exam.getLunchName(), exam.getLunchPrice()); + return new ExamResponse( exam.getId(), exam.getSchoolName(), @@ -32,6 +33,33 @@ public static ExamResponse of(ExamJpaEntity exam, Long currentQuota) { lunch ); } +// +// public static List fromList(List foundExams) { +// return foundExams.stream() +// .map(ExamResponse::from) +// .toList(); +// } +// public static ExamResponse from(ExamWithLunchProjection exam) { +// AddressResponse address = AddressResponse.from(exam.address()); +// List lunchInfos = lunches.stream().map( +// lunch -> LunchInfo.of(lunch.getName(), lunch.getPrice()) +// ).toList(); +// return new ExamResponse( +// exam.examId(), +// exam.schoolName(), +// address, +// exam.area(), +// exam.capacity(), +// exam.deadLineTime(), +// exam.examDate() +// e +// ); +// } +// public static List fromList(List foundExams) { +// return foundExams.stream() +// .map(ExamResponse::from) +// .toList(); +// } } diff --git a/src/main/java/life/mosu/mosuserver/presentation/exam/dto/LunchInfo.java b/src/main/java/life/mosu/mosuserver/presentation/exam/dto/LunchInfo.java deleted file mode 100644 index 13da3bc2..00000000 --- a/src/main/java/life/mosu/mosuserver/presentation/exam/dto/LunchInfo.java +++ /dev/null @@ -1,12 +0,0 @@ -package life.mosu.mosuserver.presentation.exam.dto; - -public record LunchInfo( - String name, - Integer price -) { - - public static LunchInfo of(String name, Integer price) { - return new LunchInfo(name, price); - } - -} diff --git a/src/main/java/life/mosu/mosuserver/presentation/examapplication/ExamApplicationController.java b/src/main/java/life/mosu/mosuserver/presentation/examapplication/ExamApplicationController.java index a85600f8..549945de 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/examapplication/ExamApplicationController.java +++ b/src/main/java/life/mosu/mosuserver/presentation/examapplication/ExamApplicationController.java @@ -2,6 +2,8 @@ import life.mosu.mosuserver.application.examapplication.ExamApplicationService; import life.mosu.mosuserver.global.annotation.UserId; +import life.mosu.mosuserver.global.exception.CustomRuntimeException; +import life.mosu.mosuserver.global.exception.ErrorCode; import life.mosu.mosuserver.global.util.ApiResponseWrapper; import life.mosu.mosuserver.presentation.admin.dto.ExamTicketResponse; import life.mosu.mosuserver.presentation.examapplication.dto.ExamApplicationInfoResponse; @@ -16,6 +18,7 @@ import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @@ -25,35 +28,37 @@ public class ExamApplicationController { private final ExamApplicationService examApplicationService; - //TODO: 테스트 필요 @GetMapping("{examApplicationId}") + @PreAuthorize("isAuthenticated() and hasRole('USER')") public ResponseEntity> getApplication( - @PathVariable("examApplicationId") Long examApplicationId + @UserId Long userId, + @PathVariable("examApplicationId") Long examApplicationId, + @RequestParam("applicationId") Long applicationId ) { + + if (applicationId == null) { + throw new CustomRuntimeException(ErrorCode.WRONG_APPLICATION_ID_TYPE); + } + ExamApplicationInfoResponse response = examApplicationService.getApplication( - examApplicationId); + userId, + examApplicationId, + applicationId); return ResponseEntity.ok( ApiResponseWrapper.success(HttpStatus.OK, "신청 정보 조회를 완료하였습니다.", response)); } - @DeleteMapping("{examApplicationId}") - public ResponseEntity> deleteExamApplication( - @PathVariable("examApplicationId") Long examApplicationId - ) { - examApplicationService.deleteExamApplication(examApplicationId); - return ResponseEntity.ok( - ApiResponseWrapper.success(HttpStatus.OK, "신청 정보 정상적으로 삭제되었습니다.")); - } - @PutMapping("{examApplicationId}/subjects") - public ResponseEntity> updateSubjects( + @PreAuthorize("isAuthenticated() and hasRole('USER')") + public ResponseEntity> updateSubjects( + @UserId Long userId, @PathVariable("examApplicationId") Long examApplicationId, @RequestBody UpdateSubjectRequest request ) { - ExamApplicationInfoResponse response = examApplicationService.updateSubjects( + examApplicationService.updateSubjects(userId, examApplicationId, request); return ResponseEntity.ok( - ApiResponseWrapper.success(HttpStatus.OK, "과목 수정을 완료했습니다.", response)); + ApiResponseWrapper.success(HttpStatus.OK, "과목 수정을 완료했습니다.")); } @GetMapping("{examApplicationId}/exam-ticket") @@ -62,9 +67,21 @@ public ResponseEntity> getExamTicket( @UserId Long userId, @PathVariable("examApplicationId") Long examApplicationId ) { - ExamTicketResponse response = examApplicationService.getExamTicket(examApplicationId); + ExamTicketResponse response = examApplicationService.getExamTicket(userId, + examApplicationId); return ResponseEntity.ok( ApiResponseWrapper.success(HttpStatus.OK, "수험표 발급을 완료했습니다.", response)); } + @Deprecated + @DeleteMapping("{examApplicationId}") + public ResponseEntity> deleteExamApplication( + @UserId Long userId, + @PathVariable("examApplicationId") Long examApplicationId + ) { + examApplicationService.deleteExamApplication(userId, examApplicationId); + return ResponseEntity.ok( + ApiResponseWrapper.success(HttpStatus.OK, "신청 정보 정상적으로 삭제되었습니다.")); + } + } diff --git a/src/main/java/life/mosu/mosuserver/presentation/examapplication/dto/UpdateSubjectRequest.java b/src/main/java/life/mosu/mosuserver/presentation/examapplication/dto/UpdateSubjectRequest.java index e7a19346..30db2070 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/examapplication/dto/UpdateSubjectRequest.java +++ b/src/main/java/life/mosu/mosuserver/presentation/examapplication/dto/UpdateSubjectRequest.java @@ -16,7 +16,7 @@ private Set validatedSubjects() { Set subjectSet; try { subjectSet = subjects.stream() - .map(Subject::valueOf) + .map(Subject::getSubject) .collect(Collectors.toSet()); } catch (IllegalArgumentException e) { throw new CustomRuntimeException(ErrorCode.WRONG_SUBJECT_TYPE); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index dc77aab6..95b4561a 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -32,7 +32,7 @@ spring: open-in-view: false show-sql: true hibernate: - ddl-auto: update + ddl-auto: create-drop properties: hibernate: show_sql: true @@ -72,6 +72,9 @@ logging: file: path: ./logs name: app.log + level: + root: INFO + toss: secret-key: test_sk_kYG57Eba3GYBMGeobgbLrpWDOxmA diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index feb6ff17..00c6d488 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -10,6 +10,8 @@ server: include-stacktrace: never spring: + flyway: + enabled: false config: import: - test-security-config.yml