-
Notifications
You must be signed in to change notification settings - Fork 0
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
Feat/sheets #66
Conversation
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 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. ๐ Files selected for processing (3)
WalkthroughThis 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
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
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
Poem
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? ๐ชง TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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 javaLength 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:
- Adding validation for the birth date format in the service layer
- 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 javaLength 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 berecommendedStudent
.
34-49
: Consider extracting common fields to an interface.The
English
andMath
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
๐ 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
tophoneNumber
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.
@PostMapping("/profile") | ||
public ResponseEntity<Void> uploadProfile(@RequestPart List<MultipartFile> profiles) { | ||
manageUseCase.saveTeacherProfile(profiles); | ||
return ResponseEntity.ok().build(); | ||
} |
There was a problem hiding this comment.
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:
- File size limits
- File type validation (only images)
- Error handling
- 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.
@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()); | |
} | |
} |
@PostMapping("/upload") | ||
public ResponseEntity<Void> uploadExcelFile(@RequestPart MultipartFile file) { | ||
manageUseCase.saveTeacherByExcelFile(file); | ||
return ResponseEntity.ok().build(); | ||
} |
There was a problem hiding this comment.
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:
- File size limits
- File type validation (only Excel files)
- Error handling for file processing failures
- 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.
@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()); | |
} | |
} |
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 | ||
) { |
There was a problem hiding this comment.
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:
- Email validation for
email
- Phone number format validation for
phoneNumber
- Date format validation for
birth
- Non-null validation for required fields
- 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.
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 | |
) { | |
} |
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; | ||
} |
There was a problem hiding this comment.
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:
- Validate file type/extension before processing
- Check file size limits
- Validate minimum required columns
- 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.
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"); | |
} | |
} |
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); | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add file validation and error handling for profile uploads.
The profile upload process needs better validation and error handling:
- Missing file type validation
- Unsafe filename parsing
- No error handling for S3 operations
- 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.
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"); | |
} | |
} |
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); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve error handling and data validation.
Several issues need attention:
- Generic exception handling may mask specific errors
- Potential NPE in region trimming
- Empty districts list passed to saveTeacher
- 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.
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"); | |
} | |
} |
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) | ||
); | ||
} |
There was a problem hiding this comment.
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:
- Magic numbers for cell indices
- Unsafe type conversions
- No validation of required fields
- 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.
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(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
๐ ๏ธ Refactor suggestion
Fix teachingStyleInfo2 mapping and reduce code duplication.
There's a bug in the teachingStyleInfo2 mapping and unnecessary code duplication:
teachingStyleInfo2
is incorrectly mapped fromteachingStyleInfo1
- 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);
}
๐ PR ์์ฝ
โจ PR ์์ธ
๊ธฐ์กด ๋ฐ์ดํฐ ์ด์ ์ค๋น
๐จ ์ฐธ๊ณ ์ฌํญ
โ ์ฒดํฌ๋ฆฌ์คํธ
Summary by CodeRabbit
New Features
Chores