diff --git a/src/main/java/life/mosu/mosuserver/application/admin/AdminDashboardService.java b/src/main/java/life/mosu/mosuserver/application/admin/AdminDashboardService.java index e772217f..90a6fa96 100644 --- a/src/main/java/life/mosu/mosuserver/application/admin/AdminDashboardService.java +++ b/src/main/java/life/mosu/mosuserver/application/admin/AdminDashboardService.java @@ -1,7 +1,8 @@ package life.mosu.mosuserver.application.admin; import life.mosu.mosuserver.domain.examapplication.repository.ExamApplicationJpaRepository; -import life.mosu.mosuserver.domain.refund.repository.RefundJpaRepository; +import life.mosu.mosuserver.domain.refund.repository.RefundFailureLogJpaRepository; +import life.mosu.mosuserver.domain.user.entity.UserRole; import life.mosu.mosuserver.domain.user.repository.UserJpaRepository; import life.mosu.mosuserver.presentation.admin.dto.DashBoardResponse; import lombok.RequiredArgsConstructor; @@ -13,15 +14,19 @@ public class AdminDashboardService { private final ExamApplicationJpaRepository examApplicationJpaRepository; private final UserJpaRepository userJpaRepository; - private final RefundJpaRepository refundJpaRepository; + private final RefundFailureLogJpaRepository refundFailureLogJpaRepository; // 대시보드 정보 조회 public DashBoardResponse getAll() { - Long applicationCounts = examApplicationJpaRepository.count(); - Long refundCounts = refundJpaRepository.count(); - Long userCounts = userJpaRepository.count(); - return new DashBoardResponse(applicationCounts, refundCounts, userCounts); - } + Long applicationCounts = examApplicationJpaRepository.countAll(); + Long refundAbortedCounts = refundFailureLogJpaRepository.count(); + Long userCounts = userJpaRepository.countByUserRoleNot(UserRole.ROLE_ADMIN); + return DashBoardResponse.of( + applicationCounts, + refundAbortedCounts, + userCounts + ); + } } 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 84198f13..09362530 100644 --- a/src/main/java/life/mosu/mosuserver/application/application/ApplicationService.java +++ b/src/main/java/life/mosu/mosuserver/application/application/ApplicationService.java @@ -46,6 +46,9 @@ public CreateApplicationResponse apply(Long userId, ApplicationRequest request) List examIds = request.examApplication().stream() .map(ExamApplicationRequest::examId) .toList(); + + validator.agreedToTerms(request); + validator.requestNoDuplicateExams(examIds); return handleApplication( userId, examIds, @@ -78,7 +81,6 @@ private CreateApplicationResponse handleApplication( List examApplications, FileRequest admissionTicket ) { - validator.requestNoDuplicateExams(examIds); List exams = examJpaRepository.findAllById(examIds); validator.examDateNotPassed(exams); validator.examNotFull(exams); 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 e664b43b..ee282f6b 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 @@ -13,10 +13,8 @@ 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; -@Slf4j @Component @RequiredArgsConstructor public class GetApplicationsStepProcessor implements @@ -33,7 +31,6 @@ 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/vaildator/ApplicationValidator.java b/src/main/java/life/mosu/mosuserver/application/application/vaildator/ApplicationValidator.java index 4df221c6..e6f450ab 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 @@ -6,29 +6,41 @@ import java.util.Set; import java.util.stream.Collectors; import life.mosu.mosuserver.application.exam.cache.ExamQuotaCacheManager; -import life.mosu.mosuserver.domain.application.repository.ApplicationJpaRepository; import life.mosu.mosuserver.domain.exam.entity.ExamJpaEntity; import life.mosu.mosuserver.domain.exam.entity.ExamJpaRepository; import life.mosu.mosuserver.domain.exam.entity.ExamStatus; +import life.mosu.mosuserver.domain.examapplication.repository.ExamApplicationJpaRepository; import life.mosu.mosuserver.global.exception.CustomRuntimeException; import life.mosu.mosuserver.global.exception.ErrorCode; +import life.mosu.mosuserver.presentation.application.dto.ApplicationRequest; import life.mosu.mosuserver.presentation.application.dto.ExamApplicationRequest; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +@Slf4j @Component @RequiredArgsConstructor public class ApplicationValidator { private final ExamJpaRepository examJpaRepository; - private final ApplicationJpaRepository applicationJpaRepository; + private final ExamApplicationJpaRepository examApplicationJpaRepository; private final ExamQuotaCacheManager examQuotaCacheManager; + public void agreedToTerms(ApplicationRequest request) { + if (!request.agreement().validateAgreement()) { + throw new CustomRuntimeException(ErrorCode.NOT_AGREED_TO_TERMS); + } + } + public void requestNoDuplicateExams(List examIds) { Set examIdSet = new HashSet<>(examIds); if (examIds.size() != examIdSet.size()) { throw new CustomRuntimeException(ErrorCode.EXAM_DUPLICATED); } + if (examIdSet.isEmpty()) { + throw new CustomRuntimeException(ErrorCode.EXAM_NOT_APPLIED); + } } public void examIdsAndLunchSelection(List requests) { @@ -39,12 +51,9 @@ public void examIdsAndLunchSelection(List requests) { List requestedExamIds = requests.stream() .map(ExamApplicationRequest::examId) .toList(); + Set examIdSet = new HashSet<>(requestedExamIds); - List existingExams = examJpaRepository.findAllById(requestedExamIds); - - if (existingExams.size() != requestedExamIds.size()) { - throw new CustomRuntimeException(ErrorCode.EXAM_NOT_FOUND); - } + List existingExams = examJpaRepository.findAllById(examIdSet); lunchSelection(requests, existingExams); } @@ -56,16 +65,18 @@ private void lunchSelection(List requests, .map(ExamJpaEntity::getId) .collect(Collectors.toSet()); - boolean hasInvalidLunchRequest = requests.stream() - .anyMatch(req -> examsWithoutLunch.contains(req.examId()) && req.isLunchChecked()); + requests.stream() + .filter(req -> req.isLunchChecked() && examsWithoutLunch.contains(req.examId())) + .findFirst() + .ifPresent(req -> { + throw new CustomRuntimeException(ErrorCode.LUNCH_SELECTION_INVALID); + }); - if (hasInvalidLunchRequest) { - throw new CustomRuntimeException(ErrorCode.LUNCH_SELECTION_INVALID); - } } public void noDuplicateApplication(Long userId, List examIds) { - boolean alreadyApplied = applicationJpaRepository.existsByUserIdAndExamIds(userId, examIds); + boolean alreadyApplied = examApplicationJpaRepository.existsByUserIdAndExamIds(userId, + examIds); if (alreadyApplied) { throw new CustomRuntimeException(ErrorCode.APPLICATION_SCHOOL_DUPLICATED); } diff --git a/src/main/java/life/mosu/mosuserver/application/auth/processor/SignUpAccountStepProcessor.java b/src/main/java/life/mosu/mosuserver/application/auth/processor/SignUpAccountStepProcessor.java index 6adfd03c..be712eff 100644 --- a/src/main/java/life/mosu/mosuserver/application/auth/processor/SignUpAccountStepProcessor.java +++ b/src/main/java/life/mosu/mosuserver/application/auth/processor/SignUpAccountStepProcessor.java @@ -18,8 +18,9 @@ public class SignUpAccountStepProcessor implements StepProcessor new CustomRuntimeException(ErrorCode.EXAM_NOT_FOUND)); exam.close(); } + + private void validateExamDate(ExamRequest request) { + if (!request.deadlineTime().isBefore(request.examDate().atStartOfDay())) { + throw new CustomRuntimeException(ErrorCode.EXAM_DATE_AFTER_DEADLINE); + } + } } 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 bde22bd6..6ae044d7 100644 --- a/src/main/java/life/mosu/mosuserver/application/examapplication/ExamApplicationService.java +++ b/src/main/java/life/mosu/mosuserver/application/examapplication/ExamApplicationService.java @@ -41,7 +41,6 @@ public class ExamApplicationService { private final S3Service s3Service; private final FixedQuantityDiscountCalculator calculator; - @Transactional public List register(RegisterExamApplicationEvent event) { List examApplicationEntities = event.toEntity(); @@ -57,7 +56,6 @@ public void updateSubjects(Long userId, Long examApplicationId, examSubjectJpaRepository.deleteExamSubjectsWithDonePayment(examApplicationId); List examSubjects = request.toEntityList(examApplicationId); examSubjectJpaRepository.saveAll(examSubjects); - } @Transactional(propagation = Propagation.REQUIRES_NEW) @@ -97,27 +95,17 @@ public ExamTicketResponse getExamTicket(Long userId, Long examApplicationId) { .map(Subject::getSubjectName) .toList(); - String s3Key = examTicketInfo.s3Key(); - String examTicketImgUrl = null; - - if (s3Key != null) { - examTicketImgUrl = s3Service.getPreSignedUrl(s3Key); - } + String examTicketImgUrl = getExamTicketImgUrl(examTicketInfo); return ExamTicketResponse.of(examTicketImgUrl, examTicketInfo.userName(), examTicketInfo.birth(), examTicketInfo.examNumber(), subjects, examTicketInfo.schoolName()); - } public ExamApplicationInfoResponse getApplication(Long userId, Long examApplicationId, Long applicationId) { - validateUser(userId, examApplicationId); - //상세 조회는 done 만 가능 -// Integer examApplicationCount = paymentJpaRepository.countByExamApplicationId( -// examApplicationId); List examApplicationEntities = examApplicationJpaRepository.findByApplicationId( applicationId); int lunchCount = (int) examApplicationEntities.stream() @@ -135,6 +123,7 @@ public ExamApplicationInfoResponse getApplication(Long userId, Long examApplicat Set subjects = examSubjects.stream() .map(ExamSubjectJpaEntity::getSubjectName) .collect(Collectors.toSet()); + //totalAmount 는 Lunch 가격이 포함되었을 수도 있음 //totalAmount - Lunch 가격으로 getAppliedDiscountAmount() 메소드에 넣어야함. @@ -187,4 +176,15 @@ private void validateExamTicketOpenDate(LocalDate examDate, String examNumber) { throw new CustomRuntimeException(ErrorCode.EXAM_TICKET_NOT_OPEN); } } + + private String getExamTicketImgUrl(ExamTicketInfoProjection examTicketInfo) { + String s3Key = examTicketInfo.s3Key(); + String examTicketImgUrl = null; + + if (s3Key != null) { + examTicketImgUrl = s3Service.getPreSignedUrl(s3Key); + } + return examTicketImgUrl; + } + } diff --git a/src/main/java/life/mosu/mosuserver/domain/admin/projection/RecommendationDetailsProjection.java b/src/main/java/life/mosu/mosuserver/domain/admin/projection/RecommendationDetailsProjection.java index 741804cc..c6a8bc59 100644 --- a/src/main/java/life/mosu/mosuserver/domain/admin/projection/RecommendationDetailsProjection.java +++ b/src/main/java/life/mosu/mosuserver/domain/admin/projection/RecommendationDetailsProjection.java @@ -10,8 +10,8 @@ public record RecommendationDetailsProjection( LocalDate birth, String recommendeeName, String recommendeePhoneNumber, - String recommendeeBank, - String recommendeeAccountNumber + String recommenderBank, + String recommenderAccountNumber ) { } diff --git a/src/main/java/life/mosu/mosuserver/domain/application/repository/ApplicationJpaRepository.java b/src/main/java/life/mosu/mosuserver/domain/application/repository/ApplicationJpaRepository.java index 4eff05c4..38613467 100644 --- a/src/main/java/life/mosu/mosuserver/domain/application/repository/ApplicationJpaRepository.java +++ b/src/main/java/life/mosu/mosuserver/domain/application/repository/ApplicationJpaRepository.java @@ -29,21 +29,6 @@ AND a.status IN ('PENDING', 'ABORT') """) List findAllByUserId(@Param("userId") Long userId); - @Query( - """ - SELECT CASE WHEN COUNT(a) > 0 THEN true ELSE false END - FROM ApplicationJpaEntity a - JOIN ExamApplicationJpaEntity ea ON a.id = ea.applicationId - JOIN ExamJpaEntity e ON ea.examId = e.id - JOIN PaymentJpaEntity p ON ea.id = p.examApplicationId - WHERE a.userId = :userId - AND p.paymentStatus = 'DONE' - AND e.id IN :examIds - """ - ) - boolean existsByUserIdAndExamIds(@Param("userId") Long userId, - @Param("examIds") List examIds); - @Modifying @Query(value = """ UPDATE application a diff --git a/src/main/java/life/mosu/mosuserver/domain/exam/entity/ExamJpaEntity.java b/src/main/java/life/mosu/mosuserver/domain/exam/entity/ExamJpaEntity.java index 24c157dc..591e999c 100644 --- a/src/main/java/life/mosu/mosuserver/domain/exam/entity/ExamJpaEntity.java +++ b/src/main/java/life/mosu/mosuserver/domain/exam/entity/ExamJpaEntity.java @@ -80,7 +80,7 @@ public ExamJpaEntity( } public boolean hasNotLunch() { - return lunchName == null; + return this.lunchName == null || this.lunchPrice == null; } public void close() { diff --git a/src/main/java/life/mosu/mosuserver/domain/examapplication/repository/ExamApplicationJpaRepository.java b/src/main/java/life/mosu/mosuserver/domain/examapplication/repository/ExamApplicationJpaRepository.java index a1e378ec..363d9ea6 100644 --- a/src/main/java/life/mosu/mosuserver/domain/examapplication/repository/ExamApplicationJpaRepository.java +++ b/src/main/java/life/mosu/mosuserver/domain/examapplication/repository/ExamApplicationJpaRepository.java @@ -44,9 +44,12 @@ public interface ExamApplicationJpaRepository extends JOIN PaymentJpaEntity p on p.examApplicationId = ea.id WHERE ea.id = :examApplicationId AND p.paymentStatus = 'DONE' + AND ea.userId = :userId + AND p.deleted = false """) - Optional findExamApplicationInfoById(Long userId, - Long examApplicationId); + Optional findExamApplicationInfoById( + @Param("userId") Long userId, + @Param("examApplicationId") Long examApplicationId); @Query(""" @@ -67,6 +70,7 @@ Optional findExamApplicationInfoById(Long userId, WHERE ea.id = :examApplicationId AND u.id = :userId AND p.paymentStatus = 'DONE' + AND p.deleted = false """) Optional findExamTicketInfoProjectionById( @Param("userId") Long userId, @@ -105,6 +109,7 @@ Optional findExamTicketInfoProjectionById( JOIN PaymentJpaEntity p on p.examApplicationId = ea.id WHERE ea.id = :targetId AND p.paymentStatus = 'DONE' + AND p.deleted = false """) Optional findExamAndPaymentByExamApplicationId( @Param("targetId") Long targetId); @@ -121,6 +126,7 @@ Optional findExamAndPaymentByExamApplicationId( JOIN PaymentJpaEntity p ON p.examApplicationId = ea.id WHERE ea.id = :examApplicationId AND p.paymentStatus = 'DONE' + AND p.deleted = false """) Optional findExamInfo(@Param("examApplicationId") Long examApplicationId); @@ -137,6 +143,7 @@ Optional findExamAndPaymentByExamApplicationId( JOIN PaymentJpaEntity p ON p.examApplicationId = ea.id WHERE ea.id = :examApplicationId AND p.paymentStatus = 'DONE' + AND p.deleted = false """) Optional findExamInfoWithExamNumber( @Param("examApplicationId") Long examApplicationId); @@ -148,6 +155,7 @@ SELECT case when COUNT(ea) > 0 then true else false end WHERE ea.id = :examApplicationId AND ea.userId = :userId AND p.paymentStatus = 'DONE' + AND p.deleted = false """) boolean existByUserIdAndExamApplicationId(@Param("userId") Long userId, @Param("examApplicationId") Long examApplicationId); @@ -161,4 +169,29 @@ boolean existByUserIdAndExamApplicationId(@Param("userId") Long userId, AND e.deleted = false """) Optional findByOrderId(String orderId); + + @Query( + """ + SELECT CASE WHEN COUNT(ea) > 0 THEN true ELSE false END + FROM ExamApplicationJpaEntity ea + JOIN PaymentJpaEntity p ON ea.id = p.examApplicationId + WHERE ea.userId = :userId + AND p.paymentStatus = 'DONE' + AND p.deleted = false + AND ea.examId IN :examIds + """ + ) + boolean existsByUserIdAndExamIds( + @Param("userId") Long userId, + @Param("examIds") List examIds); + + + @Query(""" + SELECT COUNT(ea) + FROM ExamApplicationJpaEntity ea + JOIN PaymentJpaEntity p ON ea.id = p.examApplicationId + WHERE p.paymentStatus = 'DONE' + AND p.deleted = false + """) + long countAll(); } \ No newline at end of file diff --git a/src/main/java/life/mosu/mosuserver/domain/examapplication/repository/ExamSubjectJpaRepository.java b/src/main/java/life/mosu/mosuserver/domain/examapplication/repository/ExamSubjectJpaRepository.java index 3aad5dac..e595a9e6 100644 --- a/src/main/java/life/mosu/mosuserver/domain/examapplication/repository/ExamSubjectJpaRepository.java +++ b/src/main/java/life/mosu/mosuserver/domain/examapplication/repository/ExamSubjectJpaRepository.java @@ -29,6 +29,7 @@ public interface ExamSubjectJpaRepository extends JpaRepository findFailedRefunds(@Param("time") LocalDateTime time); + } diff --git a/src/main/java/life/mosu/mosuserver/domain/user/repository/UserJpaRepository.java b/src/main/java/life/mosu/mosuserver/domain/user/repository/UserJpaRepository.java index e5038a33..a8d59e8d 100644 --- a/src/main/java/life/mosu/mosuserver/domain/user/repository/UserJpaRepository.java +++ b/src/main/java/life/mosu/mosuserver/domain/user/repository/UserJpaRepository.java @@ -2,6 +2,7 @@ import java.util.Optional; import life.mosu.mosuserver.domain.user.entity.UserJpaEntity; +import life.mosu.mosuserver.domain.user.entity.UserRole; import org.springframework.data.jpa.repository.JpaRepository; public interface UserJpaRepository extends JpaRepository { @@ -14,5 +15,7 @@ public interface UserJpaRepository extends JpaRepository { Optional findByPhoneNumber(String phoneNumber); + long countByUserRoleNot(UserRole userRole); + boolean existsByPhoneNumber(String phoneNumber); } 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 12499a2a..e5f3f131 100644 --- a/src/main/java/life/mosu/mosuserver/global/exception/ErrorCode.java +++ b/src/main/java/life/mosu/mosuserver/global/exception/ErrorCode.java @@ -45,11 +45,13 @@ public enum ErrorCode { USER_INFO_INVALID(HttpStatus.BAD_REQUEST, "유효하지 않은 사용자 정보입니다."), USER_NOT_ACCESS_FORBIDDEN(HttpStatus.BAD_REQUEST, "접근 권한이 없는 사용자입니다"), USER_SAVE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "사용자 저장에 실패했습니다."), + // 신청 관련 에러 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개를 신청해야 합니다."), + NOT_AGREED_TO_TERMS(HttpStatus.BAD_REQUEST, "신청 시 모든 약관에 동의해야 합니다."), // 수험표 관련 에러 EXAM_TICKET_NOT_OPEN(HttpStatus.BAD_REQUEST, "수험표 조회 기간이 아닙니다."), @@ -125,7 +127,8 @@ public enum ErrorCode { // 시험 관련 에러 EXAM_NOT_FOUND(HttpStatus.NOT_FOUND, "시험 정보를 찾을 수 없습니다."), EXAM_DATE_PASSED(HttpStatus.BAD_REQUEST, "이미 지난 시험입니다."), - + EXAM_NOT_APPLIED(HttpStatus.BAD_REQUEST, "1개 이상의 시험을 신청해야 합니다."), + EXAM_DATE_AFTER_DEADLINE(HttpStatus.BAD_REQUEST, "시험일은 접수 마감일 이후여야 합니다."), //lunch 관련 LUNCH_NOT_FOUND(HttpStatus.NOT_FOUND, "점심 정보를 찾을 수 없습니다."), LUNCH_PRICE_UPDATE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "가격 수정에 실패하였습니다."), @@ -163,8 +166,8 @@ public enum ErrorCode { INVALID_VIRTUAL_ACCOUNT_DEPOSIT_EVENT(HttpStatus.BAD_REQUEST, "유효하지 않은 가상 계좌 입금 이벤트입니다."), VIRTUAL_ACCOUNT_CREATION_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "가상 계좌 생성에 실패했습니다."), - VIRTUAL_ACCOUNT_LOG_NOT_FOUND(HttpStatus.NOT_FOUND, "가상 계좌 로그를 찾을 수 없습니다."), - ; + VIRTUAL_ACCOUNT_LOG_NOT_FOUND(HttpStatus.NOT_FOUND, "가상 계좌 로그를 찾을 수 없습니다."); + private final HttpStatus status; private final String message; 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 7d03fe42..0b65c608 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, - user_id, exam_id, lunch_checked, exam_number) - VALUES (?, ?, ?, ?, ?, ?, ?) + user_id, exam_id, lunch_checked, exam_number, deleted) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) """; private static final String SQL_INSERT_EXAM_SUBJECT = """ INSERT INTO exam_subject (exam_application_id, subject) VALUES (?, ?) @@ -53,7 +53,7 @@ public List saveAllExamApplicationsWithSubjects( ps.setLong(5, e.getExamId()); ps.setBoolean(6, e.getIsLunchChecked()); ps.setString(7, e.getExamNumber()); -// ps.setBoolean(8, e.getDeleted()); + ps.setBoolean(8, false); ps.addBatch(); log.info( diff --git a/src/main/java/life/mosu/mosuserver/presentation/admin/AdminBannerController.java b/src/main/java/life/mosu/mosuserver/presentation/admin/AdminBannerController.java index eb852b2f..b8257969 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/admin/AdminBannerController.java +++ b/src/main/java/life/mosu/mosuserver/presentation/admin/AdminBannerController.java @@ -1,5 +1,6 @@ package life.mosu.mosuserver.presentation.admin; +import jakarta.validation.Valid; import java.util.List; import life.mosu.mosuserver.application.admin.AdminBannerService; import life.mosu.mosuserver.global.util.ApiResponseWrapper; @@ -28,7 +29,7 @@ public class AdminBannerController { @PostMapping @PreAuthorize("isAuthenticated() and hasRole('ADMIN')") public ResponseEntity> create( - @RequestBody BannerRequest request) { + @Valid @RequestBody BannerRequest request) { adminBannerService.create(request); return ResponseEntity.ok( ApiResponseWrapper.success(HttpStatus.OK, "배너 등록 성공")); diff --git a/src/main/java/life/mosu/mosuserver/presentation/admin/dto/BannerRequest.java b/src/main/java/life/mosu/mosuserver/presentation/admin/dto/BannerRequest.java index 1abb7a7d..ddfece7b 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/admin/dto/BannerRequest.java +++ b/src/main/java/life/mosu/mosuserver/presentation/admin/dto/BannerRequest.java @@ -12,7 +12,6 @@ public record BannerRequest( FileRequest file ) { - //배너 등록할 때 fileRequest 가 없을 때는 어떤 식으로 하면 되는지 public BannerJpaEntity toEntity() { return BannerJpaEntity.builder() .fileName(file != null ? file.fileName() : null) diff --git a/src/main/java/life/mosu/mosuserver/presentation/admin/dto/DashBoardResponse.java b/src/main/java/life/mosu/mosuserver/presentation/admin/dto/DashBoardResponse.java index 0b63f660..1ef7589a 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/admin/dto/DashBoardResponse.java +++ b/src/main/java/life/mosu/mosuserver/presentation/admin/dto/DashBoardResponse.java @@ -2,12 +2,20 @@ public record DashBoardResponse( Long applicationCounts, - Long refundCounts, + Long refundAbortedCounts, Long userCounts ) { - public static DashBoardResponse of(Long applicationCounts, Long refundCounts, Long userCounts) { - return new DashBoardResponse(applicationCounts, refundCounts, userCounts); + public static DashBoardResponse of( + Long applicationCounts, + Long refundAbortedCounts, + Long userCounts + ) { + return new DashBoardResponse( + applicationCounts, + refundAbortedCounts, + userCounts + ); } } diff --git a/src/main/java/life/mosu/mosuserver/presentation/admin/dto/RecommendationExcelDto.java b/src/main/java/life/mosu/mosuserver/presentation/admin/dto/RecommendationExcelDto.java index 9cd9e401..3fe78aa7 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/admin/dto/RecommendationExcelDto.java +++ b/src/main/java/life/mosu/mosuserver/presentation/admin/dto/RecommendationExcelDto.java @@ -31,13 +31,13 @@ public record RecommendationExcelDto( @ExcelColumn(headerName = "피추천자 전화번호") String recommendeePhoneNumber, - @Schema(description = "피추천자 은행명", example = "신한은행") - @ExcelColumn(headerName = "피추천자 은행명") - String recommendeeBank, + @Schema(description = "추천자 은행명", example = "신한은행") + @ExcelColumn(headerName = "추천자 은행명") + String recommenderBank, - @Schema(description = "피추천자 계좌번호", example = "110123456789") - @ExcelColumn(headerName = "피추천자 계좌번호") - String recommendeeAccountNumber + @Schema(description = "추천자 계좌번호", example = "110123456789") + @ExcelColumn(headerName = "추천자 계좌번호") + String recommenderAccountNumber ) { public static RecommendationExcelDto of(RecommendationDetailsProjection projection) { @@ -48,8 +48,8 @@ public static RecommendationExcelDto of(RecommendationDetailsProjection projecti projection.birth(), projection.recommendeeName(), projection.recommendeePhoneNumber(), - projection.recommendeeBank(), - projection.recommendeeAccountNumber() + projection.recommenderBank(), + projection.recommenderAccountNumber() ); } diff --git a/src/main/java/life/mosu/mosuserver/presentation/application/dto/AgreementRequest.java b/src/main/java/life/mosu/mosuserver/presentation/application/dto/AgreementRequest.java index 37f1ed2a..ef15a8f1 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/application/dto/AgreementRequest.java +++ b/src/main/java/life/mosu/mosuserver/presentation/application/dto/AgreementRequest.java @@ -13,4 +13,8 @@ public record AgreementRequest( ) { + public boolean validateAgreement() { + return agreedToNotices && agreedToRefundPolicy; + } + } diff --git a/src/main/java/life/mosu/mosuserver/presentation/application/dto/ApplicationRequest.java b/src/main/java/life/mosu/mosuserver/presentation/application/dto/ApplicationRequest.java index 5a168cfc..f8457695 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/application/dto/ApplicationRequest.java +++ b/src/main/java/life/mosu/mosuserver/presentation/application/dto/ApplicationRequest.java @@ -30,8 +30,9 @@ public record ApplicationRequest( @NotNull AgreementRequest agreement, - @Schema(description = "응시 과목 목록 (예: PHYSICS_1)", example = "[\"PHYSICS_1\", \"ETHICS_AND_IDEOLOGY\"]") - Set subjects + @Schema(description = "응시 과목 목록") + @NotNull + List subjects ) { public ApplicationJpaEntity toApplicationJpaEntity(Long userId) { diff --git a/src/main/java/life/mosu/mosuserver/presentation/event/dto/DurationRequest.java b/src/main/java/life/mosu/mosuserver/presentation/event/dto/DurationRequest.java index 91120477..ebc06f04 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/event/dto/DurationRequest.java +++ b/src/main/java/life/mosu/mosuserver/presentation/event/dto/DurationRequest.java @@ -6,16 +6,13 @@ @Schema(description = "이벤트 기간 요청 DTO") public record DurationRequest( - - @Schema(description = "이벤트 시작일", example = "2025-07-01") - LocalDate startDate, - + @Schema(description = "이벤트 종료일", example = "2025-07-31") LocalDate endDate ) { public DurationJpaVO toDurationJpaVO() { - return new DurationJpaVO(startDate, endDate); + return new DurationJpaVO(LocalDate.now(), endDate); } } diff --git a/src/main/java/life/mosu/mosuserver/presentation/event/dto/EventRequest.java b/src/main/java/life/mosu/mosuserver/presentation/event/dto/EventRequest.java index 2c3af3f9..ae0241e7 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/event/dto/EventRequest.java +++ b/src/main/java/life/mosu/mosuserver/presentation/event/dto/EventRequest.java @@ -2,7 +2,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; -import java.util.List; import life.mosu.mosuserver.domain.event.entity.EventJpaEntity; import life.mosu.mosuserver.domain.file.Visibility; import life.mosu.mosuserver.presentation.common.FileRequest; @@ -25,18 +24,18 @@ public record EventRequest( ) { - public List optionalAttachment() { - FileRequest parsedAttachment = this.attachment; - return parsedAttachment == null ? List.of() : List.of(parsedAttachment); - } +// public List optionalAttachment() { +// FileRequest parsedAttachment = this.attachment; +// return parsedAttachment == null ? List.of() : List.of(parsedAttachment); +// } public EventJpaEntity toEntity() { return EventJpaEntity.builder() .title(title) .eventLink(eventLink) - .duration(duration.toDurationJpaVO()) - .fileName(attachment().fileName()) - .s3Key(attachment().s3Key()) + .duration(duration != null ? duration.toDurationJpaVO() : null) + .fileName(attachment != null ? attachment().fileName() : null) + .s3Key(attachment != null ? attachment().s3Key() : null) .visibility(Visibility.PUBLIC) .build(); } diff --git a/src/main/java/life/mosu/mosuserver/presentation/event/dto/EventResponse.java b/src/main/java/life/mosu/mosuserver/presentation/event/dto/EventResponse.java index d346a04f..ed33a7f9 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/event/dto/EventResponse.java +++ b/src/main/java/life/mosu/mosuserver/presentation/event/dto/EventResponse.java @@ -34,8 +34,9 @@ public static EventResponse of(EventJpaEntity event, String eventUrl) { return new EventResponse( event.getId(), event.getTitle(), - event.getDuration().getEndDate(), - event.getEventLink(), + event.getDuration() != null ? event.getDuration().getEndDate() + : null, + event.getEventLink() != null ? event.getEventLink() : null, attachment ); } diff --git a/src/main/java/life/mosu/mosuserver/presentation/exam/ExamController.java b/src/main/java/life/mosu/mosuserver/presentation/exam/ExamController.java index 5ff384c2..9e18c357 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/exam/ExamController.java +++ b/src/main/java/life/mosu/mosuserver/presentation/exam/ExamController.java @@ -1,5 +1,6 @@ package life.mosu.mosuserver.presentation.exam; +import jakarta.validation.Valid; import java.util.List; import life.mosu.mosuserver.application.exam.ExamService; import life.mosu.mosuserver.global.util.ApiResponseWrapper; @@ -29,7 +30,7 @@ public class ExamController { @PostMapping @PreAuthorize("isAuthenticated() and hasRole('ADMIN')") public ResponseEntity> register( - @RequestBody ExamRequest request + @Valid @RequestBody ExamRequest request ) { examService.register(request); return ResponseEntity.ok( @@ -65,10 +66,9 @@ public ResponseEntity> delete(@PathVariable Long examId examService.delete(examId); return ResponseEntity.ok( ApiResponseWrapper.success(HttpStatus.OK, "시험장 삭제 성공")); - } - @PatchMapping("/{examId}") + @PatchMapping("/{examId}/close") @PreAuthorize("isAuthenticated() and hasRole('ADMIN')") public ResponseEntity> close(@PathVariable Long examId) { examService.close(examId); diff --git a/src/main/java/life/mosu/mosuserver/presentation/exam/dto/ExamRequest.java b/src/main/java/life/mosu/mosuserver/presentation/exam/dto/ExamRequest.java index 2c793688..e5b0616f 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/exam/dto/ExamRequest.java +++ b/src/main/java/life/mosu/mosuserver/presentation/exam/dto/ExamRequest.java @@ -1,5 +1,7 @@ package life.mosu.mosuserver.presentation.exam.dto; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import java.time.LocalDate; import java.time.LocalDateTime; import life.mosu.mosuserver.domain.exam.entity.AddressJpaVO; @@ -7,11 +9,11 @@ import life.mosu.mosuserver.domain.exam.entity.ExamJpaEntity; public record ExamRequest( - String schoolName, - String areaName, - AddressRequest address, - LocalDate examDate, - Integer capacity, + @NotBlank String schoolName, + @NotBlank String areaName, + @NotNull AddressRequest address, + @NotNull LocalDate examDate, + @NotNull Integer capacity, LocalDateTime deadlineTime, LunchRequest lunch ) { @@ -25,8 +27,8 @@ public ExamJpaEntity toEntity() { .examDate(examDate) .capacity(capacity) .deadlineTime(deadlineTime) - .lunchName(lunch.name()) - .lunchPrice(lunch.price()) + .lunchName(lunch != null ? lunch.name() : null) + .lunchPrice(lunch != null ? lunch.price() : null) .build(); } diff --git a/src/main/java/life/mosu/mosuserver/presentation/profile/dto/EditProfileRequest.java b/src/main/java/life/mosu/mosuserver/presentation/profile/dto/EditProfileRequest.java index 40f439b6..003733bf 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/profile/dto/EditProfileRequest.java +++ b/src/main/java/life/mosu/mosuserver/presentation/profile/dto/EditProfileRequest.java @@ -9,7 +9,7 @@ public record EditProfileRequest( @Schema(description = "이메일 주소", example = "hong@example.com") String email, - @Schema(description = "학력 정보 (Enum: ELEMENTARY, MIDDLE, HIGH_SCHOOL, UNIVERSITY 등)", example = "HIGH_SCHOOL") + @Schema(description = "학력 정보") Education education, @Schema(description = "학교 정보", implementation = SchoolInfoRequest.class)