diff --git a/src/main/java/classfit/example/classfit/drive/service/DriveFolderService.java b/src/main/java/classfit/example/classfit/drive/service/DriveFolderService.java index ed3d667b..a7ec3488 100644 --- a/src/main/java/classfit/example/classfit/drive/service/DriveFolderService.java +++ b/src/main/java/classfit/example/classfit/drive/service/DriveFolderService.java @@ -31,6 +31,9 @@ public class DriveFolderService { private String bucketName; public String createFolder(Member member, DriveType driveType, String folderName, String folderPath) { + if (folderName == null || folderName.trim().isEmpty()) { + throw new IllegalArgumentException("폴더 이름은 비어 있을 수 없습니다."); + } String uniqueFolderName = generateUniqueFolderName(member, driveType, folderName, folderPath); String fullFolderPath = generateFolderKey(member, driveType, uniqueFolderName, folderPath); diff --git a/src/main/java/classfit/example/classfit/drive/service/DriveGetService.java b/src/main/java/classfit/example/classfit/drive/service/DriveGetService.java index d4c0d5e6..4bf3e84b 100644 --- a/src/main/java/classfit/example/classfit/drive/service/DriveGetService.java +++ b/src/main/java/classfit/example/classfit/drive/service/DriveGetService.java @@ -30,9 +30,12 @@ public List getFilesFromS3(Member member, DriveType driveType, Str String prefix = DriveUtil.buildPrefix(driveType, member, folderPath); List objectSummaries = getS3ObjectList(prefix); + String folderPathWithSlash = folderPath.isEmpty() ? folderPath : folderPath + "/"; for (S3ObjectSummary summary : objectSummaries) { FileResponse fileInfo = buildFileInfo(summary); - files.add(fileInfo); + if (!fileInfo.fileName().equals(folderPathWithSlash)) { + files.add(fileInfo); + } } return files; } @@ -41,9 +44,14 @@ public List searchFilesByName(Member member, DriveType driveType, ListObjectsV2Request request = createListObjectsRequest(driveType, member, folderPath); ListObjectsV2Result result = amazonS3.listObjectsV2(request); + String folderPathWithSlash = folderPath.isEmpty() ? folderPath : folderPath + "/"; return result.getObjectSummaries().stream() .map(this::buildFileInfo) - .filter(fileInfo -> normalize(fileInfo.fileName()).contains(normalize(fileName))) // 정규화하여 필터링 + .filter(fileInfo -> { + boolean isFolder = fileInfo.fileName().equals(folderPathWithSlash); + boolean matchesFileName = fileName.isEmpty() || normalize(fileInfo.fileName()).contains(normalize(fileName)); + return !isFolder && matchesFileName; + }) .collect(Collectors.toList()); } @@ -72,8 +80,7 @@ private Map getTagsForS3Object(String objectKey) { private ListObjectsV2Request createListObjectsRequest(DriveType driveType, Member member, String folderPath) { ListObjectsV2Request request = new ListObjectsV2Request() - .withBucketName(bucketName) - .withDelimiter("/"); + .withBucketName(bucketName); String prefix = DriveUtil.buildPrefix(driveType, member, folderPath); request.setPrefix(prefix); @@ -88,11 +95,13 @@ public List classifyFilesByType(Member member, DriveType driveType ListObjectsV2Request request = createListObjectsRequest(driveType, member, folderPath); ListObjectsV2Result result = amazonS3.listObjectsV2(request); + String folderPathWithSlash = folderPath.isEmpty() ? folderPath : folderPath + "/"; return result.getObjectSummaries().stream() .map(this::buildFileInfo) .filter(fileInfo -> { - FileType fileType = DriveUtil.getFileType(fileInfo.fileName()); - return fileType.equals(filterFileType); + boolean isFolder = fileInfo.fileName().equals(folderPathWithSlash); + boolean matchesFileType = DriveUtil.getFileType(fileInfo.fileName()).equals(filterFileType); + return !isFolder && matchesFileType; }) .collect(Collectors.toList()); } diff --git a/src/main/java/classfit/example/classfit/drive/service/DriveUploadService.java b/src/main/java/classfit/example/classfit/drive/service/DriveUploadService.java index 537fba1b..ffdb9755 100644 --- a/src/main/java/classfit/example/classfit/drive/service/DriveUploadService.java +++ b/src/main/java/classfit/example/classfit/drive/service/DriveUploadService.java @@ -67,6 +67,7 @@ private void addTagsToS3Object(String objectKey, Member member, String folderPat LocalDateTime now = LocalDateTime.now(); String formattedDate = now.format(DateTimeFormatter.ISO_DATE_TIME); String finalFolderPath = folderPath != null && !folderPath.trim().isEmpty() ? folderPath : ""; + finalFolderPath = finalFolderPath + "/"; List tags = List.of( new Tag("folderPath", finalFolderPath), diff --git a/src/main/java/classfit/example/classfit/event/controller/EventController.java b/src/main/java/classfit/example/classfit/event/controller/EventController.java index e381bda4..1f276b12 100644 --- a/src/main/java/classfit/example/classfit/event/controller/EventController.java +++ b/src/main/java/classfit/example/classfit/event/controller/EventController.java @@ -6,7 +6,7 @@ import classfit.example.classfit.common.ApiResponse; import classfit.example.classfit.event.dto.request.EventCreateRequest; import classfit.example.classfit.event.dto.response.EventModalResponse; -import classfit.example.classfit.event.dto.response.EventMontylyResponse; +import classfit.example.classfit.event.dto.response.EventMonthlyResponse; import classfit.example.classfit.event.dto.response.EventResponse; import classfit.example.classfit.event.service.EventService; import classfit.example.classfit.member.domain.Member; @@ -54,12 +54,12 @@ public ApiResponse> getAcademyMembers(@AuthMember Me @GetMapping("/monthly") @Operation(summary = "월별 일정 조회", description = "월별 일정들을 조회하는 api 입니다.") - public ApiResponse> getMonthlyEvents( + public ApiResponse> getMonthlyEvents( @RequestParam CalendarType calendarType, @RequestParam int year, @RequestParam int month ) { - List events = eventService.getMonthlyEventsByCalendarType(calendarType, year, month); + List events = eventService.getMonthlyEventsByCalendarType(calendarType, year, month); return ApiResponse.success(events, 200, "SUCCESS"); } diff --git a/src/main/java/classfit/example/classfit/event/domain/Event.java b/src/main/java/classfit/example/classfit/event/domain/Event.java index 8be84455..efb53505 100644 --- a/src/main/java/classfit/example/classfit/event/domain/Event.java +++ b/src/main/java/classfit/example/classfit/event/domain/Event.java @@ -148,6 +148,7 @@ public static EventModalResponse buildModalEventResponse(Event event) { return EventModalResponse.of( event.getId(), event.getName(), + event.getMemberCalendar().getType(), event.getEventType(), event.getStartDate(), event.getEndDate(), diff --git a/src/main/java/classfit/example/classfit/event/dto/response/EventModalResponse.java b/src/main/java/classfit/example/classfit/event/dto/response/EventModalResponse.java index a406060f..371d24ec 100644 --- a/src/main/java/classfit/example/classfit/event/dto/response/EventModalResponse.java +++ b/src/main/java/classfit/example/classfit/event/dto/response/EventModalResponse.java @@ -2,12 +2,14 @@ import classfit.example.classfit.event.domain.EventRepeatType; import classfit.example.classfit.event.domain.EventType; +import classfit.example.classfit.memberCalendar.domain.CalendarType; import java.time.LocalDateTime; public record EventModalResponse ( Long id, String name, + CalendarType calendarType, EventType eventType, LocalDateTime startDate, LocalDateTime endDate, @@ -16,7 +18,7 @@ public record EventModalResponse LocalDateTime repeatEndDate, boolean isAllDay ) { - public static EventModalResponse of(final Long id, final String name, final EventType eventType, final LocalDateTime startDate, final LocalDateTime endDate, final Long categoryId, final EventRepeatType eventRepeatType, final LocalDateTime repeatEndDate, final boolean isAllDay) { - return new EventModalResponse(id, name, eventType, startDate, endDate, categoryId, eventRepeatType, repeatEndDate, isAllDay); + public static EventModalResponse of(final Long id, final String name, final CalendarType calendarType, final EventType eventType, final LocalDateTime startDate, final LocalDateTime endDate, final Long categoryId, final EventRepeatType eventRepeatType, final LocalDateTime repeatEndDate, final boolean isAllDay) { + return new EventModalResponse(id, name, calendarType, eventType, startDate, endDate, categoryId, eventRepeatType, repeatEndDate, isAllDay); } } diff --git a/src/main/java/classfit/example/classfit/event/dto/response/EventMontylyResponse.java b/src/main/java/classfit/example/classfit/event/dto/response/EventMonthlyResponse.java similarity index 67% rename from src/main/java/classfit/example/classfit/event/dto/response/EventMontylyResponse.java rename to src/main/java/classfit/example/classfit/event/dto/response/EventMonthlyResponse.java index 5efe15b4..7a9ee085 100644 --- a/src/main/java/classfit/example/classfit/event/dto/response/EventMontylyResponse.java +++ b/src/main/java/classfit/example/classfit/event/dto/response/EventMonthlyResponse.java @@ -1,6 +1,6 @@ package classfit.example.classfit.event.dto.response; -public record EventMontylyResponse +public record EventMonthlyResponse ( String id, String name, @@ -9,7 +9,7 @@ public record EventMontylyResponse String startDate, String endDate ) { - public static EventMontylyResponse of(final String id, final String name, final String color, final String eventType, final String startDate, final String endDate) { - return new EventMontylyResponse(id, name, color, eventType, startDate, endDate); + public static EventMonthlyResponse of(final String id, final String name, final String color, final String eventType, final String startDate, final String endDate) { + return new EventMonthlyResponse(id, name, color, eventType, startDate, endDate); } } diff --git a/src/main/java/classfit/example/classfit/event/service/EventGetService.java b/src/main/java/classfit/example/classfit/event/service/EventGetService.java index bb098abf..60fb965a 100644 --- a/src/main/java/classfit/example/classfit/event/service/EventGetService.java +++ b/src/main/java/classfit/example/classfit/event/service/EventGetService.java @@ -5,8 +5,7 @@ import classfit.example.classfit.common.exception.ClassfitException; import classfit.example.classfit.event.domain.Event; import classfit.example.classfit.event.dto.response.EventModalResponse; -import classfit.example.classfit.event.dto.response.EventMontylyResponse; -import classfit.example.classfit.event.dto.response.EventResponse; +import classfit.example.classfit.event.dto.response.EventMonthlyResponse; import classfit.example.classfit.event.repository.EventRepository; import classfit.example.classfit.memberCalendar.domain.CalendarType; import java.time.LocalDateTime; @@ -35,7 +34,7 @@ private Event getEventById(long eventId) { } @Transactional(readOnly = true) - public List getMonthlyEventsByCalendarType(CalendarType calendarType, int year, int month) { + public List getMonthlyEventsByCalendarType(CalendarType calendarType, int year, int month) { LocalDateTime startOfMonth = LocalDateTime.of(year, month, 1, 0, 0, 0, 0); LocalDateTime endOfMonth = startOfMonth.plusMonths(1).minusSeconds(1); @@ -43,11 +42,11 @@ public List getMonthlyEventsByCalendarType(CalendarType ca return mapToEventCreateResponse(events); } - private List mapToEventCreateResponse(List events) { + private List mapToEventCreateResponse(List events) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); return events.stream() - .map(event -> EventMontylyResponse.of( + .map(event -> EventMonthlyResponse.of( String.valueOf(event.getId()), event.getName(), event.getCategory() != null ? String.valueOf(event.getCategory().getColor().getHexCode()) : "000000", diff --git a/src/main/java/classfit/example/classfit/event/service/EventRepeatService.java b/src/main/java/classfit/example/classfit/event/service/EventRepeatService.java index d53df5b4..290cfcf4 100644 --- a/src/main/java/classfit/example/classfit/event/service/EventRepeatService.java +++ b/src/main/java/classfit/example/classfit/event/service/EventRepeatService.java @@ -127,7 +127,7 @@ private Event buildModalEventWithUpdatedDates( .eventRepeatType(request.eventRepeatType()) .repeatEndDate(request.repeatEndDate().orElse(null)) .build(); - event.setDates(request.isAllDay(), request.startDate(), request.getEndDate()); + event.setDates(request.isAllDay(), currentStartDate, currentEndDate); return event; } diff --git a/src/main/java/classfit/example/classfit/event/service/EventService.java b/src/main/java/classfit/example/classfit/event/service/EventService.java index 6ff0e74e..010891a8 100644 --- a/src/main/java/classfit/example/classfit/event/service/EventService.java +++ b/src/main/java/classfit/example/classfit/event/service/EventService.java @@ -8,7 +8,7 @@ import classfit.example.classfit.event.dto.request.EventDragUpdate; import classfit.example.classfit.event.dto.request.EventModalRequest; import classfit.example.classfit.event.dto.response.EventModalResponse; -import classfit.example.classfit.event.dto.response.EventMontylyResponse; +import classfit.example.classfit.event.dto.response.EventMonthlyResponse; import classfit.example.classfit.event.dto.response.EventResponse; import classfit.example.classfit.event.repository.EventRepository; import classfit.example.classfit.member.domain.Member; @@ -43,7 +43,7 @@ public EventModalResponse getEvent(long eventId) { return eventGetService.getEvent(eventId); } - public List getMonthlyEventsByCalendarType( + public List getMonthlyEventsByCalendarType( CalendarType calendarType, int year, int month diff --git a/src/main/java/classfit/example/classfit/member/domain/Member.java b/src/main/java/classfit/example/classfit/member/domain/Member.java index 3ceb1a9d..aae204b0 100644 --- a/src/main/java/classfit/example/classfit/member/domain/Member.java +++ b/src/main/java/classfit/example/classfit/member/domain/Member.java @@ -1,14 +1,12 @@ package classfit.example.classfit.member.domain; import classfit.example.classfit.academy.domain.Academy; -import classfit.example.classfit.category.domain.MainClass; import classfit.example.classfit.common.domain.BaseEntity; import classfit.example.classfit.member.dto.request.MemberUpdateInfoRequest; import jakarta.persistence.*; -import java.time.LocalDate; import lombok.*; -import java.util.List; +import java.time.LocalDate; @Entity @Getter @@ -41,10 +39,10 @@ public class Member extends BaseEntity { @Column(columnDefinition = "VARCHAR(20)", nullable = false) private MemberStatus status; - @Column(nullable = false) + @Column(length = 20) private LocalDate birthDate; - @Column(nullable = false) + @Column(length = 20) private String subject; @ManyToOne(fetch = FetchType.LAZY) diff --git a/src/main/java/classfit/example/classfit/student/controller/StudentControllerImpl.java b/src/main/java/classfit/example/classfit/student/controller/StudentController.java similarity index 92% rename from src/main/java/classfit/example/classfit/student/controller/StudentControllerImpl.java rename to src/main/java/classfit/example/classfit/student/controller/StudentController.java index f08da7be..bbeb675f 100644 --- a/src/main/java/classfit/example/classfit/student/controller/StudentControllerImpl.java +++ b/src/main/java/classfit/example/classfit/student/controller/StudentController.java @@ -1,6 +1,8 @@ package classfit.example.classfit.student.controller; +import classfit.example.classfit.auth.annotation.AuthMember; import classfit.example.classfit.common.ApiResponse; +import classfit.example.classfit.member.domain.Member; import classfit.example.classfit.student.dto.request.StudentRequest; import classfit.example.classfit.student.dto.request.StudentUpdateRequest; import classfit.example.classfit.student.dto.response.StudentInfoResponse; @@ -18,7 +20,7 @@ @RequestMapping("/api/v1/student") @RequiredArgsConstructor @Tag(name = "학생 컨트롤러", description = "학생 관련 API") -public class StudentControllerImpl { +public class StudentController { private final StudentService studentService; @@ -32,9 +34,9 @@ public ApiResponse registerStudent(@RequestBody @Valid StudentR @GetMapping("/") @Operation(summary = "학생 정보 조회", description = "전체 학생 정보 조회하는 API 입니다. ") - public ApiResponse> studentInfoAll() { + public ApiResponse> studentInfoAll(@AuthMember Member member) { - List studentList = studentService.studentInfoAll(); + List studentList = studentService.studentInfoAll(member); return ApiResponse.success(studentList, 200, "FIND STUDENTS"); } diff --git a/src/main/java/classfit/example/classfit/student/domain/Student.java b/src/main/java/classfit/example/classfit/student/domain/Student.java index 7a36187b..20d0ad39 100644 --- a/src/main/java/classfit/example/classfit/student/domain/Student.java +++ b/src/main/java/classfit/example/classfit/student/domain/Student.java @@ -39,7 +39,7 @@ public class Student extends BaseEntity { @Column(nullable = false, length = 14) private String parentNumber; - @Column(nullable = false, length = 10) + @Column(nullable = false, length = 20) private String grade; @Column(nullable = false, length = 100) diff --git a/src/main/java/classfit/example/classfit/student/repository/StudentRepository.java b/src/main/java/classfit/example/classfit/student/repository/StudentRepository.java index e683361c..ed9ff6cb 100644 --- a/src/main/java/classfit/example/classfit/student/repository/StudentRepository.java +++ b/src/main/java/classfit/example/classfit/student/repository/StudentRepository.java @@ -22,4 +22,12 @@ public interface StudentRepository extends JpaRepository { List findSubClassesByStudentId(@Param("studentId") Long studentId); Optional> findAllByName(String studentName); + + @Query("SELECT s FROM Student s " + + "JOIN FETCH ClassStudent cs ON s.id = cs.student.id " + + "JOIN FETCH SubClass sc ON cs.subClass.id = sc.id " + + "JOIN FETCH MainClass mc ON sc.mainClass.id = mc.id " + + "JOIN FETCH Academy a ON mc.academy.id = a.id " + + "WHERE a.id = :academyId") + List findStudentsByAcademyId(Long academyId); } diff --git a/src/main/java/classfit/example/classfit/student/service/StudentService.java b/src/main/java/classfit/example/classfit/student/service/StudentService.java index 75019fc3..4758abf2 100644 --- a/src/main/java/classfit/example/classfit/student/service/StudentService.java +++ b/src/main/java/classfit/example/classfit/student/service/StudentService.java @@ -1,5 +1,6 @@ package classfit.example.classfit.student.service; +import classfit.example.classfit.academy.domain.Academy; import classfit.example.classfit.attendance.domain.Attendance; import classfit.example.classfit.attendance.domain.AttendanceStatus; import classfit.example.classfit.attendance.repository.AttendanceRepository; @@ -8,6 +9,7 @@ import classfit.example.classfit.classStudent.domain.ClassStudent; import classfit.example.classfit.classStudent.repository.ClassStudentRepository; import classfit.example.classfit.common.exception.ClassfitException; +import classfit.example.classfit.member.domain.Member; import classfit.example.classfit.student.domain.Gender; import classfit.example.classfit.student.domain.Student; import classfit.example.classfit.student.dto.request.StudentRequest; @@ -37,12 +39,11 @@ public class StudentService { private final AttendanceRepository attendanceRepository; @Transactional - public StudentResponse registerStudent(StudentRequest req) { - - Student student = req.toEntity(true); + public StudentResponse registerStudent(StudentRequest request) { + Student student = request.toEntity(true); studentRepository.save(student); - req.subClassList().forEach(subClassId -> { + request.subClassList().forEach(subClassId -> { SubClass subClass = subClassRepository.findById(subClassId).orElseThrow( () -> new ClassfitException("존재하지 않는 SubClass ID입니다.", HttpStatus.NOT_FOUND)); ClassStudent classStudent = new ClassStudent(); @@ -56,31 +57,8 @@ public StudentResponse registerStudent(StudentRequest req) { return StudentResponse.from(student); } - private void createAttendanceForThreeWeeks(Student student) { - LocalDate currentDate = LocalDate.now(); - LocalDate weekStart = currentDate.with(DayOfWeek.MONDAY); - ClassStudent classStudent = classStudentRepository.findByStudent(student); - - // 3주간의 출결 생성 (현재 주 + 향후 2주) - for (int i = 0; i < 3; i++) { - LocalDate weekDate = weekStart.plusWeeks(i); - for (int j = 0; j < 7; j++) { - LocalDate attendanceDate = weekDate.plusDays(j); - Attendance attendance = Attendance.builder() - .date(attendanceDate) - .week(j) - .status(AttendanceStatus.PRESENT) - .student(student) - .classStudent(classStudent) - .build(); - attendanceRepository.save(attendance); - } - } - } - @Transactional public void deleteStudent(List studentIds) { - studentIds.stream() .map(studentId -> studentRepository.findById(studentId).orElseThrow(() -> new ClassfitException("해당하는 학생 정보를 찾을 수 없습니다", HttpStatus.NOT_FOUND))) @@ -91,8 +69,9 @@ public void deleteStudent(List studentIds) { } @Transactional(readOnly = true) - public List studentInfoAll() { - List studentAll = studentRepository.findAll(); + public List studentInfoAll(Member member) { + Academy academy = member.getAcademy(); + List studentAll = studentRepository.findStudentsByAcademyId(academy.getId()); return studentAll.stream() .map(StudentResponse::from) @@ -125,8 +104,8 @@ public void updateStudent(Long studentId, StudentUpdateRequest req) { Student student = studentRepository.findById(studentId).orElseThrow( () -> new ClassfitException("해당하는 학생 정보가 존재하지 않습니다.", HttpStatus.NOT_FOUND)); - updateFields(student, req); // 필드 업데이트 - updateSubClasses(student, req.subClassList()); // 서브 클래스 업데이트 + updateFields(student, req); + updateSubClasses(student, req.subClassList()); } private void updateFields(Student student, StudentUpdateRequest req) { @@ -176,4 +155,26 @@ private void updateSubClasses(Student student, List subClassList) { classStudentRepository.save(classStudent); }); } + + private void createAttendanceForThreeWeeks(Student student) { + LocalDate currentDate = LocalDate.now(); + LocalDate weekStart = currentDate.with(DayOfWeek.MONDAY); + ClassStudent classStudent = classStudentRepository.findByStudent(student); + + // 3주간의 출결 생성 (현재 주 + 향후 2주) + for (int i = 0; i < 3; i++) { + LocalDate weekDate = weekStart.plusWeeks(i); + for (int j = 0; j < 7; j++) { + LocalDate attendanceDate = weekDate.plusDays(j); + Attendance attendance = Attendance.builder() + .date(attendanceDate) + .week(j) + .status(AttendanceStatus.PRESENT) + .student(student) + .classStudent(classStudent) + .build(); + attendanceRepository.save(attendance); + } + } + } } \ No newline at end of file