Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/sheets #66

Merged
merged 14 commits into from
Feb 24, 2025
Merged

Feat/sheets #66

merged 14 commits into from
Feb 24, 2025

Conversation

ywj9811
Copy link
Contributor

@ywj9811 ywj9811 commented Feb 24, 2025

๐Ÿˆ PR ์š”์•ฝ

PR ๋‚ด์šฉ ํ•œ ์ค„๋กœ ์š”์•ฝ

โœจ PR ์ƒ์„ธ

๊ธฐ์กด ๋ฐ์ดํ„ฐ ์ด์ „ ์ค€๋น„

๐Ÿšจ ์ฐธ๊ณ ์‚ฌํ•ญ

๋ฆฌ๋ทฐ์–ด๋“ค์ด ์•Œ์•„์•ผ ํ•˜๊ฑฐ๋‚˜ ์•Œ๋ฉด ์ข‹์€ ์ฐธ๊ณ ์‚ฌํ•ญ ์ž‘์„ฑ

โœ… ์ฒดํฌ๋ฆฌ์ŠคํŠธ

  • Label ์ง€์ •ํ–ˆ๋‚˜์š”?
  • ๊ด€๋ จ ํ…Œํฌ์ŠคํŽ™ ๋งํฌ ์—ฐ๊ฒฐํ–ˆ๋‚˜์š”?

Summary by CodeRabbit

  • New Features

    • Introduced endpoints for Excel file uploads to support bulk teacher data imports and profile image updates.
    • Enabled enhanced teacher search by name and nickname.
    • Improved teacher profile processing by allowing optional values for specific personal details.
  • Chores

    • Upgraded document processing dependencies to enhance Excel file handling.

@ywj9811 ywj9811 added the โœจ Feature New feature or request label Feb 24, 2025
@ywj9811 ywj9811 self-assigned this Feb 24, 2025
Copy link

coderabbitai bot commented Feb 24, 2025

Warning

Rate limit exceeded

@ywj9811 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 11 minutes and 48 seconds before requesting another review.

โŒ› How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

๐Ÿšฆ How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between cd7c10e and ef9e6d8.

๐Ÿ“’ Files selected for processing (3)
  • src/main/java/com/yedu/backend/domain/teacher/application/mapper/TeacherMapper.java (6 hunks)
  • src/main/java/com/yedu/backend/global/excel/application/mapper/ExcelMapper.java (1 hunks)
  • src/main/java/com/yedu/backend/global/excel/constant/TeacherExcelColumn.java (1 hunks)

Walkthrough

This pull request introduces several modifications across the project. A new Apache POI dependency is added for Excel file processing. The removal of redundant calls in methods refines the teacher messaging flow. Mapping logic within the teacher domain is updated with changes in method visibility and new mapping functions for different request types. Database schema modifications now allow nullable fields for certain teacher attributes. Additionally, a new Excel processing feature is implemented with classes for parsing, mapping, and handling Excel and profile uploads, alongside a SQL migration to adjust the teacher profile column.

Changes

Files Change Summary
build.gradle Added dependency org.apache.poi:poi-ooxml:5.4.0 for Excel file reading.
.../AdminTestController.java, .../TeacherManageUseCase.java Removed calls to applyChannel, adjusting the teacher messaging flow.
.../TeacherMapper.java Changed visibility of mapping methods; added new mapping methods for TeacherInfoRequest and a null-check update.
.../TeacherEnglish.java, .../TeacherMath.java, .../TeacherInfo.java Modified annotations to allow nullable fields (e.g., teachingExperience, birth).
.../TeacherRepository.java, .../TeacherGetService.java Updated parameter naming and added new methods for retrieving a Teacher by name and nickname.
.../TeacherInfoRequest.java, .../ExcelMapper.java, .../ExcelManageUseCase.java, .../ExcelParserUseCase.java, .../ExcelController.java Introduced new Excel features including DTO for teacher info, Excel parsing/mapping classes, use cases for Excel file and profile processing, and corresponding controller endpoints.
.../V1_202502241825__alter_text.sql SQL migration altering the teacher.profile column to TEXT.

Sequence Diagram(s)

sequenceDiagram
    participant EC as ExcelController
    participant EMU as ExcelManageUseCase
    participant EPU as ExcelParserUseCase
    participant TM as TeacherMapper
    participant TS as TeacherSaveService

    EC->>EMU: uploadExcelFile(file)
    EMU->>EPU: parseExcel(file)
    EPU->>EPU: Iterate and extract rows
    EPU-->>EMU: List<TeacherInfoRequest>
    EMU->>TM: Map TeacherInfoRequest -> Teacher
    EMU->>TS: Save Teacher with districts and subjects
Loading
sequenceDiagram
    participant EC as ExcelController
    participant EMU as ExcelManageUseCase
    participant TGS as TeacherGetService
    participant TR as TeacherRepository
    participant S3 as S3Service
    participant TUS as TeacherUpdateService

    EC->>EMU: uploadProfile(profiles)
    EMU->>TGS: Retrieve Teacher by name and nickname
    TGS->>TR: findByTeacherInfo_NameAndTeacherInfo_NickName(...)
    TR-->>TGS: Teacher entity (or error)
    EMU->>S3: Upload profile image
    S3-->>EMU: Return profile URL
    EMU->>TUS: Update teacher profile with URL
Loading

Poem

Oh, I hop in lines of code so light,
Mapping changes make my day bright.
Excel files now twirl and dance,
Dependencies give our project a chance.
With each tweak, my heart leaps in delight! ๐Ÿ‡๐Ÿ’ป


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

โค๏ธ Share
๐Ÿชง 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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. (Beta)
  • @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

@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: 8

๐Ÿงน Nitpick comments (9)
src/main/java/com/yedu/backend/domain/teacher/domain/service/TeacherGetService.java (1)

20-23: Add custom exception handling for clarity.

The orElseThrow() without a custom exception could lead to unclear error messages. Consider using a custom exception similar to other methods in the codebase.

-        return teacherRepository.findByTeacherInfo_NameAndTeacherInfo_NickName(name, nickName)
-                .orElseThrow();
+        return teacherRepository.findByTeacherInfo_NameAndTeacherInfo_NickName(name, nickName)
+                .orElseThrow(() -> new TeacherNotFoundException("Teacher not found with name: " + name + " and nickname: " + nickName));
src/main/java/com/yedu/backend/domain/teacher/domain/entity/TeacherEnglish.java (1)

22-23:

โ“ Verification inconclusive

Consider adding validation for teachingExperience.

While making the field nullable supports data migration, consider adding validation logic in the service layer to ensure data quality for new entries.


๐Ÿ Script executed:

#!/bin/bash
# Check if validation exists in service layer
rg -l "teachingExperience.*validate" --type java

Length of output: 50


Add service-layer validation for teachingExperience

While making the field nullable aids the migration, the search did not reveal any existing validation logic for teachingExperience in the service layer. This may risk data integrity for new entries. Please review and, if appropriate, implement robust validation (or confirm that a generic validator elsewhere covers this case).

  • Verify if any alternate validation mechanism exists for teachingExperience.
  • If not, add explicit validation in the service logic to enforce data quality on new records.
src/main/java/com/yedu/backend/domain/teacher/domain/entity/TeacherInfo.java (2)

39-40: Address TODO comment about video feature.

The TODO comment indicates pending work on the video feature. Would you like me to help create an issue to track this enhancement?


28-29:

โ“ Verification inconclusive

Consider data validation strategy for birth field.

While making the field nullable supports data migration, consider:

  1. Adding validation for the birth date format in the service layer
  2. Implementing a migration strategy to populate this field with valid data

๐Ÿ Script executed:

#!/bin/bash
# Check for existing birth date validation
rg -l "birth.*validate" --type java

Length of output: 37


Please verify the validation strategy for the birth field in TeacherInfo.java

It appears that there isnโ€™t any existing validation logic for the birth field. While making it nullable supports data migration, this demographic field is important and should be validated. I recommend:

  • Implementing service-layer validation to enforce the correct birth date format (e.g., โ€œYYYY-MM-DDโ€ or your chosen format).
  • Designing a migration strategy to update existing records with valid birth data.
  • Reviewing the TODO on the video feature to ensure it does not impact or conflict with the birth fieldโ€™s purpose.

Please manually verify whether additional validations are needed elsewhere in the code.

src/main/java/com/yedu/backend/global/excel/application/dto/TeacherInfoRequest.java (2)

25-25: Fix typo in field name.

The field recommenedStudent is misspelled. It should be recommendedStudent.


34-49: Consider extracting common fields to an interface.

The English and Math records share similar fields. Consider extracting these to a common interface or base record to improve maintainability.

+public interface TeachingSpecialization {
+    String appealPoint();
+    String teachingExperience();
+    int teachingHistory();
+    String teachingStyle();
+    String managementStyle();
+}

-public record English(...)
+public record English(
     String appealPoint,
     String teachingExperience,
     String foreignExperience,
     int teachingHistory,
     String teachingStyle,
     String managementStyle
-) {}
+) implements TeachingSpecialization {}

-public record Math(...)
+public record Math(
     String appealPoint,
     String teachingExperience,
     int teachingHistory,
     String teachingStyle,
     String managementStyle
-) {}
+) implements TeachingSpecialization {}
src/main/java/com/yedu/backend/global/excel/application/usecase/ExcelParserUseCase.java (1)

44-53: Optimize row emptiness check.

The current implementation iterates through all cells. Consider using getLastCellNum() for a more efficient check.

 private boolean isRowEmpty(Row row) {
     if (row == null)
         return true;
+    int lastCell = row.getLastCellNum();
+    if (lastCell <= 0) return true;
     for (Cell cell : row) {
         if (cell != null && cell.getCellType() != CellType.BLANK && cell.getCellType() != CellType._NONE) {
             return false;
         }
     }
     return true;
 }
src/main/java/com/yedu/backend/global/excel/application/mapper/ExcelMapper.java (1)

82-96: Enhance cell value extraction robustness.

The cell value extraction methods need additional validation and error handling.

 private static String getStringValue(Cell cell) {
-    return (cell != null && cell.getCellType() != CellType.BLANK && cell.getCellType() != CellType._NONE) ? cell.getStringCellValue() : null;
+    if (cell == null || cell.getCellType() == CellType.BLANK || cell.getCellType() == CellType._NONE) {
+        return null;
+    }
+    try {
+        switch (cell.getCellType()) {
+            case STRING:
+                return cell.getStringCellValue();
+            case NUMERIC:
+                return String.valueOf(cell.getNumericCellValue());
+            default:
+                return null;
+        }
+    } catch (Exception e) {
+        log.warn("Failed to get string value from cell: {}", e.getMessage());
+        return null;
+    }
 }

 private static double getNumericValue(Cell cell) {
-    return (cell != null && cell.getCellType() != CellType.BLANK && cell.getCellType() != CellType._NONE) ? cell.getNumericCellValue() : 0;
+    if (cell == null || cell.getCellType() == CellType.BLANK || cell.getCellType() == CellType._NONE) {
+        return 0;
+    }
+    try {
+        switch (cell.getCellType()) {
+            case NUMERIC:
+                return cell.getNumericCellValue();
+            case STRING:
+                return Double.parseDouble(cell.getStringCellValue());
+            default:
+                return 0;
+        }
+    } catch (Exception e) {
+        log.warn("Failed to get numeric value from cell: {}", e.getMessage());
+        return 0;
+    }
 }
src/main/java/com/yedu/backend/domain/teacher/application/mapper/TeacherMapper.java (1)

148-156: Improve null handling in getOrganizeContexts.

The method could be more concise using Optional.

 private static List<String> getOrganizeContexts(String context) {
-    if (context == null)
-        return new ArrayList<>();
-    String organizeContext = context.replace("#", "");
-    return Arrays.stream(organizeContext.split("\n")).toList()
-            .stream()
-            .map(String::trim)
-            .toList();
+    return Optional.ofNullable(context)
+            .map(ctx -> ctx.replace("#", ""))
+            .map(ctx -> Arrays.stream(ctx.split("\n"))
+                    .map(String::trim)
+                    .toList())
+            .orElse(new ArrayList<>());
 }
๐Ÿ“œ Review details

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

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between 2c1063e and cd7c10e.

๐Ÿ“’ Files selected for processing (15)
  • build.gradle (1 hunks)
  • src/main/java/com/yedu/backend/admin/presentation/AdminTestController.java (0 hunks)
  • src/main/java/com/yedu/backend/domain/teacher/application/mapper/TeacherMapper.java (6 hunks)
  • src/main/java/com/yedu/backend/domain/teacher/application/usecase/TeacherManageUseCase.java (0 hunks)
  • src/main/java/com/yedu/backend/domain/teacher/domain/entity/TeacherEnglish.java (1 hunks)
  • src/main/java/com/yedu/backend/domain/teacher/domain/entity/TeacherInfo.java (1 hunks)
  • src/main/java/com/yedu/backend/domain/teacher/domain/entity/TeacherMath.java (1 hunks)
  • src/main/java/com/yedu/backend/domain/teacher/domain/repository/TeacherRepository.java (1 hunks)
  • src/main/java/com/yedu/backend/domain/teacher/domain/service/TeacherGetService.java (1 hunks)
  • src/main/java/com/yedu/backend/global/excel/application/dto/TeacherInfoRequest.java (1 hunks)
  • src/main/java/com/yedu/backend/global/excel/application/mapper/ExcelMapper.java (1 hunks)
  • src/main/java/com/yedu/backend/global/excel/application/usecase/ExcelManageUseCase.java (1 hunks)
  • src/main/java/com/yedu/backend/global/excel/application/usecase/ExcelParserUseCase.java (1 hunks)
  • src/main/java/com/yedu/backend/global/excel/presentation/ExcelController.java (1 hunks)
  • src/main/resources/db/migration/V1_202502241825__alter_text.sql (1 hunks)
๐Ÿ’ค Files with no reviewable changes (2)
  • src/main/java/com/yedu/backend/domain/teacher/application/usecase/TeacherManageUseCase.java
  • src/main/java/com/yedu/backend/admin/presentation/AdminTestController.java
โœ… Files skipped from review due to trivial changes (1)
  • src/main/resources/db/migration/V1_202502241825__alter_text.sql
๐Ÿ”‡ Additional comments (4)
src/main/java/com/yedu/backend/domain/teacher/domain/entity/TeacherMath.java (1)

22-23: LGTM! Consistent with TeacherEnglish changes.

The change maintains consistency with similar modifications in TeacherEnglish, supporting the data migration requirements.

build.gradle (1)

68-69: LGTM! Apache POI dependency added for Excel processing.

The dependency is correctly added with a stable version and clear purpose.

src/main/java/com/yedu/backend/domain/teacher/domain/repository/TeacherRepository.java (2)

9-9: LGTM! Parameter name follows Java naming conventions.

The parameter name change from PhoneNumber to phoneNumber aligns with Java's camelCase convention.


13-13: LGTM! New finder method follows consistent naming pattern.

The method correctly returns Optional<Teacher> for null safety and follows the same naming pattern as other finder methods.

Comment on lines +23 to +27
@PostMapping("/profile")
public ResponseEntity<Void> uploadProfile(@RequestPart List<MultipartFile> profiles) {
manageUseCase.saveTeacherProfile(profiles);
return ResponseEntity.ok().build();
}
Copy link

Choose a reason for hiding this comment

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

๐Ÿ› ๏ธ Refactor suggestion

Add validation and error handling for profile uploads.

Similar to the Excel upload endpoint, add:

  1. File size limits
  2. File type validation (only images)
  3. Error handling
  4. Response body
 @PostMapping("/profile")
-public ResponseEntity<Void> uploadProfile(@RequestPart List<MultipartFile> profiles) {
+public ResponseEntity<String> uploadProfile(
+    @RequestPart(required = true) @Valid @Size(max = 10)
+    List<@Size(max = 5242880) @Pattern(regexp = "^.*\\.(jpg|jpeg|png)$") MultipartFile> profiles
+) {
+    try {
         manageUseCase.saveTeacherProfile(profiles);
-        return ResponseEntity.ok().build();
+        return ResponseEntity.ok("Profiles processed successfully");
+    } catch (Exception e) {
+        return ResponseEntity.badRequest().body("Error processing profiles: " + e.getMessage());
+    }
 }
๐Ÿ“ 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
@PostMapping("/profile")
public ResponseEntity<Void> uploadProfile(@RequestPart List<MultipartFile> profiles) {
manageUseCase.saveTeacherProfile(profiles);
return ResponseEntity.ok().build();
}
@PostMapping("/profile")
public ResponseEntity<String> uploadProfile(
@RequestPart(required = true) @Valid @Size(max = 10)
List<@Size(max = 5242880) @Pattern(regexp = "^.*\\.(jpg|jpeg|png)$") MultipartFile> profiles
) {
try {
manageUseCase.saveTeacherProfile(profiles);
return ResponseEntity.ok("Profiles processed successfully");
} catch (Exception e) {
return ResponseEntity.badRequest().body("Error processing profiles: " + e.getMessage());
}
}

Comment on lines +17 to +21
@PostMapping("/upload")
public ResponseEntity<Void> uploadExcelFile(@RequestPart MultipartFile file) {
manageUseCase.saveTeacherByExcelFile(file);
return ResponseEntity.ok().build();
}
Copy link

Choose a reason for hiding this comment

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

๐Ÿ› ๏ธ Refactor suggestion

Add file validation and error handling.

Consider adding:

  1. File size limits
  2. File type validation (only Excel files)
  3. Error handling for file processing failures
  4. Response body indicating success/failure status
 @PostMapping("/upload")
-public ResponseEntity<Void> uploadExcelFile(@RequestPart MultipartFile file) {
+public ResponseEntity<String> uploadExcelFile(
+    @RequestPart(required = true) @Valid @Size(max = 10485760) // 10MB limit
+    @Pattern(regexp = "^.*\\.(xlsx|xls)$", message = "Only Excel files allowed")
+    MultipartFile file
+) {
+    try {
         manageUseCase.saveTeacherByExcelFile(file);
-        return ResponseEntity.ok().build();
+        return ResponseEntity.ok("File processed successfully");
+    } catch (Exception e) {
+        return ResponseEntity.badRequest().body("Error processing file: " + e.getMessage());
+    }
 }
๐Ÿ“ 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
@PostMapping("/upload")
public ResponseEntity<Void> uploadExcelFile(@RequestPart MultipartFile file) {
manageUseCase.saveTeacherByExcelFile(file);
return ResponseEntity.ok().build();
}
@PostMapping("/upload")
public ResponseEntity<String> uploadExcelFile(
@RequestPart(required = true) @Valid @Size(max = 10485760) // 10MB limit
@Pattern(regexp = "^.*\\.(xlsx|xls)$", message = "Only Excel files allowed")
MultipartFile file
) {
try {
manageUseCase.saveTeacherByExcelFile(file);
return ResponseEntity.ok("File processed successfully");
} catch (Exception e) {
return ResponseEntity.badRequest().body("Error processing file: " + e.getMessage());
}
}

Comment on lines +7 to +33
public record TeacherInfoRequest(
String name,
String nickName,
String email,
String phoneNumber,
String birth,
TeacherGender gender,
String university,
String major,
String highSchool,
String highSchoolType,
String introduce,
String teachingStyle1,
String teachingStyleInfo1,
String teachingStyle2,
String teachingStyleInfo2,
boolean englishPossible,
boolean mathPossible,
String recommenedStudent,
String comment,
List<List<String>> available,
List<String> region,
String source,
boolean marketingAgree,
English english,
Math math
) {
Copy link

Choose a reason for hiding this comment

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

๐Ÿ› ๏ธ Refactor suggestion

Add validation annotations for fields.

Consider adding validation annotations to ensure data integrity:

  1. Email validation for email
  2. Phone number format validation for phoneNumber
  3. Date format validation for birth
  4. Non-null validation for required fields
  5. Size constraints for lists
+import javax.validation.constraints.*;
+import java.util.List;

 public record TeacherInfoRequest(
+        @NotBlank(message = "Name is required")
         String name,
+        @NotBlank(message = "Nickname is required")
         String nickName,
+        @Email(message = "Invalid email format")
         String email,
+        @Pattern(regexp = "^\\d{3}-\\d{3,4}-\\d{4}$", message = "Invalid phone number format")
         String phoneNumber,
+        @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}$", message = "Invalid date format (YYYY-MM-DD)")
         String birth,
+        @NotNull(message = "Gender is required")
         TeacherGender gender,
         // ... other fields ...
+        @Size(max = 7, message = "Maximum 7 days allowed")
         List<List<String>> available,
+        @Size(max = 5, message = "Maximum 5 regions allowed")
         List<String> region,
         // ... remaining fields ...
 )
๐Ÿ“ 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 record TeacherInfoRequest(
String name,
String nickName,
String email,
String phoneNumber,
String birth,
TeacherGender gender,
String university,
String major,
String highSchool,
String highSchoolType,
String introduce,
String teachingStyle1,
String teachingStyleInfo1,
String teachingStyle2,
String teachingStyleInfo2,
boolean englishPossible,
boolean mathPossible,
String recommenedStudent,
String comment,
List<List<String>> available,
List<String> region,
String source,
boolean marketingAgree,
English english,
Math math
) {
import javax.validation.constraints.*;
import java.util.List;
public record TeacherInfoRequest(
@NotBlank(message = "Name is required")
String name,
@NotBlank(message = "Nickname is required")
String nickName,
@Email(message = "Invalid email format")
String email,
@Pattern(regexp = "^\\d{3}-\\d{3,4}-\\d{4}$", message = "Invalid phone number format")
String phoneNumber,
@Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}$", message = "Invalid date format (YYYY-MM-DD)")
String birth,
@NotNull(message = "Gender is required")
TeacherGender gender,
String university,
String major,
String highSchool,
String highSchoolType,
String introduce,
String teachingStyle1,
String teachingStyleInfo1,
String teachingStyle2,
String teachingStyleInfo2,
boolean englishPossible,
boolean mathPossible,
String recommenedStudent,
String comment,
@Size(max = 7, message = "Maximum 7 days allowed")
List<List<String>> available,
@Size(max = 5, message = "Maximum 5 regions allowed")
List<String> region,
String source,
boolean marketingAgree,
English english,
Math math
) {
}

Comment on lines +17 to +42
public List<Row> parseExcel(MultipartFile file) {
List<Row> rows = new ArrayList<>();

try (InputStream inputStream = file.getInputStream();
Workbook workbook = new XSSFWorkbook(inputStream)) {

Sheet sheet = workbook.getSheetAt(0);
boolean isHeader = true;

for (Row row : sheet) {
if (isHeader) {
isHeader = false;
continue;
}
if (isRowEmpty(row)) {
break;
}
rows.add(row);
}

} catch (IOException e) {
log.error("Failed to parse Excel file", e);
throw new RuntimeException("Excel ํŒŒ์ผ์„ ์ฝ๋Š” ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ", e);
}
return rows;
}
Copy link

Choose a reason for hiding this comment

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

๐Ÿ› ๏ธ Refactor suggestion

Add input validation and specific error handling.

The Excel parsing logic needs additional validation and error handling:

  1. Validate file type/extension before processing
  2. Check file size limits
  3. Validate minimum required columns
  4. Use custom exceptions instead of generic RuntimeException

Consider this implementation:

 public List<Row> parseExcel(MultipartFile file) {
+    validateExcelFile(file);
     List<Row> rows = new ArrayList<>();
     try (InputStream inputStream = file.getInputStream();
          Workbook workbook = new XSSFWorkbook(inputStream)) {
         Sheet sheet = workbook.getSheetAt(0);
+        validateSheetStructure(sheet);
         boolean isHeader = true;
         for (Row row : sheet) {
             if (isHeader) {
                 isHeader = false;
                 continue;
             }
             if (isRowEmpty(row)) {
                 break;
             }
             rows.add(row);
         }
     } catch (IOException e) {
         log.error("Failed to parse Excel file", e);
-        throw new RuntimeException("Excel ํŒŒ์ผ์„ ์ฝ๋Š” ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ", e);
+        throw new ExcelParsingException("Excel ํŒŒ์ผ์„ ์ฝ๋Š” ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ", e);
     }
     return rows;
 }

+private void validateExcelFile(MultipartFile file) {
+    String filename = file.getOriginalFilename();
+    if (filename == null || !filename.endsWith(".xlsx")) {
+        throw new InvalidFileTypeException("Only .xlsx files are supported");
+    }
+    if (file.getSize() > MAX_FILE_SIZE) {
+        throw new FileSizeLimitExceededException("File size exceeds limit");
+    }
+}

+private void validateSheetStructure(Sheet sheet) {
+    Row headerRow = sheet.getRow(0);
+    if (headerRow == null || headerRow.getPhysicalNumberOfCells() < REQUIRED_COLUMNS) {
+        throw new InvalidSheetStructureException("Invalid sheet structure");
+    }
+}
๐Ÿ“ 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<Row> parseExcel(MultipartFile file) {
List<Row> rows = new ArrayList<>();
try (InputStream inputStream = file.getInputStream();
Workbook workbook = new XSSFWorkbook(inputStream)) {
Sheet sheet = workbook.getSheetAt(0);
boolean isHeader = true;
for (Row row : sheet) {
if (isHeader) {
isHeader = false;
continue;
}
if (isRowEmpty(row)) {
break;
}
rows.add(row);
}
} catch (IOException e) {
log.error("Failed to parse Excel file", e);
throw new RuntimeException("Excel ํŒŒ์ผ์„ ์ฝ๋Š” ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ", e);
}
return rows;
}
public List<Row> parseExcel(MultipartFile file) {
validateExcelFile(file);
List<Row> rows = new ArrayList<>();
try (InputStream inputStream = file.getInputStream();
Workbook workbook = new XSSFWorkbook(inputStream)) {
Sheet sheet = workbook.getSheetAt(0);
validateSheetStructure(sheet);
boolean isHeader = true;
for (Row row : sheet) {
if (isHeader) {
isHeader = false;
continue;
}
if (isRowEmpty(row)) {
break;
}
rows.add(row);
}
} catch (IOException e) {
log.error("Failed to parse Excel file", e);
throw new ExcelParsingException("Excel ํŒŒ์ผ์„ ์ฝ๋Š” ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ", e);
}
return rows;
}
private void validateExcelFile(MultipartFile file) {
String filename = file.getOriginalFilename();
if (filename == null || !filename.endsWith(".xlsx")) {
throw new InvalidFileTypeException("Only .xlsx files are supported");
}
if (file.getSize() > MAX_FILE_SIZE) {
throw new FileSizeLimitExceededException("File size exceeds limit");
}
}
private void validateSheetStructure(Sheet sheet) {
Row headerRow = sheet.getRow(0);
if (headerRow == null || headerRow.getPhysicalNumberOfCells() < REQUIRED_COLUMNS) {
throw new InvalidSheetStructureException("Invalid sheet structure");
}
}

Comment on lines +61 to +74
public void saveTeacherProfile(List<MultipartFile> profiles) {
profiles.stream()
.forEach(profile -> {
String fileName = profile.getOriginalFilename()
.replace(" ", "")
.split("-")[0];
String[] split = fileName.split("_");
String name = split[0];
String nickName = split[1];
Teacher teacher = teacherGetService.byNameAndNickName(name, nickName);
String profileUrl = s3UploadService.saveProfileFile(profile);
teacherUpdateService.updateProfile(teacher, profileUrl);
});
}
Copy link

Choose a reason for hiding this comment

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

โš ๏ธ Potential issue

Add file validation and error handling for profile uploads.

The profile upload process needs better validation and error handling:

  1. Missing file type validation
  2. Unsafe filename parsing
  3. No error handling for S3 operations
  4. No file size limits

Consider this implementation:

 public void saveTeacherProfile(List<MultipartFile> profiles) {
+    validateProfiles(profiles);
     profiles.stream()
             .forEach(profile -> {
+                try {
+                    validateProfileImage(profile);
                     String fileName = profile.getOriginalFilename()
                             .replace(" ", "")
                             .split("-")[0];
+                    if (!fileName.contains("_")) {
+                        throw new InvalidFileNameException("Invalid profile filename format");
+                    }
                     String[] split = fileName.split("_");
                     String name = split[0];
                     String nickName = split[1];
                     Teacher teacher = teacherGetService.byNameAndNickName(name, nickName);
                     String profileUrl = s3UploadService.saveProfileFile(profile);
                     teacherUpdateService.updateProfile(teacher, profileUrl);
+                } catch (Exception e) {
+                    log.error("Failed to process profile: {}", profile.getOriginalFilename(), e);
+                    throw new ProfileProcessingException("Failed to process profile", e);
+                }
             });
 }

+private void validateProfiles(List<MultipartFile> profiles) {
+    if (profiles == null || profiles.isEmpty()) {
+        throw new InvalidInputException("No profile images provided");
+    }
+}

+private void validateProfileImage(MultipartFile file) {
+    String contentType = file.getContentType();
+    if (contentType == null || !contentType.startsWith("image/")) {
+        throw new InvalidFileTypeException("Invalid file type. Only images are allowed");
+    }
+    if (file.getSize() > MAX_PROFILE_SIZE) {
+        throw new FileSizeLimitExceededException("Profile image size exceeds limit");
+    }
+}
๐Ÿ“ 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 void saveTeacherProfile(List<MultipartFile> profiles) {
profiles.stream()
.forEach(profile -> {
String fileName = profile.getOriginalFilename()
.replace(" ", "")
.split("-")[0];
String[] split = fileName.split("_");
String name = split[0];
String nickName = split[1];
Teacher teacher = teacherGetService.byNameAndNickName(name, nickName);
String profileUrl = s3UploadService.saveProfileFile(profile);
teacherUpdateService.updateProfile(teacher, profileUrl);
});
}
public void saveTeacherProfile(List<MultipartFile> profiles) {
validateProfiles(profiles);
profiles.stream()
.forEach(profile -> {
try {
validateProfileImage(profile);
String fileName = profile.getOriginalFilename()
.replace(" ", "")
.split("-")[0];
if (!fileName.contains("_")) {
throw new InvalidFileNameException("Invalid profile filename format");
}
String[] split = fileName.split("_");
String name = split[0];
String nickName = split[1];
Teacher teacher = teacherGetService.byNameAndNickName(name, nickName);
String profileUrl = s3UploadService.saveProfileFile(profile);
teacherUpdateService.updateProfile(teacher, profileUrl);
} catch (Exception e) {
log.error("Failed to process profile: {}", profile.getOriginalFilename(), e);
throw new ProfileProcessingException("Failed to process profile", e);
}
});
}
private void validateProfiles(List<MultipartFile> profiles) {
if (profiles == null || profiles.isEmpty()) {
throw new InvalidInputException("No profile images provided");
}
}
private void validateProfileImage(MultipartFile file) {
String contentType = file.getContentType();
if (contentType == null || !contentType.startsWith("image/")) {
throw new InvalidFileTypeException("Invalid file type. Only images are allowed");
}
if (file.getSize() > MAX_PROFILE_SIZE) {
throw new FileSizeLimitExceededException("Profile image size exceeds limit");
}
}

Comment on lines +36 to +59
public void saveTeacherByExcelFile(MultipartFile file) {
try {
List<TeacherInfoRequest> teacherInfoRequests = excelParserUseCase.parseExcel(file)
.stream()
.map(ExcelMapper::mapToInfoRequest)
.toList();
teacherInfoRequests.forEach(request -> {
log.info("request : {}", request);
Teacher teacher = TeacherMapper.mapToTeacher(request);
List<TeacherDistrict> districts = request.region()
.stream()
.map(region -> {
region = region.trim();
return mapToTeacherDistrict(teacher, region);
})
.toList();// ์„ ์ƒ๋‹˜ ๊ต์œก ๊ฐ€๋Šฅ ๊ตฌ์—ญ
TeacherEnglish english = request.englishPossible() ? TeacherMapper.mapToTeacherEnglish(teacher, request) : null;
TeacherMath math = request.mathPossible() ? TeacherMapper.mapToTeacherMath(teacher, request) : null;
teacherSaveService.saveTeacher(teacher, new ArrayList<>(), districts, english, math);
});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Copy link

Choose a reason for hiding this comment

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

โš ๏ธ Potential issue

Improve error handling and data validation.

Several issues need attention:

  1. Generic exception handling may mask specific errors
  2. Potential NPE in region trimming
  3. Empty districts list passed to saveTeacher
  4. No transaction rollback strategy for partial failures

Consider this implementation:

 public void saveTeacherByExcelFile(MultipartFile file) {
     try {
         List<TeacherInfoRequest> teacherInfoRequests = excelParserUseCase.parseExcel(file)
                 .stream()
                 .map(ExcelMapper::mapToInfoRequest)
                 .toList();
+        validateTeacherRequests(teacherInfoRequests);
         teacherInfoRequests.forEach(request -> {
             log.info("request : {}", request);
             Teacher teacher = TeacherMapper.mapToTeacher(request);
-            List<TeacherDistrict> districts = request.region()
+            List<TeacherDistrict> districts = request.region() != null ? request.region()
                     .stream()
-                    .map(region -> {
-                        region = region.trim();
+                    .map(region -> region != null ? region.trim() : null)
+                    .filter(region -> region != null && !region.isEmpty())
+                    .map(region -> {
                         return mapToTeacherDistrict(teacher, region);
                     })
-                    .toList();
+                    .toList() : new ArrayList<>();
+            if (districts.isEmpty()) {
+                throw new InvalidDataException("Teacher must have at least one district");
+            }
             TeacherEnglish english = request.englishPossible() ? TeacherMapper.mapToTeacherEnglish(teacher, request) : null;
             TeacherMath math = request.mathPossible() ? TeacherMapper.mapToTeacherMath(teacher, request) : null;
             teacherSaveService.saveTeacher(teacher, new ArrayList<>(), districts, english, math);
         });
     } catch (Exception e) {
-        throw new RuntimeException(e);
+        log.error("Failed to save teacher data", e);
+        throw new TeacherDataProcessingException("Failed to process teacher data", e);
     }
 }

+private void validateTeacherRequests(List<TeacherInfoRequest> requests) {
+    if (requests.isEmpty()) {
+        throw new InvalidDataException("No valid teacher data found in Excel file");
+    }
+}
๐Ÿ“ 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 void saveTeacherByExcelFile(MultipartFile file) {
try {
List<TeacherInfoRequest> teacherInfoRequests = excelParserUseCase.parseExcel(file)
.stream()
.map(ExcelMapper::mapToInfoRequest)
.toList();
teacherInfoRequests.forEach(request -> {
log.info("request : {}", request);
Teacher teacher = TeacherMapper.mapToTeacher(request);
List<TeacherDistrict> districts = request.region()
.stream()
.map(region -> {
region = region.trim();
return mapToTeacherDistrict(teacher, region);
})
.toList();// ์„ ์ƒ๋‹˜ ๊ต์œก ๊ฐ€๋Šฅ ๊ตฌ์—ญ
TeacherEnglish english = request.englishPossible() ? TeacherMapper.mapToTeacherEnglish(teacher, request) : null;
TeacherMath math = request.mathPossible() ? TeacherMapper.mapToTeacherMath(teacher, request) : null;
teacherSaveService.saveTeacher(teacher, new ArrayList<>(), districts, english, math);
});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void saveTeacherByExcelFile(MultipartFile file) {
try {
List<TeacherInfoRequest> teacherInfoRequests = excelParserUseCase.parseExcel(file)
.stream()
.map(ExcelMapper::mapToInfoRequest)
.toList();
validateTeacherRequests(teacherInfoRequests);
teacherInfoRequests.forEach(request -> {
log.info("request : {}", request);
Teacher teacher = TeacherMapper.mapToTeacher(request);
List<TeacherDistrict> districts = request.region() != null ? request.region()
.stream()
.map(region -> region != null ? region.trim() : null)
.filter(region -> region != null && !region.isEmpty())
.map(region -> {
return mapToTeacherDistrict(teacher, region);
})
.toList() : new ArrayList<>();
if (districts.isEmpty()) {
throw new InvalidDataException("Teacher must have at least one district");
}
TeacherEnglish english = request.englishPossible() ? TeacherMapper.mapToTeacherEnglish(teacher, request) : null;
TeacherMath math = request.mathPossible() ? TeacherMapper.mapToTeacherMath(teacher, request) : null;
teacherSaveService.saveTeacher(teacher, new ArrayList<>(), districts, english, math);
});
} catch (Exception e) {
log.error("Failed to save teacher data", e);
throw new TeacherDataProcessingException("Failed to process teacher data", e);
}
}
private void validateTeacherRequests(List<TeacherInfoRequest> requests) {
if (requests.isEmpty()) {
throw new InvalidDataException("No valid teacher data found in Excel file");
}
}

Comment on lines 15 to 47
public static TeacherInfoRequest mapToInfoRequest(Row row) {
String email = getStringValue(row.getCell(1));
String name = getStringValue(row.getCell(2));
String nickName = getStringValue(row.getCell(3));
String phoneNumber = getStringValue(row.getCell(4));
double birthNum = getNumericValue(row.getCell(5));
String birth = birthNum == 0 ? null : String.valueOf(birthNum);
String gender = getStringValue(row.getCell(6));
String university = getStringValue(row.getCell(7));
String major = getStringValue(row.getCell(8));
String highSchool = getStringValue(row.getCell(9));
String highSchoolType = getStringValue(row.getCell(10));
String introduce = getStringValue(row.getCell(11));
String teachingStyle1 = getStringValue(row.getCell(12));
String teachingStyleInfo1 = getStringValue(row.getCell(13));
String teachingStyle2 = getStringValue(row.getCell(14));
String teachingStyleInfo2 = getStringValue(row.getCell(15));
boolean englishPossible = getBooleanValueByString(row.getCell(16));
boolean mathPossible = getBooleanValueByString(row.getCell(23));
String recommendStudent = getStringValue(row.getCell(29));
String comment = getStringValue(row.getCell(30));
List<List<String>> available = new ArrayList<>();
List<String> region = Arrays.stream((getStringValue(row.getCell(38)))
.split(",")).toList();
String source = getStringValue(row.getCell(39));
boolean marketingAgree = true;

return new TeacherInfoRequest(
name, nickName, email, phoneNumber, birth, TeacherGender.valueOf(gender), university,
major, highSchool, highSchoolType, introduce, teachingStyle1, teachingStyleInfo1, teachingStyle2, teachingStyleInfo2,
englishPossible, mathPossible, recommendStudent, comment, available, region, source, marketingAgree, getEnglish(row), getMath(row)
);
}
Copy link

Choose a reason for hiding this comment

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

๐Ÿ› ๏ธ Refactor suggestion

Improve data mapping robustness and maintainability.

The current implementation has several issues:

  1. Magic numbers for cell indices
  2. Unsafe type conversions
  3. No validation of required fields
  4. Potential NPE in string operations

Consider this implementation:

+private static final class CellIndex {
+    static final int EMAIL = 1;
+    static final int NAME = 2;
+    // ... other indices
+}

 public static TeacherInfoRequest mapToInfoRequest(Row row) {
+    validateRow(row);
-    String email = getStringValue(row.getCell(1));
-    String name = getStringValue(row.getCell(2));
+    String email = getStringValue(row.getCell(CellIndex.EMAIL));
+    String name = getStringValue(row.getCell(CellIndex.NAME));
+    if (name == null || email == null) {
+        throw new RequiredFieldMissingException("Name and email are required");
+    }
     // ... other fields
-    List<String> region = Arrays.stream((getStringValue(row.getCell(38)))
+    String regionStr = getStringValue(row.getCell(CellIndex.REGION));
+    List<String> region = regionStr == null ? new ArrayList<>() : Arrays.stream(regionStr)
             .split(",")).toList();

     return new TeacherInfoRequest(
             name, nickName, email, phoneNumber, birth, TeacherGender.valueOf(gender),
             // ... other fields
     );
 }

+private static void validateRow(Row row) {
+    if (row == null) {
+        throw new InvalidRowException("Row cannot be null");
+    }
+    if (row.getLastCellNum() < REQUIRED_CELLS) {
+        throw new InvalidRowException("Row does not contain all required fields");
+    }
+}

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 203 to 215
private static TeacherClassInfo mapToTeacherClassInfo(TeacherInfoRequest request) {
// style enum์œผ๋กœ ์‚ฌ์šฉ ์˜ˆ์ •
TeacherClassInfo.TeacherClassInfoBuilder builder = TeacherClassInfo.builder()
.introduce(request.introduce())
.teachingStyle1(TeachingStyle.fromString(request.teachingStyle1()))
.teachingStyle2(TeachingStyle.fromString(request.teachingStyle2()))
.teachingStyleInfo1(request.teachingStyleInfo1())
.teachingStyleInfo2(request.teachingStyleInfo1())
.comment(request.comment())
.englishPossible(request.englishPossible())
.mathPossible(request.mathPossible());
return builder.build();
}
Copy link

Choose a reason for hiding this comment

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

๐Ÿ› ๏ธ Refactor suggestion

โš ๏ธ Potential issue

Fix teachingStyleInfo2 mapping and reduce code duplication.

There's a bug in the teachingStyleInfo2 mapping and unnecessary code duplication:

  1. teachingStyleInfo2 is incorrectly mapped from teachingStyleInfo1
  2. Similar mapping logic is duplicated for different request types

Fix the immediate bug:

 private static TeacherClassInfo mapToTeacherClassInfo(TeacherInfoRequest request) {
     TeacherClassInfo.TeacherClassInfoBuilder builder = TeacherClassInfo.builder()
             .introduce(request.introduce())
             .teachingStyle1(TeachingStyle.fromString(request.teachingStyle1()))
             .teachingStyle2(TeachingStyle.fromString(request.teachingStyle2()))
             .teachingStyleInfo1(request.teachingStyleInfo1())
-            .teachingStyleInfo2(request.teachingStyleInfo1())
+            .teachingStyleInfo2(request.teachingStyleInfo2())
             .comment(request.comment())
             .englishPossible(request.englishPossible())
             .mathPossible(request.mathPossible());
     return builder.build();
 }

Consider using a common mapper interface to reduce duplication:

public interface TeacherRequestMapper<T> {
    TeacherInfo mapToTeacherInfo(T request);
    TeacherSchoolInfo mapToTeacherSchoolInfo(T request);
    TeacherClassInfo mapToTeacherClassInfo(T request);
}

@ywj9811 ywj9811 merged commit ffcbd35 into develop Feb 24, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
โœจ Feature New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant