diff --git a/src/main/java/life/mosu/mosuserver/application/school/SchoolService.java b/src/main/java/life/mosu/mosuserver/application/school/SchoolService.java index 714f160f..a12fa4f7 100644 --- a/src/main/java/life/mosu/mosuserver/application/school/SchoolService.java +++ b/src/main/java/life/mosu/mosuserver/application/school/SchoolService.java @@ -1,8 +1,8 @@ package life.mosu.mosuserver.application.school; - import java.util.List; import life.mosu.mosuserver.domain.school.SchoolJpaRepository; +import life.mosu.mosuserver.presentation.school.dto.AvailableSchoolResponse; import life.mosu.mosuserver.presentation.school.dto.SchoolRegistrationRequest; import life.mosu.mosuserver.presentation.school.dto.SchoolResponse; import lombok.RequiredArgsConstructor; @@ -15,12 +15,21 @@ public class SchoolService { private final SchoolJpaRepository schoolJpaRepository; + private final SchoolQuotaCacheManager schoolQuotaCacheManager; @Transactional(readOnly = true, propagation = Propagation.SUPPORTS) - public List getSchools() { + public List getAvailableSchools() { + return schoolJpaRepository.findAll() .stream() - .map(SchoolResponse::from) + .map(school -> { + SchoolResponse schoolResponse = SchoolResponse.from(school); + + Long currentQuantity = schoolQuotaCacheManager.getSchoolApplicationCounts( + schoolResponse.schoolName()); + + return AvailableSchoolResponse.from(schoolResponse, currentQuantity); + }) .toList(); } diff --git a/src/main/java/life/mosu/mosuserver/domain/school/SchoolJpaRepository.java b/src/main/java/life/mosu/mosuserver/domain/school/SchoolJpaRepository.java index 60228e10..ad0b7cc5 100644 --- a/src/main/java/life/mosu/mosuserver/domain/school/SchoolJpaRepository.java +++ b/src/main/java/life/mosu/mosuserver/domain/school/SchoolJpaRepository.java @@ -12,6 +12,8 @@ public interface SchoolJpaRepository extends JpaRepository countBySchoolNameGroupBy(); 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 108149b4..bcc45d4e 100644 --- a/src/main/java/life/mosu/mosuserver/global/initializer/DatabaseInitializer.java +++ b/src/main/java/life/mosu/mosuserver/global/initializer/DatabaseInitializer.java @@ -2,12 +2,14 @@ import jakarta.annotation.PostConstruct; import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.Set; +import java.util.UUID; import life.mosu.mosuserver.domain.application.ApplicationJpaEntity; import life.mosu.mosuserver.domain.application.ApplicationJpaRepository; import life.mosu.mosuserver.domain.application.Lunch; @@ -23,6 +25,11 @@ import life.mosu.mosuserver.domain.inquiryAnswer.InquiryAnswerJpaRepository; import life.mosu.mosuserver.domain.notice.NoticeJpaEntity; import life.mosu.mosuserver.domain.notice.NoticeJpaRepository; +import life.mosu.mosuserver.domain.payment.PaymentAmountVO; +import life.mosu.mosuserver.domain.payment.PaymentJpaEntity; +import life.mosu.mosuserver.domain.payment.PaymentMethod; +import life.mosu.mosuserver.domain.payment.PaymentRepository; +import life.mosu.mosuserver.domain.payment.PaymentStatus; import life.mosu.mosuserver.domain.profile.Education; import life.mosu.mosuserver.domain.profile.Gender; import life.mosu.mosuserver.domain.profile.Grade; @@ -55,6 +62,7 @@ public class DatabaseInitializer { private final NoticeJpaRepository noticeJpaRepository; private final InquiryAnswerJpaRepository inquiryAnswerJpaRepository; private final EventJpaRepository eventRepository; + private final PaymentRepository paymentRepository; private final PasswordEncoder passwordEncoder; @PostConstruct @@ -69,7 +77,9 @@ public void init() { List createdUsers = initializeUsersAndProfiles(random); List createdSchools = initializeSchools(); - initializeApplications(createdUsers, createdSchools, random); + List createdAppSchools = initializeApplications(createdUsers, + createdSchools, random); + initializePayments(createdAppSchools); initializeBoardItems(createdUsers, random); log.info("모든 더미 데이터 초기화가 완료되었습니다."); @@ -115,32 +125,64 @@ private List initializeUsersAndProfiles(Random random) { private List initializeSchools() { List schools = new ArrayList<>(List.of( SchoolJpaEntity.builder() - .schoolName("모수고등학교") + .schoolName("대치중학교") .area(Area.DAECHI) - .address(new AddressJpaVO("06164", "서울특별시", "강남구 테헤란로 123")) - .examDate(LocalDate.of(2025, 11, 20)) - .capacity(300L) + .address(new AddressJpaVO("06234", "서울특별시", "강남구 대치동 987")) + .examDate(LocalDate.of(2025, 10, 19)) + .capacity(532L) + .deadlineTime(LocalDateTime.of(2025, 10, 10, 23, 59)) + .lunch(Lunch.OPTION1) + .lunchPrice(12000) .build(), SchoolJpaEntity.builder() - .schoolName("대치고등학교") - .area(Area.DAECHI) - .address(new AddressJpaVO("06283", "서울특별시", "강남구 대치동 학원가 100")) - .examDate(LocalDate.of(2025, 12, 5)) - .capacity(150L) + .schoolName("목운중학교") + .area(Area.MOKDONG) + .address(new AddressJpaVO("07995", "서울특별시", "양천구 목동서로 369")) + .examDate(LocalDate.of(2025, 10, 26)) + .capacity(896L) + .deadlineTime(LocalDateTime.of(2025, 10, 17, 23, 59)) + .lunch(Lunch.OPTION1) + .lunchPrice(12000) + .build(), + SchoolJpaEntity.builder() + .schoolName("신서중학교") + .area(Area.MOKDONG) + .address(new AddressJpaVO("08018", "서울특별시", "양천구 신정로 250")) + .examDate(LocalDate.of(2025, 11, 2)) + .capacity(896L) + .deadlineTime(LocalDateTime.of(2025, 10, 24, 23, 59)) + .lunch(Lunch.OPTION2) + .lunchPrice(13000) .build(), SchoolJpaEntity.builder() - .schoolName("부산명문고") + .schoolName("개원중학교") .area(Area.DAECHI) - .address(new AddressJpaVO("48057", "부산광역시", "해운대구 센텀시티로 50")) - .examDate(LocalDate.of(2025, 11, 25)) - .capacity(250L) + .address(new AddressJpaVO("06327", "서울특별시", "강남구 개포로 619")) + .examDate(LocalDate.of(2025, 10, 26)) + .capacity(840L) + .deadlineTime(LocalDateTime.of(2025, 10, 17, 23, 59)) + .lunch(Lunch.OPTION1) + .lunchPrice(12000) + .build(), + SchoolJpaEntity.builder() + .schoolName("문래중학교") + .area(Area.MOKDONG) + .address(new AddressJpaVO("07291", "서울특별시", "영등포구 문래로 195")) + .examDate(LocalDate.of(2025, 10, 19)) + .capacity(558L) + .deadlineTime(LocalDateTime.of(2025, 10, 10, 23, 59)) + .lunch(Lunch.OPTION2) + .lunchPrice(13000) .build(), SchoolJpaEntity.builder() - .schoolName("노원스터디센터") + .schoolName("온곡중학교") .area(Area.NOWON) - .address(new AddressJpaVO("01777", "서울특별시", "노원구 동일로 1400")) - .examDate(LocalDate.of(2026, 1, 10)) - .capacity(80L) + .address(new AddressJpaVO("01673", "서울특별시", "노원구 덕릉로 70길 99")) + .examDate(LocalDate.of(2025, 10, 19)) + .capacity(448L) + .deadlineTime(LocalDateTime.of(2025, 10, 10, 23, 59)) + .lunch(Lunch.OPTION1) + .lunchPrice(12000) .build() )); schoolRepository.saveAll(schools); @@ -148,8 +190,10 @@ private List initializeSchools() { return schools; } - private void initializeApplications(List users, List schools, + private List initializeApplications(List users, + List schools, Random random) { + List createdAppSchools = new ArrayList<>(); for (int i = 0; i < users.size(); i++) { UserJpaEntity user = users.get(i); ApplicationJpaEntity application = applicationRepository.save( @@ -172,7 +216,7 @@ private void initializeApplications(List users, List users, List applicationSchools) { + List payments = new ArrayList<>(); + int successCount = applicationSchools.size() / 2; + + for (int i = 0; i < applicationSchools.size(); i++) { + ApplicationSchoolJpaEntity appSchool = applicationSchools.get(i); + String orderId = "order-" + UUID.randomUUID().toString().substring(0, 8); + + if (i < successCount) { + PaymentAmountVO paymentAmount = PaymentAmountVO.of( + 12000, + 10909, + 1091, + 12000, + 0 + ); + + PaymentJpaEntity payment = PaymentJpaEntity.of( + appSchool.getId(), + "pkey-" + UUID.randomUUID().toString().substring(0, 12), + orderId, + PaymentStatus.DONE, + paymentAmount, + PaymentMethod.CARD + ); + payments.add(payment); + } else { + PaymentJpaEntity payment = PaymentJpaEntity.ofFailure( + appSchool.getId(), + orderId, + PaymentStatus.ABORTED, + 12000 + ); + payments.add(payment); + } + } + paymentRepository.saveAll(payments); + log.info("Payment 데이터 {}건 생성 완료 (성공: {}, 실패: {}).", payments.size(), successCount, + payments.size() - successCount); } private void initializeBoardItems(List users, Random random) { @@ -241,4 +328,4 @@ private void initializeBoardItems(List users, Random random) { } log.info("Board(Notice, Inquiry, Event) 데이터 생성 완료."); } -} \ No newline at end of file +} diff --git a/src/main/java/life/mosu/mosuserver/presentation/school/SchoolController.java b/src/main/java/life/mosu/mosuserver/presentation/school/SchoolController.java index 90634930..6e764ecc 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/school/SchoolController.java +++ b/src/main/java/life/mosu/mosuserver/presentation/school/SchoolController.java @@ -3,8 +3,8 @@ import java.util.List; import life.mosu.mosuserver.application.school.SchoolService; import life.mosu.mosuserver.global.util.ApiResponseWrapper; +import life.mosu.mosuserver.presentation.school.dto.AvailableSchoolResponse; import life.mosu.mosuserver.presentation.school.dto.SchoolRegistrationRequest; -import life.mosu.mosuserver.presentation.school.dto.SchoolResponse; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -31,9 +31,9 @@ public ResponseEntity> create( } @GetMapping - public ResponseEntity>> getSchools() { + public ResponseEntity>> getSchools() { - List schools = schoolService.getSchools(); + List schools = schoolService.getAvailableSchools(); return ResponseEntity.ok(ApiResponseWrapper.success(HttpStatus.OK, "학교 조회 성공", schools)); } diff --git a/src/main/java/life/mosu/mosuserver/presentation/school/SchoolControllerDocs.java b/src/main/java/life/mosu/mosuserver/presentation/school/SchoolControllerDocs.java index 10c5ed9f..60fcf660 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/school/SchoolControllerDocs.java +++ b/src/main/java/life/mosu/mosuserver/presentation/school/SchoolControllerDocs.java @@ -9,8 +9,8 @@ import io.swagger.v3.oas.annotations.tags.Tag; import java.util.List; import life.mosu.mosuserver.global.util.ApiResponseWrapper; +import life.mosu.mosuserver.presentation.school.dto.AvailableSchoolResponse; import life.mosu.mosuserver.presentation.school.dto.SchoolRegistrationRequest; -import life.mosu.mosuserver.presentation.school.dto.SchoolResponse; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestBody; @@ -19,17 +19,15 @@ public interface SchoolControllerDocs { @Operation(summary = "[관리자] 학교 정보 등록", description = "새로운 학교 정보를 시스템에 등록합니다. 인가 추가 예정") @ApiResponses(value = { - @ApiResponse(responseCode = "201", description = "학교 등록 성공"), - @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터"), - @ApiResponse(responseCode = "409", description = "이미 존재하는 학교 정보") + @ApiResponse(responseCode = "201", description = "학교 등록 성공") }) ResponseEntity> create(@RequestBody SchoolRegistrationRequest request); @Operation(summary = "[사용자] 전체 학교 목록 조회", description = "시스템에 등록된 모든 학교 목록을 조회합니다. 인가 추가 예정") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "학교 목록 조회 성공", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = SchoolResponse.class)))), + content = @Content(array = @ArraySchema(schema = @Schema(implementation = AvailableSchoolResponse.class)))), @ApiResponse(responseCode = "500", description = "서버 내부 오류") }) - ResponseEntity>> getSchools(); + ResponseEntity>> getSchools(); } \ No newline at end of file diff --git a/src/main/java/life/mosu/mosuserver/presentation/school/dto/AvailableSchoolResponse.java b/src/main/java/life/mosu/mosuserver/presentation/school/dto/AvailableSchoolResponse.java new file mode 100644 index 00000000..04536c88 --- /dev/null +++ b/src/main/java/life/mosu/mosuserver/presentation/school/dto/AvailableSchoolResponse.java @@ -0,0 +1,35 @@ +package life.mosu.mosuserver.presentation.school.dto; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +public record AvailableSchoolResponse( + Long id, + String schoolName, + String area, + String lunch, + Integer lunchPrice, + LocalDateTime deadlineTime, + LocalDate examDate, + Long currentQuantity, + Long capacity +) { + + public static AvailableSchoolResponse from( + SchoolResponse school, + Long currentQuantity + ) { + return new AvailableSchoolResponse( + school.id(), + school.schoolName(), + school.area(), + school.lunch(), + school.lunchPrice(), + school.deadlineTime(), + school.examDate(), + currentQuantity, + school.capacity() + ); + } + +} diff --git a/src/main/java/life/mosu/mosuserver/presentation/school/dto/SchoolResponse.java b/src/main/java/life/mosu/mosuserver/presentation/school/dto/SchoolResponse.java index 118ba95b..d78d45bf 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/school/dto/SchoolResponse.java +++ b/src/main/java/life/mosu/mosuserver/presentation/school/dto/SchoolResponse.java @@ -1,14 +1,16 @@ package life.mosu.mosuserver.presentation.school.dto; import java.time.LocalDate; +import java.time.LocalDateTime; import life.mosu.mosuserver.domain.school.SchoolJpaEntity; -import life.mosu.mosuserver.presentation.common.AddressResponse; public record SchoolResponse( Long id, String schoolName, String area, - AddressResponse address, + String lunch, + Integer lunchPrice, + LocalDateTime deadlineTime, LocalDate examDate, Long capacity ) { @@ -17,8 +19,10 @@ public static SchoolResponse from(SchoolJpaEntity school) { return new SchoolResponse( school.getId(), school.getSchoolName(), - school.getArea().name(), - AddressResponse.from(school.getAddress()), + school.getArea().getAreaName(), + school.getLunch().getLunchName(), + school.getLunchPrice(), + school.getDeadlineTime(), school.getExamDate(), school.getCapacity() );