Skip to content

MOSU-39 feat: 신청 관련 기능 수정 및 관리자 신청 조회 관련 기능 구현#60

Merged
chominju02 merged 17 commits intodevelopfrom
feature/mosu-39
Jul 9, 2025
Merged

MOSU-39 feat: 신청 관련 기능 수정 및 관리자 신청 조회 관련 기능 구현#60
chominju02 merged 17 commits intodevelopfrom
feature/mosu-39

Conversation

@chominju02
Copy link
Contributor

@chominju02 chominju02 commented Jul 8, 2025

✨ 구현한 기능

  • 이전의 리뷰대로 수정 완료
  • 관리자 신청 조회 기능 구현
  • 관리자 신청 조회 엑셀 다운로드 기능 구현
  • 관리자 학교별 도시락 수 조회 기능 구현
  • profile, application 관련 @userid 적용

📢 논의하고 싶은 내용

  • 추후 관리자 신청 조회 기능 부분에 있어 쿼리 최적화가 필요할 것 같습니다.

🎸 기타

Summary by CodeRabbit

  • New Features

    • Added admin endpoints to view and export application and school lunch data, including Excel download capability.
    • Introduced new filters and data transfer objects for enhanced application management.
    • Added support for guardian phone number in application forms.
  • Enhancements

    • Improved phone number validation to support a wider range of formats.
    • Updated user and application endpoints to use security-based user identification instead of request parameters.
    • Provided more descriptive labels for enums such as gender, grade, and education.
  • Bug Fixes

    • Fixed subject fields in responses to return subject names instead of objects.
  • Chores

    • Updated .gitignore to exclude additional environment files.
    • Adjusted Redis configuration in application settings.

@chominju02 chominju02 added this to the 모수 MVP API 구현 milestone Jul 8, 2025
@chominju02 chominju02 self-assigned this Jul 8, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jul 8, 2025

Walkthrough

This update introduces new administrative features for managing and exporting application data, including endpoints for retrieving school lunch counts and application lists with filtering and Excel export. It refactors DTOs and domain models to support additional fields and validation, improves phone number validation, and changes user identification in controllers to use security context rather than request parameters.

Changes

File(s) / Group Change Summary
.gitignore Added docker-compose/.env.local to ignored files.
src/main/resources/application.yml Changed Redis port env var and removed Redis password.
src/main/java/life/mosu/mosuserver/global/annotation/PhoneNumberPattern.java Relaxed phone number regex and updated error message.
src/main/java/life/mosu/mosuserver/global/config/ObjectMapperConfig.java Configured ObjectMapper for JavaTimeModule and readable date serialization.
src/main/java/life/mosu/mosuserver/domain/application/ApplicationJpaEntity.java Added guardianPhoneNumber field and updated constructor.
src/main/java/life/mosu/mosuserver/domain/profile/Education.java
Gender.java
Grade.java
Refactored enums to include string labels and added Lombok annotations.
src/main/java/life/mosu/mosuserver/domain/school/SchoolJpaRepository.java Added new JPA repository interface for schools.
src/main/java/life/mosu/mosuserver/domain/admin/ApplicationQueryRepository.java
ApplicationQueryRepositoryImpl.java
Introduced application query repository interface and implementation with search/export methods.
src/main/java/life/mosu/mosuserver/application/admin/AdminService.java Added methods for lunch counts, application list, and Excel export.
src/main/java/life/mosu/mosuserver/application/application/ApplicationService.java Added dependency on SchoolJpaRepository and changed school entity creation logic.
src/main/java/life/mosu/mosuserver/application/applicationschool/ApplicationSchoolService.java Simplified method signatures (removed unused params), mapped subjects to names.
src/main/java/life/mosu/mosuserver/presentation/admin/AdminController.java Added endpoints for lunch counts, application list, and Excel export; secured with @PreAuthorize.
src/main/java/life/mosu/mosuserver/presentation/admin/dto/ApplicationExcelDto.java
ApplicationFilter.java
ApplicationListResponse.java
SchoolLunchResponse.java
Added new DTOs/records for admin features: Excel export, filtering, listing, and lunch counts.
src/main/java/life/mosu/mosuserver/presentation/application/ApplicationController.java
applicationschool/ApplicationSchoolController.java
profile/ProfileController.java
Changed user ID injection from request param to @UserId annotation; updated method signatures accordingly.
src/main/java/life/mosu/mosuserver/presentation/application/dto/ApplicationRequest.java Added guardianPhoneNumber with validation; updated entity conversion.
src/main/java/life/mosu/mosuserver/presentation/application/dto/ApplicationSchoolRequest.java Added validation annotations; changed toEntity to accept address object.
src/main/java/life/mosu/mosuserver/presentation/application/dto/ApplicationSchoolResponse.java
applicationschool/dto/AdmissionTicketResponse.java
Changed subjects field from Set<Subject> to Set<String>; updated mapping logic.
src/main/java/life/mosu/mosuserver/presentation/profile/dto/EditProfileRequest.java
ProfileRequest.java
Replaced inline phone regex with @PhoneNumberPattern; added date format annotation.

Sequence Diagram(s)

sequenceDiagram
  participant Admin as AdminController
  participant Service as AdminService
  participant Repo as ApplicationQueryRepositoryImpl
  participant Excel as ExcelExport
  participant DB as Database

  Admin->>Service: getLunchCounts()
  Service->>Repo: searchAllSchoolLunches()
  Repo->>DB: Query lunch counts grouped by school
  DB-->>Repo: Lunch count data
  Repo-->>Service: List<SchoolLunchResponse>
  Service-->>Admin: ApiResponseWrapper<List<SchoolLunchResponse>>

  Admin->>Service: getApplications(filter, pageable)
  Service->>Repo: searchAllApplications(filter, pageable)
  Repo->>DB: Query applications with filters and pagination
  DB-->>Repo: Application data
  Repo-->>Service: Page<ApplicationListResponse>
  Service-->>Admin: ApiResponseWrapper<Page<ApplicationListResponse>>

  Admin->>Service: getApplicationExcelData()
  Service->>Repo: searchAllApplicationsForExcel()
  Repo->>DB: Query all applications for export
  DB-->>Repo: Application data
  Repo-->>Service: List<ApplicationExcelDto>
  Service->>Excel: Generate Excel file
  Excel-->>Admin: Excel file response
Loading

Poem

A rabbit hopped through fields of code,
Bringing admin tools in a brand new mode.
Lunch counts, Excel sheets, and filters galore—
With enums that speak and phone checks at the door!
Now user IDs leap from context, not a param,
And with each refactor, the system says, "Wham!"
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @chominju02, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly expands the administrative capabilities for managing applications, introduces new data fields for applications, and refines existing user-facing application and profile features. Key changes include the implementation of new admin API endpoints for application inquiry and export, enhanced application submission logic to capture more detailed school information, and a refactoring of user ID handling across several controllers for improved consistency and security.

Highlights

  • New Admin Application Management Features: Implemented comprehensive administrator functionalities for application management, including paginated listing of applications, Excel export of application data, and a new endpoint to query school-specific lunch application counts. All new and existing admin endpoints are secured with @PreAuthorize("isAuthenticated() and hasRole('ADMIN')").
  • Enhanced Application Submission Logic: The application submission process (ApplicationService.apply) now incorporates the guardian's phone number and fetches school address details from the SchoolJpaRepository to ensure more complete and accurate application records. Validation for application school requests has also been refined.
  • Refactored User ID Handling and Authorization: Standardized the extraction of userId in various controllers (ApplicationController, ApplicationSchoolController, ProfileController) by replacing @RequestParam Long userId with a custom @UserId annotation. This improves consistency and potentially simplifies authentication integration. User-facing application endpoints are now explicitly secured with @PreAuthorize("isAuthenticated() and hasRole('USER')").
  • Improved Data Representation and Validation: Enums for Education, Gender, and Grade now include descriptive names for better display. Phone number validation has been broadened to support more formats using a custom @PhoneNumberPattern annotation. ObjectMapper configuration was updated to correctly handle Java 8 Date/Time API objects, and subject lists in various DTOs are now represented as Set<String> for clearer presentation.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request includes several new features related to application management and admin functionalities. It also incorporates modifications based on previous reviews and applies @UserId annotation and phone number validation. The changes include new DTOs, repository methods, and controller endpoints for managing applications, school lunches, and generating Excel reports. Security annotations have been added to restrict access to admin-related endpoints.

Comment on lines 8 to +10
public enum Education {
ENROLLED, // 재학생
GRADUATED // 졸업생
ENROLLED("재학생"),
GRADUATED("졸업생");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Consider adding a JavaDoc comment to explain what this enum represents.

/**
 * Represents the education level of a student.
 */
@Getter
@RequiredArgsConstructor
public enum Education {

Comment on lines +6 to 8
@Getter
@RequiredArgsConstructor
public enum Gender {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Consider adding a JavaDoc comment to explain what this enum represents.

/**
 * Represents the gender of a person.
 */
@Getter
@RequiredArgsConstructor
public enum Gender {

Comment on lines +6 to 8
@Getter
@RequiredArgsConstructor
public enum Grade {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Consider adding a JavaDoc comment to explain what this enum represents.

/**
 * Represents the grade level of a student.
 */
@Getter
@RequiredArgsConstructor
public enum Grade {

Comment on lines +13 to +16
public ObjectMapper mapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Consider extracting the object mapper configuration to a separate method for better readability and maintainability.

    @Bean
    public ObjectMapper mapper() {
        ObjectMapper objectMapper = createObjectMapper();
        return objectMapper;
    }

    private ObjectMapper createObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        return objectMapper;
    }

Comment on lines +19 to +21
@JsonFormat(pattern = "yyyy-MM-dd")
@NotNull(message = "생년월일은 필수입니다.")
LocalDate birth,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Consider adding a description to the JsonFormat annotation to explain the date format being used.

Suggested change
@JsonFormat(pattern = "yyyy-MM-dd")
@NotNull(message = "생년월일은 필수입니다.")
LocalDate birth,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul")
@NotNull(message = "생년월일은 필수입니다.")
LocalDate birth,

) {
ApplicationSchoolResponse response = applicationSchoolService.updateSubjects(userId, applicationSchoolId, request);
ApplicationSchoolResponse response = applicationSchoolService.updateSubjects(
applicationSchoolId, request);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The updateSubjects method in ApplicationSchoolService does not use the userId parameter. Remove the parameter from the controller method signature to avoid confusion.

        ApplicationSchoolResponse response = applicationSchoolService.updateSubjects(
                applicationSchoolId, request);

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🔭 Outside diff range comments (1)
src/main/java/life/mosu/mosuserver/presentation/admin/AdminController.java (1)

47-63: Add error handling for Excel file generation.

While the Excel download implementation is functional, consider adding try-catch blocks to handle potential errors during file generation and provide appropriate error responses to clients.

 @GetMapping("/excel/students")
 @PreAuthorize("isAuthenticated() and hasRole('ADMIN')")
 public void downloadStudentInfo(
         HttpServletResponse response) throws IOException {
-    String fileName = URLEncoder.encode("학생정보목록.xlsx", StandardCharsets.UTF_8)
-            .replaceAll("\\+", "%20");
-
-    response.setContentType(
-            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
-    response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + fileName);
-
-    List<StudentExcelDto> data = adminService.getStudentExcelData();
-    SimpleExcelFile<StudentExcelDto> excelFile = new SimpleExcelFile<>(data,
-            StudentExcelDto.class);
-
-    excelFile.write(response.getOutputStream());
+    try {
+        String fileName = URLEncoder.encode("학생정보목록.xlsx", StandardCharsets.UTF_8)
+                .replaceAll("\\+", "%20");
+
+        response.setContentType(
+                "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+        response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + fileName);
+
+        List<StudentExcelDto> data = adminService.getStudentExcelData();
+        SimpleExcelFile<StudentExcelDto> excelFile = new SimpleExcelFile<>(data,
+                StudentExcelDto.class);
+
+        excelFile.write(response.getOutputStream());
+    } catch (Exception e) {
+        log.error("Failed to generate student Excel file", e);
+        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+        response.getWriter().write("Failed to generate Excel file");
+    }
 }
♻️ Duplicate comments (4)
src/main/java/life/mosu/mosuserver/presentation/admin/AdminController.java (1)

83-99: Apply the same error handling as suggested for the student Excel download.

This method has the same error handling issue as the downloadStudentInfo method.

src/main/java/life/mosu/mosuserver/domain/admin/ApplicationQueryRepositoryImpl.java (1)

84-95: Same N+1 query issue for Excel export - potentially more critical.

This method has the same N+1 query problem, but it's potentially more critical here since Excel exports typically involve larger datasets without pagination.

src/main/java/life/mosu/mosuserver/presentation/applicationschool/ApplicationSchoolController.java (2)

39-48: Remove unused @userid parameter.

Similar to the previous method, the @UserId parameter is declared but not used in the service call.


50-58: Remove unused @userid parameter.

The @UserId parameter is not used in the service call.

🧹 Nitpick comments (3)
src/main/java/life/mosu/mosuserver/domain/user/UserJpaEntity.java (1)

34-36: Column length for Enum may be over-provisioned

Adding nullable = false is spot-on.
However, specifying length = 50 on an EnumType.STRING column is usually unnecessary—JPA will size the column to the longest enum constant name (often far below 50). Consider trimming this to a tighter bound (e.g., length = 20) or omitting it altogether to keep DDL concise.

-    @Column(name = "user_role", nullable = false, length = 50)
+    @Column(name = "user_role", nullable = false, length = 20)  // or remove `length`
src/main/java/life/mosu/mosuserver/presentation/admin/AdminController.java (1)

37-45: Security annotation is correct but consider using method-level security configuration.

The security annotation correctly enforces admin role authentication. For consistency and to avoid repetition, consider using a class-level @PreAuthorize annotation or creating a custom security annotation.

src/main/java/life/mosu/mosuserver/presentation/applicationschool/ApplicationSchoolController.java (1)

29-37: Good refactoring to use @userid annotation.

The removal of explicit userId parameter from the service call aligns with the security context-based user identification pattern. The @UserId parameter is still declared but not used - consider removing it if it's not needed.

 @DeleteMapping("/{applicationSchoolId}/cancel")
 public ApiResponseWrapper<Void> cancelApplicationSchool(
         @PathVariable("applicationSchoolId") Long applicationSchoolId,
-        @UserId Long userId,
         @RequestBody RefundRequest request
 ) {
     applicationSchoolService.cancelApplicationSchool(applicationSchoolId, request);
     return ApiResponseWrapper.success(HttpStatus.OK, "신청 학교 취소 및 환불 처리 완료");
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 52f458c and 11ff2d1.

📒 Files selected for processing (29)
  • .gitignore (1 hunks)
  • src/main/java/life/mosu/mosuserver/application/admin/AdminService.java (3 hunks)
  • src/main/java/life/mosu/mosuserver/application/application/ApplicationService.java (4 hunks)
  • src/main/java/life/mosu/mosuserver/application/applicationschool/ApplicationSchoolService.java (2 hunks)
  • src/main/java/life/mosu/mosuserver/domain/admin/ApplicationQueryRepository.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/domain/admin/ApplicationQueryRepositoryImpl.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/domain/application/ApplicationJpaEntity.java (3 hunks)
  • src/main/java/life/mosu/mosuserver/domain/profile/Education.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/domain/profile/Gender.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/domain/profile/Grade.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/domain/school/SchoolJpaRepository.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/domain/user/UserJpaEntity.java (2 hunks)
  • src/main/java/life/mosu/mosuserver/global/annotation/PhoneNumberPattern.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/global/config/ObjectMapperConfig.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/admin/AdminController.java (5 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/admin/dto/ApplicationExcelDto.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/admin/dto/ApplicationFilter.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/admin/dto/ApplicationListResponse.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/admin/dto/SchoolLunchResponse.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/application/ApplicationController.java (2 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/application/dto/ApplicationRequest.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/application/dto/ApplicationSchoolRequest.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/application/dto/ApplicationSchoolResponse.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/applicationschool/ApplicationSchoolController.java (3 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/applicationschool/dto/AdmissionTicketResponse.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/profile/ProfileController.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/profile/dto/EditProfileRequest.java (2 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/profile/dto/ProfileRequest.java (2 hunks)
  • src/main/resources/application.yml (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/main/java/life/mosu/mosuserver/domain/profile/Grade.java (1)
src/main/java/life/mosu/mosuserver/domain/profile/SchoolInfoJpaVO.java (1)
  • Getter (10-25)
src/main/java/life/mosu/mosuserver/domain/profile/Education.java (2)
src/main/java/life/mosu/mosuserver/domain/school/AddressJpaVO.java (1)
  • Getter (10-34)
src/main/java/life/mosu/mosuserver/domain/profile/SchoolInfoJpaVO.java (1)
  • Getter (10-25)
🔇 Additional comments (49)
src/main/java/life/mosu/mosuserver/domain/user/UserJpaEntity.java (1)

3-10: Explicit JPA imports – nice cleanup

Replacing the wildcard with explicit imports makes the dependency list clearer and avoids accidental inclusion of unused classes. 👍

.gitignore (1)

40-40: LGTM! Good security practice.

Adding .env.local to the ignore rules prevents accidental commits of local environment configurations, which is a good security practice.

src/main/java/life/mosu/mosuserver/domain/school/SchoolJpaRepository.java (1)

1-7: LGTM! Standard JPA repository implementation.

This is a clean, minimal JPA repository following Spring Data JPA conventions. The interface provides all necessary CRUD operations for SchoolJpaEntity.

src/main/java/life/mosu/mosuserver/presentation/applicationschool/dto/AdmissionTicketResponse.java (2)

11-11: LGTM! Good architectural improvement.

Changing the subjects field from Set<Subject> to Set<String> reduces coupling between the presentation layer and domain model, which is a good architectural practice.


15-31: LGTM! Factory method properly updated.

The factory method signature and implementation are correctly updated to match the new Set<String> type for subjects.

src/main/java/life/mosu/mosuserver/presentation/application/dto/ApplicationSchoolResponse.java (3)

18-18: LGTM! Consistent with architectural improvements.

The field type change from Set<Subject> to Set<String> is consistent with similar changes in AdmissionTicketResponse and maintains good architectural separation.


22-24: LGTM! Clean conversion logic.

The conversion from Subject entities to their names using streams is clean and efficient. The use of Collectors.toSet() ensures proper collection type conversion.


26-34: LGTM! Factory method properly updated.

The factory method correctly uses the converted subject names and maintains consistency with the new field type.

src/main/java/life/mosu/mosuserver/domain/profile/Gender.java (2)

3-7: Well-structured enum enhancement for internationalization.

The addition of Lombok annotations and Korean labels follows good practices for internationalization. The @RequiredArgsConstructor will generate a constructor that initializes the genderName field properly.


9-12: Clean enum constant definitions with descriptive labels.

The enum constants with Korean labels ("남자", "여자") provide clear localization support. The private final field ensures immutability and proper encapsulation.

src/main/java/life/mosu/mosuserver/domain/admin/ApplicationQueryRepository.java (1)

13-18: Well-defined repository interface methods.

The method signatures are clear and follow Spring Data conventions. The separation of concerns between paginated results, Excel export, and school lunch data is appropriate.

src/main/java/life/mosu/mosuserver/presentation/application/dto/ApplicationSchoolRequest.java (4)

3-4: Good addition of validation imports.

The Jakarta validation constraints are properly imported for enhanced input validation.


17-31: Comprehensive validation annotations with Korean messages.

The validation annotations are appropriately applied to required fields. The Korean validation messages are consistent with the internationalization approach seen in other parts of the codebase.


34-46: Improved method signature for better separation of concerns.

The updated toEntity method now accepts AddressJavaVO as a parameter instead of deriving it internally. This improves separation of concerns and makes the method more explicit about its dependencies.


51-53: Minor formatting improvement in stream pipeline.

The stream pipeline formatting is cleaner and more readable.

src/main/java/life/mosu/mosuserver/application/application/ApplicationService.java (5)

3-3: Appropriate import for ArrayList initialization.

The ArrayList import is correctly added for the new school entities list initialization.


13-15: Proper imports for enhanced school entity handling.

The imports for AddressJavaVO, SchoolJavaEntity, and SchoolJavaRepository are correctly added to support the enhanced application logic.


36-36: Appropriate dependency injection for school repository.

The SchoolJavaRepository dependency is correctly injected to support school entity fetching during application processing.


42-42: Good initialization of school entities list.

The explicit initialization of the schoolEntities list is clear and appropriate for the enhanced logic.


60-69: Enhanced logic with proper error handling.

The enhanced logic properly fetches school entities and handles the case where a school is not found by throwing a CustomRuntimeException. The address extraction and passing to the toEntity method is correctly implemented.

src/main/java/life/mosu/mosuserver/presentation/admin/dto/SchoolLunchResponse.java (1)

3-8: LGTM! Well-designed response DTO.

The record implementation is appropriate for this use case. Using a record for immutable response data is a good choice, and the field types are suitable for representing school lunch statistics.

src/main/java/life/mosu/mosuserver/global/config/ObjectMapperConfig.java (1)

13-18: Excellent configuration for consistent date/time handling.

The addition of JavaTimeModule and disabling WRITE_DATES_AS_TIMESTAMPS ensures consistent ISO-8601 date formatting across the application. This is particularly important for the new admin DTOs that contain LocalDate and LocalDateTime fields.

src/main/java/life/mosu/mosuserver/presentation/profile/dto/EditProfileRequest.java (1)

25-25: Good refactoring to use custom validation annotation.

The change from @pattern to @PhoneNumberPattern promotes consistency across the application and centralizes phone number validation logic. This makes the code more maintainable and reusable.

src/main/java/life/mosu/mosuserver/presentation/admin/dto/ApplicationFilter.java (1)

7-14: Well-designed filter DTO with appropriate validation.

The record implementation is clean and appropriate for filtering application data. The use of @PhoneNumberPattern maintains consistency with other DTOs, and the optional nature of all fields provides flexibility for admin queries.

src/main/java/life/mosu/mosuserver/presentation/profile/ProfileController.java (1)

51-51: Excellent security improvement using @userid annotation.

The change from @RequestParam to @userid annotation is a significant security improvement. It prevents user ID manipulation by extracting the user ID from the authenticated security context rather than relying on client-provided parameters.

src/main/java/life/mosu/mosuserver/domain/profile/Grade.java (1)

6-13: LGTM! Good enum enhancement with descriptive Korean names.

The addition of Korean descriptive names to the Grade enum improves internationalization and display capabilities. The use of Lombok annotations follows best practices for reducing boilerplate code.

src/main/java/life/mosu/mosuserver/presentation/application/dto/ApplicationRequest.java (2)

11-12: LGTM! Good addition of guardian phone number with consistent validation.

The addition of the guardianPhoneNumber field enhances the application data model, and both phone number fields now use the consistent @PhoneNumberPattern validation annotation.


20-20: LGTM! Properly integrated the new field in entity conversion.

The toEntity method correctly includes the new guardianPhoneNumber field in the builder pattern.

src/main/java/life/mosu/mosuserver/domain/profile/Education.java (1)

6-12: LGTM! Consistent enum enhancement pattern.

The Education enum follows the same enhancement pattern as Grade.java, adding appropriate Korean descriptive names ("재학생" for enrolled, "졸업생" for graduated) with proper Lombok annotations.

src/main/java/life/mosu/mosuserver/presentation/profile/dto/ProfileRequest.java (2)

19-21: LGTM! Good addition of JSON date formatting.

The @JsonFormat(pattern = "yyyy-MM-dd") annotation on the birth field ensures consistent date formatting in JSON serialization/deserialization.


27-27: LGTM! Consistent phone number validation.

Switching from direct @Pattern to the custom @PhoneNumberPattern annotation provides consistent phone number validation across the application.

src/main/java/life/mosu/mosuserver/global/annotation/PhoneNumberPattern.java (2)

12-13: LGTM! Improved phone number validation pattern.

The updated regex pattern ^01[016789]-\d{3,4}-\d{4}$ now supports more Korean mobile prefixes (010, 011, 016, 017, 018, 019) while maintaining proper validation structure. The message formatting improvement with proper spacing is also good.


20-20: LGTM! Consistent message formatting.

The default message method matches the annotation parameter message with proper spacing.

src/main/java/life/mosu/mosuserver/application/applicationschool/ApplicationSchoolService.java (3)

4-5: LGTM! Clean addition of necessary imports.

The added imports for Set and Collectors support the new subject name conversion logic appropriately.


107-109: Excellent refactoring of subject data representation.

The conversion from Subject entities to Set<String> of subject names is well-implemented using streams. This simplifies the data structure for response DTOs and improves consistency across the application.


45-48: Proper removal of userId parameter aligns with security context usage.

The method signature simplification by removing the userId parameter is consistent with the @userid annotation usage in the controller layer, indicating proper security context integration.

src/main/java/life/mosu/mosuserver/application/admin/AdminService.java (2)

25-25: Good addition of repository dependency.

The addition of ApplicationQueryRepositoryImpl dependency follows proper dependency injection patterns and supports the new administrative features.


35-47: Clean implementation of administrative service methods.

The three new methods properly delegate to the repository layer, maintaining good separation of concerns:

  • getLunchCounts() for school lunch aggregation
  • getApplications() for paginated application queries
  • getApplicationExcelData() for Excel export functionality

The method signatures are clear and follow established patterns.

src/main/java/life/mosu/mosuserver/presentation/admin/dto/ApplicationListResponse.java (1)

13-33: Well-designed comprehensive record for application data.

The ApplicationListResponse record effectively captures all necessary application information with:

  • Proper use of Java types (LocalDate, LocalDateTime, Set<String>)
  • Complete coverage of personal, educational, payment, and examination details
  • Consistent use of domain enums (Gender, Education, Grade, PaymentStatus, PaymentMethod)
  • The guardianPhoneNumber field addition enhances data completeness

The immutable record approach is ideal for this data transfer object.

src/main/java/life/mosu/mosuserver/presentation/application/ApplicationController.java (3)

30-30: Excellent addition of role-based security.

The @PreAuthorize("isAuthenticated() and hasRole('USER')") annotation properly secures the application endpoint, ensuring only authenticated users with the USER role can access the functionality.


32-32: Great refactoring to use security context for user identification.

The replacement of @RequestParam Long userId with @UserId Long userId in both methods demonstrates proper security context usage, eliminating the need for explicit user ID parameters in requests.

Also applies to: 42-42


41-41: Better method naming for clarity.

The method rename from getAll to getApplications provides better semantic clarity about what the endpoint returns.

src/main/java/life/mosu/mosuserver/presentation/admin/dto/ApplicationExcelDto.java (1)

15-67: Well-structured Excel DTO with appropriate type choices.

The ApplicationExcelDto class is well-designed for Excel export with:

  • Proper use of @ExcelColumn annotations with Korean headers for localization
  • Comprehensive field coverage matching the application data structure
  • Appropriate use of String types for display fields (gender, educationLevel, grade, applicationDate) which is suitable for Excel formatting
  • Consistent use of Set<String> for subjects matching the broader refactoring
  • Final fields ensuring immutability

The class effectively bridges domain objects and Excel export requirements.

src/main/java/life/mosu/mosuserver/presentation/admin/AdminController.java (2)

65-72: Well-implemented endpoint for lunch counts.

The implementation is clean and follows the established pattern with proper security and response wrapping.


73-82: Properly implemented paginated endpoint with filtering.

Good use of @Valid annotation for request validation and proper pagination support.

src/main/java/life/mosu/mosuserver/presentation/applicationschool/ApplicationSchoolController.java (1)

60-68: Correct usage of @userid annotation.

This method correctly uses both the userId from the security context and the applicationSchoolId from the path variable.

src/main/java/life/mosu/mosuserver/domain/application/ApplicationJpaEntity.java (3)

3-8: LGTM: Explicit imports improve code clarity.

The change from wildcard imports to explicit Jakarta Persistence imports is a good practice that improves code readability and reduces potential naming conflicts.


29-30: LGTM: Proper JPA field mapping.

The new guardianPhoneNumber field is properly mapped with the correct column name and appropriate data type. The field placement follows logical ordering within the entity.


44-48: LGTM: Constructor properly updated.

The constructor has been correctly updated to include the new guardianPhoneNumber parameter and field initialization. Parameter ordering is logical and consistent with the field declarations.

Also applies to: 52-52

host: ${REDIS_HOST}
port: ${REDIS_EXPORTER}
password: ${REDIS_PASSWORD}
port: ${VELKEY_PORT}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify environment variable consistency across deployment configurations.

The Redis port configuration was changed from ${REDIS_EXPORTER} to ${VELKEY_PORT}. Ensure that all deployment environments (development, staging, production) have the corresponding environment variable updated to prevent runtime configuration errors.


🏁 Script executed:

#!/bin/bash
# Description: Check if VELKEY_PORT environment variable is referenced elsewhere in the codebase
# Expected: Find other references to ensure consistency

echo "Searching for VELKEY_PORT references:"
rg "VELKEY_PORT" --type yaml --type properties --type sh --type dockerfile

echo -e "\nSearching for old REDIS_EXPORTER references that might need updating:"
rg "REDIS_EXPORTER" --type yaml --type properties --type sh --type dockerfile

Length of output: 462


🏁 Script executed:

#!/bin/bash
# Re-check references for VELKEY_PORT and REDIS_EXPORTER without invalid --type filters

echo "Searching for VELKEY_PORT references across the repository:"
rg "VELKEY_PORT"

echo -e "\nSearching for REDIS_EXPORTER references across the repository:"
rg "REDIS_EXPORTER"

Length of output: 1102


Update Docker-Compose configurations to use VELKEY_PORT

The change from ${REDIS_EXPORTER} to ${VELKEY_PORT} in application.yml needs to be mirrored in your Docker Compose files. Our search shows remaining ${REDIS_EXPORTER} mappings in both prod and local stacks:

  • docker-compose/docker-compose.prod.yml
    – Replace
    - ${REDIS_EXPORTER}:${REDIS_EXPORTER}
    with
    - ${VELKEY_PORT}:${VELKEY_PORT}

  • docker-compose/docker-compose.local.yml
    – Replace
    - ${REDIS_EXPORTER}:${REDIS_EXPORTER}
    with
    - ${VELKEY_PORT}:${VELKEY_PORT}

Ensuring all references use VELKEY_PORT will keep your service port mappings and REDIS_ADDR settings consistent across environments.

🤖 Prompt for AI Agents
In docker-compose/docker-compose.prod.yml and
docker-compose/docker-compose.local.yml, replace all occurrences of the
environment variable mapping from ${REDIS_EXPORTER}:${REDIS_EXPORTER} to
${VELKEY_PORT}:${VELKEY_PORT} to align with the updated port configuration in
src/main/resources/application.yml line 44. This ensures consistent port
mappings and environment variable usage across your Docker Compose
configurations.

Comment on lines +4 to +7
import life.mosu.mosuserver.presentation.admin.dto.ApplicationExcelDto;
import life.mosu.mosuserver.presentation.admin.dto.ApplicationFilter;
import life.mosu.mosuserver.presentation.admin.dto.ApplicationListResponse;
import life.mosu.mosuserver.presentation.admin.dto.SchoolLunchResponse;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Architecture concern: Domain layer importing presentation DTOs.

The domain layer is importing DTOs from the presentation layer (lines 4-7), which violates the typical layered architecture where domain should not depend on presentation. This creates a reverse dependency that makes the domain layer less reusable and harder to test.

Consider moving these DTOs to a shared package or creating separate domain-specific DTOs:

- import life.mosu.mosuserver.presentation.admin.dto.ApplicationExcelDto;
- import life.mosu.mosuserver.presentation.admin.dto.ApplicationFilter;
- import life.mosu.mosuserver.presentation.admin.dto.ApplicationListResponse;
- import life.mosu.mosuserver.presentation.admin.dto.SchoolLunchResponse;
+ import life.mosu.mosuserver.domain.admin.dto.ApplicationExcelDto;
+ import life.mosu.mosuserver.domain.admin.dto.ApplicationFilter;
+ import life.mosu.mosuserver.domain.admin.dto.ApplicationListResponse;
+ import life.mosu.mosuserver.domain.admin.dto.SchoolLunchResponse;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import life.mosu.mosuserver.presentation.admin.dto.ApplicationExcelDto;
import life.mosu.mosuserver.presentation.admin.dto.ApplicationFilter;
import life.mosu.mosuserver.presentation.admin.dto.ApplicationListResponse;
import life.mosu.mosuserver.presentation.admin.dto.SchoolLunchResponse;
import life.mosu.mosuserver.domain.admin.dto.ApplicationExcelDto;
import life.mosu.mosuserver.domain.admin.dto.ApplicationFilter;
import life.mosu.mosuserver.domain.admin.dto.ApplicationListResponse;
import life.mosu.mosuserver.domain.admin.dto.SchoolLunchResponse;
🤖 Prompt for AI Agents
In
src/main/java/life/mosu/mosuserver/domain/admin/ApplicationQueryRepository.java
around lines 4 to 7, the domain layer is importing DTO classes from the
presentation layer, which breaks the layered architecture by creating a reverse
dependency. To fix this, refactor by moving these DTO classes to a shared or
common package accessible by both domain and presentation layers, or create new
domain-specific DTOs within the domain layer itself, then update the imports
accordingly to remove any direct dependency on the presentation layer.

Comment on lines +70 to +78
List<ApplicationListResponse> content = query.fetch().stream()
.map(tuple -> {
Long appSchoolId = tuple.get(applicationSchool.id);
Set<Subject> subjects = appSchoolId != null
? findSubjectsByApplicationSchoolId(appSchoolId)
: new HashSet<>();
return mapToResponse(tuple, subjects);
})
.toList();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Performance: N+1 query problem when fetching subjects.

The current implementation executes a separate query for each application to fetch subjects, creating an N+1 query problem. This will significantly impact performance with large datasets.

Consider fetching all subjects in a single query and then mapping them:

// Fetch all application school IDs first
List<Long> appSchoolIds = query.fetch().stream()
    .map(t -> t.get(applicationSchool.id))
    .filter(Objects::nonNull)
    .toList();

// Fetch all subjects for all application schools in one query
Map<Long, Set<Subject>> subjectsMap = fetchSubjectsForApplicationSchools(appSchoolIds);

// Then map the results
List<ApplicationListResponse> content = query.fetch().stream()
    .map(tuple -> {
        Long appSchoolId = tuple.get(applicationSchool.id);
        Set<Subject> subjects = subjectsMap.getOrDefault(appSchoolId, new HashSet<>());
        return mapToResponse(tuple, subjects);
    })
    .toList();
🤖 Prompt for AI Agents
In
src/main/java/life/mosu/mosuserver/domain/admin/ApplicationQueryRepositoryImpl.java
around lines 70 to 78, the current code fetches subjects individually for each
application, causing an N+1 query problem. To fix this, first collect all
application school IDs from the query results, then fetch all subjects for these
IDs in a single query and store them in a map. Finally, update the mapping logic
to retrieve subjects from this map instead of querying per application,
eliminating the N+1 queries and improving performance.

Comment on lines +98 to +115
public List<SchoolLunchResponse> searchAllSchoolLunches() {
return queryFactory
.select(
school.schoolName,
applicationSchool.lunch.count()
)
.from(applicationSchool)
.rightJoin(school).on(applicationSchool.schoolId.eq(school.id))
.where(applicationSchool.lunch.ne(Lunch.NONE))
.groupBy(school.id, school.schoolName)
.fetch()
.stream()
.map(t -> new SchoolLunchResponse(
t.get(school.schoolName),
t.get(applicationSchool.lunch.count())
))
.toList();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Potential logic issue: RIGHT JOIN with WHERE clause may exclude schools.

The combination of rightJoin with where(applicationSchool.lunch.ne(Lunch.NONE)) might not work as intended. The WHERE clause will filter out schools that have no applications, defeating the purpose of the RIGHT JOIN.

If you want to include all schools (even those with 0 lunch applications), move the condition to the ON clause:

 return queryFactory
         .select(
                 school.schoolName,
                 applicationSchool.lunch.count()
         )
         .from(applicationSchool)
-        .rightJoin(school).on(applicationSchool.schoolId.eq(school.id))
-        .where(applicationSchool.lunch.ne(Lunch.NONE))
+        .rightJoin(school).on(
+            applicationSchool.schoolId.eq(school.id)
+            .and(applicationSchool.lunch.ne(Lunch.NONE))
+        )
         .groupBy(school.id, school.schoolName)
         .fetch()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public List<SchoolLunchResponse> searchAllSchoolLunches() {
return queryFactory
.select(
school.schoolName,
applicationSchool.lunch.count()
)
.from(applicationSchool)
.rightJoin(school).on(applicationSchool.schoolId.eq(school.id))
.where(applicationSchool.lunch.ne(Lunch.NONE))
.groupBy(school.id, school.schoolName)
.fetch()
.stream()
.map(t -> new SchoolLunchResponse(
t.get(school.schoolName),
t.get(applicationSchool.lunch.count())
))
.toList();
}
public List<SchoolLunchResponse> searchAllSchoolLunches() {
return queryFactory
.select(
school.schoolName,
applicationSchool.lunch.count()
)
.from(applicationSchool)
.rightJoin(school).on(
applicationSchool.schoolId.eq(school.id)
.and(applicationSchool.lunch.ne(Lunch.NONE))
)
.groupBy(school.id, school.schoolName)
.fetch()
.stream()
.map(t -> new SchoolLunchResponse(
t.get(school.schoolName),
t.get(applicationSchool.lunch.count())
))
.toList();
}
🤖 Prompt for AI Agents
In
src/main/java/life/mosu/mosuserver/domain/admin/ApplicationQueryRepositoryImpl.java
around lines 98 to 115, the use of rightJoin combined with a where clause
filtering on applicationSchool.lunch excludes schools without lunch
applications, negating the right join effect. To fix this, move the lunch
condition from the where clause into the on clause of the join so that all
schools are included and the lunch filter applies only to joined records.

Comment on lines +59 to +81
public Page<ApplicationListResponse> searchAllApplications(ApplicationFilter filter,
Pageable pageable) {

JPAQuery<Tuple> query = baseQuery()
.where(
buildNameCondition(filter.name()),
buildPhoneCondition(filter.phone())
)
.offset(pageable.getOffset())
.limit(pageable.getPageSize());

List<ApplicationListResponse> content = query.fetch().stream()
.map(tuple -> {
Long appSchoolId = tuple.get(applicationSchool.id);
Set<Subject> subjects = appSchoolId != null
? findSubjectsByApplicationSchoolId(appSchoolId)
: new HashSet<>();
return mapToResponse(tuple, subjects);
})
.toList();

return new PageImpl<>(content, pageable, content.size());
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical: Incorrect pagination implementation will cause data inconsistency.

The pagination implementation has a critical issue. You're using content.size() as the total count, which will always return the page size or less, not the actual total count of all records. This will break pagination in the UI.

 @Override
 public Page<ApplicationListResponse> searchAllApplications(ApplicationFilter filter,
         Pageable pageable) {
 
     JPAQuery<Tuple> query = baseQuery()
             .where(
                     buildNameCondition(filter.name()),
                     buildPhoneCondition(filter.phone())
             )
+            ;
+    
+    long total = query.fetchCount();
+    
+    query = query
             .offset(pageable.getOffset())
             .limit(pageable.getPageSize());
 
     List<ApplicationListResponse> content = query.fetch().stream()
             .map(tuple -> {
                 Long appSchoolId = tuple.get(applicationSchool.id);
                 Set<Subject> subjects = appSchoolId != null
                         ? findSubjectsByApplicationSchoolId(appSchoolId)
                         : new HashSet<>();
                 return mapToResponse(tuple, subjects);
             })
             .toList();
 
-    return new PageImpl<>(content, pageable, content.size());
+    return new PageImpl<>(content, pageable, total);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public Page<ApplicationListResponse> searchAllApplications(ApplicationFilter filter,
Pageable pageable) {
JPAQuery<Tuple> query = baseQuery()
.where(
buildNameCondition(filter.name()),
buildPhoneCondition(filter.phone())
)
.offset(pageable.getOffset())
.limit(pageable.getPageSize());
List<ApplicationListResponse> content = query.fetch().stream()
.map(tuple -> {
Long appSchoolId = tuple.get(applicationSchool.id);
Set<Subject> subjects = appSchoolId != null
? findSubjectsByApplicationSchoolId(appSchoolId)
: new HashSet<>();
return mapToResponse(tuple, subjects);
})
.toList();
return new PageImpl<>(content, pageable, content.size());
}
@Override
public Page<ApplicationListResponse> searchAllApplications(ApplicationFilter filter,
Pageable pageable) {
// build base query and count total before pagination
JPAQuery<Tuple> query = baseQuery()
.where(
buildNameCondition(filter.name()),
buildPhoneCondition(filter.phone())
);
long total = query.fetchCount();
// apply pagination
query = query
.offset(pageable.getOffset())
.limit(pageable.getPageSize());
List<ApplicationListResponse> content = query.fetch().stream()
.map(tuple -> {
Long appSchoolId = tuple.get(applicationSchool.id);
Set<Subject> subjects = appSchoolId != null
? findSubjectsByApplicationSchoolId(appSchoolId)
: new HashSet<>();
return mapToResponse(tuple, subjects);
})
.toList();
return new PageImpl<>(content, pageable, total);
}
🤖 Prompt for AI Agents
In
src/main/java/life/mosu/mosuserver/domain/admin/ApplicationQueryRepositoryImpl.java
around lines 59 to 81, the pagination implementation incorrectly uses
content.size() as the total count, which only reflects the current page size and
not the total number of records. To fix this, add a separate count query that
calculates the total number of matching records without pagination limits, then
use this count value as the third argument in the PageImpl constructor instead
of content.size().

@chominju02 chominju02 merged commit 60a5ef6 into develop Jul 9, 2025
2 checks passed
@chominju02 chominju02 deleted the feature/mosu-39 branch July 9, 2025 07:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant