From 2cd55e688ff35cc05fbec74000eb5842c46811c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=85=B8=ED=98=84=EC=A3=BC?= Date: Fri, 30 Aug 2024 10:16:16 +0900 Subject: [PATCH 01/13] add :: lecture excel api mapping --- .../team/msg/domain/admin/presentation/AdminController.kt | 6 ++++++ .../kotlin/team/msg/domain/admin/service/AdminService.kt | 1 + .../main/kotlin/team/msg/global/security/SecurityConfig.kt | 1 + 3 files changed, 8 insertions(+) diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/presentation/AdminController.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/presentation/AdminController.kt index ce3e453c..229e1b90 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/presentation/AdminController.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/presentation/AdminController.kt @@ -52,4 +52,10 @@ class AdminController( adminService.uploadClubListExcel(file) return ResponseEntity.status(HttpStatus.CREATED).build() } + + @PostMapping("/lecture/excel") + fun uploadLectureListExcel(@RequestPart file: MultipartFile): ResponseEntity { + adminService.uploadLectureListExcel(file) + return ResponseEntity.status(HttpStatus.CREATED).build() + } } \ No newline at end of file diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminService.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminService.kt index 2caadeb8..95426036 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminService.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminService.kt @@ -12,4 +12,5 @@ interface AdminService { fun forceWithdraw(userIds: List) fun uploadStudentListExcel(file: MultipartFile) fun uploadClubListExcel(file: MultipartFile) + fun uploadLectureListExcel(file: MultipartFile) } \ No newline at end of file diff --git a/bitgouel-api/src/main/kotlin/team/msg/global/security/SecurityConfig.kt b/bitgouel-api/src/main/kotlin/team/msg/global/security/SecurityConfig.kt index e9a1fd71..33ffc090 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/global/security/SecurityConfig.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/global/security/SecurityConfig.kt @@ -135,6 +135,7 @@ class SecurityConfig( .mvcMatchers(HttpMethod.DELETE, "/admin/withdraw").hasRole(ADMIN) .mvcMatchers(HttpMethod.POST, "/admin/student/excel").hasRole(ADMIN) .mvcMatchers(HttpMethod.POST, "/admin/club/excel").hasRole(ADMIN) + .mvcMatchers(HttpMethod.POST, "/admin/lecture/excel").hasRole(ADMIN) // inquiry .mvcMatchers(HttpMethod.POST, "/inquiry").authenticated() From bd8e8fc04e592cbadc1e83ce0a394b7898e65e4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=85=B8=ED=98=84=EC=A3=BC?= Date: Sat, 31 Aug 2024 21:35:03 +0900 Subject: [PATCH 02/13] add :: find user name and email --- .../kotlin/team/msg/domain/user/repository/UserRepository.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/bitgouel-domain/src/main/kotlin/team/msg/domain/user/repository/UserRepository.kt b/bitgouel-domain/src/main/kotlin/team/msg/domain/user/repository/UserRepository.kt index 799057d8..73f75f52 100644 --- a/bitgouel-domain/src/main/kotlin/team/msg/domain/user/repository/UserRepository.kt +++ b/bitgouel-domain/src/main/kotlin/team/msg/domain/user/repository/UserRepository.kt @@ -15,4 +15,5 @@ interface UserRepository : CrudRepository, CustomUserRepository { @Modifying(flushAutomatically = true, clearAutomatically = true) @Query("delete from User u where u.id in :ids") fun deleteByIdIn(ids: List) + fun findByNameAndEmail(name: String, email: String): User? } \ No newline at end of file From 8a684d0de4bea71abf36a58a8da1345118cd8518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=85=B8=ED=98=84=EC=A3=BC?= Date: Sun, 1 Sep 2024 21:42:28 +0900 Subject: [PATCH 03/13] =?UTF-8?q?add=20::=20lecture=20excel=20service=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/admin/service/AdminServiceImpl.kt | 133 +++++++++++++++++- 1 file changed, 132 insertions(+), 1 deletion(-) diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt index 70337bd2..97098c51 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt @@ -6,6 +6,7 @@ import org.springframework.transaction.annotation.Transactional import org.springframework.web.multipart.MultipartFile import team.msg.common.enums.ApproveStatus import team.msg.common.enums.Field +import team.msg.common.util.KakaoUtil import team.msg.common.util.StudentUtil import team.msg.common.util.UserUtil import team.msg.domain.admin.exception.InvalidCellTypeException @@ -15,6 +16,13 @@ import team.msg.domain.club.exception.ClubNotFoundException import team.msg.domain.club.exception.InvalidFieldException import team.msg.domain.club.model.Club import team.msg.domain.club.repository.ClubRepository +import team.msg.domain.lecture.enums.Semester +import team.msg.domain.lecture.model.Lecture +import team.msg.domain.lecture.model.LectureDate +import team.msg.domain.lecture.model.LectureLocation +import team.msg.domain.lecture.repository.LectureDateRepository +import team.msg.domain.lecture.repository.LectureLocationRepository +import team.msg.domain.lecture.repository.LectureRepository import team.msg.domain.school.exception.SchoolNotFoundException import team.msg.domain.school.repository.SchoolRepository import team.msg.domain.student.repository.StudentRepository @@ -23,11 +31,16 @@ import team.msg.domain.user.exception.InvalidEmailException import team.msg.domain.user.exception.InvalidPasswordException import team.msg.domain.user.exception.InvalidPhoneNumberException import team.msg.domain.user.exception.UserAlreadyApprovedException +import team.msg.domain.user.exception.UserNotFoundException import team.msg.domain.user.model.User import team.msg.domain.user.presentation.data.response.UserResponse import team.msg.domain.user.presentation.data.response.UsersResponse import team.msg.domain.user.repository.UserRepository import team.msg.global.exception.InternalServerException +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.LocalTime +import java.time.format.DateTimeFormatter import java.util.* @Service @@ -37,7 +50,11 @@ class AdminServiceImpl( private val studentUtil: StudentUtil, private val clubRepository: ClubRepository, private val studentRepository: StudentRepository, - private val schoolRepository: SchoolRepository + private val schoolRepository: SchoolRepository, + private val lectureRepository: LectureRepository, + private val lectureDateRepository: LectureDateRepository, + private val kakaoUtil: KakaoUtil, + private val lectureLocationRepository: LectureLocationRepository ) : AdminService { /** @@ -214,6 +231,111 @@ class AdminServiceImpl( } } + /** + * 강의 리스트 엑셀을 업로드 하는 비지니스 로직입니다 + * @param file 강의 리스트 엑셀 업로드를 위한 MultipartFile + */ + @Transactional(rollbackFor = [Exception::class]) + override fun uploadLectureListExcel(file: MultipartFile) { + file.inputStream.use { + val workbook = try { + WorkbookFactory.create(file.inputStream) + } catch (e: Exception) { + throw InternalServerException("엑셀 파일 처리 중 문제가 발생했습니다. info : [ errorMessage = ${e.message} ]") + } + + val sheet = workbook.getSheetAt(0) + + sheet.forEachIndexed { index, row -> + if (index == 0 || index == 1) + return@forEachIndexed + + if (row.getCell(0).stringCellValue == "") + return + + val name = row.getCell(0).stringCellValue + val content = row.getCell(1).stringCellValue + + val instructorName = row.getCell(2).stringCellValue + val instructorEmail = row.getCell(3).stringCellValue + + val instructor = userRepository.findByNameAndEmail(instructorName, instructorEmail) + ?: throw UserNotFoundException("존재하지 않는 강사입니다. info : [ name = $instructorName, email = $instructorEmail ]") + + val type = row.getCell(4).stringCellValue + val credit = row.getCell(5).stringCellValue.toInt() + val semester = row.getCell(6).stringCellValue + + val lectureSemester = when(semester) { + "1학년 2학기" -> Semester.FIRST_YEAR_FALL_SEMESTER + "2학년 1학기" -> Semester.SECOND_YEAR_SPRING_SEMESTER + "2학년 2학기" -> Semester.SECOND_YEAR_FALL_SEMESTER + "3학년 1학기" -> Semester.THIRD_YEAR_SPRING_SEMESTER + else -> Semester.NOT_APPLICABLE + } + + val division = row.getCell(7).stringCellValue + val line = row.getCell(8).stringCellValue + val department = row.getCell(9).stringCellValue + val maxRegisteredUser = row.getCell(10).stringCellValue.toInt() + + val startDate = row.getCell(11).stringCellValue.toLocalDateTime() + val endDate = row.getCell(12).stringCellValue.toLocalDateTime() + val essentialComplete = row.getCell(16).stringCellValue + + val lectureEssentialComplete = essentialComplete == "O" + + val lecture = Lecture( + id = UUID(0, 0), + name = name, + content = content, + instructor = instructorName, + user = instructor, + lectureType = type, + credit = credit, + semester = lectureSemester, + division = division, + line = line, + department = department, + maxRegisteredUser = maxRegisteredUser, + startDate = startDate, + endDate = endDate, + essentialComplete = lectureEssentialComplete + ) + + lectureRepository.save(lecture) + + val lectureDates = row.getCell(13).stringCellValue + .split(",").map { + val timeZone = it.split(" ", "~") + LectureDate( + id = UUID(0, 0), + lecture = lecture, + completeDate = timeZone[0].toLocalDate(), + startTime = timeZone[1].toLocalTime(), + endTime = timeZone[2].toLocalTime() + ) + } + + lectureDateRepository.saveAll(lectureDates) + + val address = row.getCell(14).stringCellValue + val addressDetails = row.getCell(15).stringCellValue + + val coordinate = kakaoUtil.getCoordinate(address) + val lectureLocation = LectureLocation( + lectureId = lecture.id, + x = coordinate.first, + y = coordinate.second, + address = address, + details = addressDetails + ) + + lectureLocationRepository.save(lectureLocation) + } + } + } + private fun validateExcelStudentData(email: String, phoneNumber: String, password: String) { val emailRegex = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\$".toRegex() if (!email.matches(emailRegex)) @@ -239,4 +361,13 @@ class AdminServiceImpl( const val AI_CONVERGENCE = "AI 융복합" const val CULTURE = "문화산업" } + + private fun String.toLocalDateTime(): LocalDateTime = + LocalDateTime.parse(this, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) + + private fun String.toLocalDate(): LocalDate = + LocalDate.parse(this, DateTimeFormatter.ofPattern("yyyy-MM-dd")) + + private fun String.toLocalTime(): LocalTime = + LocalTime.parse(this, DateTimeFormatter.ofPattern("HH:mm")) } \ No newline at end of file From 0fc894495dce737848891d7257745135e83eff64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=85=B8=ED=98=84=EC=A3=BC?= Date: Sun, 1 Sep 2024 23:42:48 +0900 Subject: [PATCH 04/13] =?UTF-8?q?update=20::=20=ED=83=88=EC=B6=9C=20?= =?UTF-8?q?=EC=A1=B0=EA=B1=B4=EC=8B=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/msg/domain/admin/service/AdminServiceImpl.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt index 97098c51..88803fe4 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt @@ -250,7 +250,7 @@ class AdminServiceImpl( if (index == 0 || index == 1) return@forEachIndexed - if (row.getCell(0).stringCellValue == "") + if (row.getCell(0) == null || row.getCell(0).stringCellValue == "") return val name = row.getCell(0).stringCellValue @@ -263,7 +263,7 @@ class AdminServiceImpl( ?: throw UserNotFoundException("존재하지 않는 강사입니다. info : [ name = $instructorName, email = $instructorEmail ]") val type = row.getCell(4).stringCellValue - val credit = row.getCell(5).stringCellValue.toInt() + val credit = row.getCell(5).numericCellValue.toInt() val semester = row.getCell(6).stringCellValue val lectureSemester = when(semester) { @@ -277,7 +277,7 @@ class AdminServiceImpl( val division = row.getCell(7).stringCellValue val line = row.getCell(8).stringCellValue val department = row.getCell(9).stringCellValue - val maxRegisteredUser = row.getCell(10).stringCellValue.toInt() + val maxRegisteredUser = row.getCell(10).numericCellValue.toInt() val startDate = row.getCell(11).stringCellValue.toLocalDateTime() val endDate = row.getCell(12).stringCellValue.toLocalDateTime() From a672005ba5456a106c99eb3e951da5dbde69dabe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=85=B8=ED=98=84=EC=A3=BC?= Date: Mon, 2 Sep 2024 00:03:07 +0900 Subject: [PATCH 05/13] =?UTF-8?q?add=20::=20=EC=98=88=EC=99=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt index 88803fe4..16f616a3 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt @@ -240,7 +240,9 @@ class AdminServiceImpl( file.inputStream.use { val workbook = try { WorkbookFactory.create(file.inputStream) - } catch (e: Exception) { + } catch (e: IndexOutOfBoundsException) { + throw InvalidCellTypeException("셀 서식을 텍스트로 변경해주세요.") + } catch (e: Exception) { throw InternalServerException("엑셀 파일 처리 중 문제가 발생했습니다. info : [ errorMessage = ${e.message} ]") } From bf8445c0a48e0f53bbfca9de97eb31b6997a87cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=85=B8=ED=98=84=EC=A3=BC?= Date: Mon, 2 Sep 2024 00:21:35 +0900 Subject: [PATCH 06/13] =?UTF-8?q?add=20::=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=A2=85=EC=86=8D=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/admin/service/AdminServiceImplTest.kt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/bitgouel-api/src/test/kotlin/team/msg/domain/admin/service/AdminServiceImplTest.kt b/bitgouel-api/src/test/kotlin/team/msg/domain/admin/service/AdminServiceImplTest.kt index 58e17cd2..df2deaa7 100644 --- a/bitgouel-api/src/test/kotlin/team/msg/domain/admin/service/AdminServiceImplTest.kt +++ b/bitgouel-api/src/test/kotlin/team/msg/domain/admin/service/AdminServiceImplTest.kt @@ -11,10 +11,14 @@ import io.mockk.just import io.mockk.mockk import io.mockk.verify import team.msg.common.enums.ApproveStatus +import team.msg.common.util.KakaoUtil import team.msg.common.util.StudentUtil import team.msg.common.util.UserUtil import team.msg.domain.admin.presentation.data.request.QueryUsersRequest import team.msg.domain.club.repository.ClubRepository +import team.msg.domain.lecture.repository.LectureDateRepository +import team.msg.domain.lecture.repository.LectureLocationRepository +import team.msg.domain.lecture.repository.LectureRepository import team.msg.domain.school.repository.SchoolRepository import team.msg.domain.student.model.Student import team.msg.domain.student.repository.StudentRepository @@ -36,13 +40,21 @@ class AdminServiceImplTest : BehaviorSpec({ val clubRepository = mockk() val studentRepository = mockk() val schoolRepository = mockk() + val lectureRepository = mockk() + val lectureDateRepository = mockk() + val lectureLocationRepository = mockk() + val kakaoUtil = mockk() val adminServiceImpl = AdminServiceImpl( userRepository = userRepository, userUtil = userUtil, studentUtil = studentUtil, clubRepository = clubRepository, studentRepository = studentRepository, - schoolRepository = schoolRepository + schoolRepository = schoolRepository, + lectureRepository = lectureRepository, + lectureDateRepository = lectureDateRepository, + lectureLocationRepository = lectureLocationRepository, + kakaoUtil = kakaoUtil ) // queryUsers 테스트 코드 From 149bc6db71be04bd567bfed985fcb86212ff20f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=85=B8=ED=98=84=EC=A3=BC?= Date: Mon, 2 Sep 2024 00:25:13 +0900 Subject: [PATCH 07/13] =?UTF-8?q?add=20::=20=EC=9C=A0=EB=8B=88=ED=81=AC=20?= =?UTF-8?q?=EC=86=8D=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/kotlin/team/msg/domain/user/model/User.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bitgouel-domain/src/main/kotlin/team/msg/domain/user/model/User.kt b/bitgouel-domain/src/main/kotlin/team/msg/domain/user/model/User.kt index 92decc71..1f05d4e4 100644 --- a/bitgouel-domain/src/main/kotlin/team/msg/domain/user/model/User.kt +++ b/bitgouel-domain/src/main/kotlin/team/msg/domain/user/model/User.kt @@ -15,13 +15,13 @@ class User( @get:JvmName("getIdentifier") override var id: UUID, - @Column(columnDefinition = "VARBINARY(100)", nullable = false) + @Column(columnDefinition = "VARBINARY(100)", nullable = false, unique = true) val email: String, @Column(columnDefinition = "VARCHAR(10)", nullable = false) val name: String, - @Column(columnDefinition = "VARCHAR(20)", nullable = false) + @Column(columnDefinition = "VARCHAR(20)", nullable = false, unique = true) val phoneNumber: String, @Column(columnDefinition = "VARBINARY(100)", nullable = false) From 6459d1ca5d0809c99839bb7d54be20a8c52803db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=85=B8=ED=98=84=EC=A3=BC?= Date: Mon, 2 Sep 2024 00:26:40 +0900 Subject: [PATCH 08/13] =?UTF-8?q?update=20::=20email=EB=A1=9C=EB=A7=8C=20u?= =?UTF-8?q?ser=EB=A5=BC=20=EC=B0=BE=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt index 16f616a3..a51e0750 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt @@ -261,8 +261,8 @@ class AdminServiceImpl( val instructorName = row.getCell(2).stringCellValue val instructorEmail = row.getCell(3).stringCellValue - val instructor = userRepository.findByNameAndEmail(instructorName, instructorEmail) - ?: throw UserNotFoundException("존재하지 않는 강사입니다. info : [ name = $instructorName, email = $instructorEmail ]") + val instructor = userRepository.findByEmail(instructorEmail) + ?: throw UserNotFoundException("존재하지 않는 강사입니다. info : [ email = $instructorEmail ]") val type = row.getCell(4).stringCellValue val credit = row.getCell(5).numericCellValue.toInt() From 814281d1700afc231b17f22b7e159fc9a4bbe9a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=85=B8=ED=98=84=EC=A3=BC?= <106813267+ani2689@users.noreply.github.com> Date: Mon, 2 Sep 2024 00:33:15 +0900 Subject: [PATCH 09/13] =?UTF-8?q?update=20::=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EA=B3=B5=EB=B0=B1=20=EC=9E=85=EB=A0=A5=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 박주홍 <103554978+JuuuuHong@users.noreply.github.com> --- .../kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt index a51e0750..8c39e628 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt @@ -242,7 +242,7 @@ class AdminServiceImpl( WorkbookFactory.create(file.inputStream) } catch (e: IndexOutOfBoundsException) { throw InvalidCellTypeException("셀 서식을 텍스트로 변경해주세요.") - } catch (e: Exception) { + } catch (e: Exception) { throw InternalServerException("엑셀 파일 처리 중 문제가 발생했습니다. info : [ errorMessage = ${e.message} ]") } From d0a7cb23229c9c2b4d38fa53af5438cd9c031ff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=85=B8=ED=98=84=EC=A3=BC?= Date: Mon, 2 Sep 2024 00:54:51 +0900 Subject: [PATCH 10/13] =?UTF-8?q?update=20::=20=ED=95=9C=20=EB=B2=88?= =?UTF-8?q?=EB=A7=8C=20=ED=8C=8C=EC=9D=BC=20=EC=97=B4=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/admin/service/AdminServiceImpl.kt | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt index a51e0750..131c2b69 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt @@ -140,15 +140,13 @@ class AdminServiceImpl( @Transactional(rollbackFor = [Exception::class]) override fun uploadStudentListExcel(file: MultipartFile) { file.inputStream.use { - val workbook = try { - WorkbookFactory.create(file.inputStream) + val sheet = try { + WorkbookFactory.create(it) } catch (e: IndexOutOfBoundsException) { throw InvalidCellTypeException("전화번호 셀 서식을 텍스트로 바꿔주세요.") } catch (e: Exception) { throw InternalServerException("엑셀 파일 처리 중 문제가 발생했습니다. info : [ errorMessage = ${e.message} ]") - } - - val sheet = workbook.getSheetAt(0) + }.getSheetAt(0) sheet.forEachIndexed { index, row -> if (index == 0) @@ -186,13 +184,11 @@ class AdminServiceImpl( @Transactional(rollbackFor = [Exception::class]) override fun uploadClubListExcel(file: MultipartFile) { file.inputStream.use { - val workbook = try { - WorkbookFactory.create(file.inputStream) + val sheet = try { + WorkbookFactory.create(it) } catch (e: Exception) { throw InternalServerException("엑셀 파일 처리 중 문제가 발생했습니다. info : [ errorMessage = ${e.message} ]") - } - - val sheet = workbook.getSheetAt(0) + }.getSheetAt(0) sheet.forEachIndexed { index, row -> if (index == 0) @@ -238,15 +234,13 @@ class AdminServiceImpl( @Transactional(rollbackFor = [Exception::class]) override fun uploadLectureListExcel(file: MultipartFile) { file.inputStream.use { - val workbook = try { - WorkbookFactory.create(file.inputStream) + val sheet = try { + WorkbookFactory.create(it) } catch (e: IndexOutOfBoundsException) { throw InvalidCellTypeException("셀 서식을 텍스트로 변경해주세요.") } catch (e: Exception) { throw InternalServerException("엑셀 파일 처리 중 문제가 발생했습니다. info : [ errorMessage = ${e.message} ]") - } - - val sheet = workbook.getSheetAt(0) + }.getSheetAt(0) sheet.forEachIndexed { index, row -> if (index == 0 || index == 1) From 686528aa481a56bc88e32ea9f55349c57c076865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=85=B8=ED=98=84=EC=A3=BC?= Date: Mon, 2 Sep 2024 00:58:25 +0900 Subject: [PATCH 11/13] update :: semester companion object --- .../msg/domain/admin/service/AdminServiceImpl.kt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt index 131c2b69..427b5e87 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt @@ -263,10 +263,10 @@ class AdminServiceImpl( val semester = row.getCell(6).stringCellValue val lectureSemester = when(semester) { - "1학년 2학기" -> Semester.FIRST_YEAR_FALL_SEMESTER - "2학년 1학기" -> Semester.SECOND_YEAR_SPRING_SEMESTER - "2학년 2학기" -> Semester.SECOND_YEAR_FALL_SEMESTER - "3학년 1학기" -> Semester.THIRD_YEAR_SPRING_SEMESTER + FIRST_YEAR_FALL_SEMESTER -> Semester.FIRST_YEAR_FALL_SEMESTER + SECOND_YEAR_SPRING_SEMESTER -> Semester.SECOND_YEAR_SPRING_SEMESTER + SECOND_YEAR_FALL_SEMESTER -> Semester.SECOND_YEAR_FALL_SEMESTER + THIRD_YEAR_SPRING_SEMESTER -> Semester.THIRD_YEAR_SPRING_SEMESTER else -> Semester.NOT_APPLICABLE } @@ -356,6 +356,11 @@ class AdminServiceImpl( const val MEDICAL_HEALTHCARE = "의료 헬스케어" const val AI_CONVERGENCE = "AI 융복합" const val CULTURE = "문화산업" + + const val FIRST_YEAR_FALL_SEMESTER = "1학년 2학기" + const val SECOND_YEAR_SPRING_SEMESTER = "2학년 1학기" + const val SECOND_YEAR_FALL_SEMESTER = "2학년 2학기" + const val THIRD_YEAR_SPRING_SEMESTER = "3학년 1학기" } private fun String.toLocalDateTime(): LocalDateTime = From 65e062f2cb204a9679d7c7f7c054a585ba26c081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=85=B8=ED=98=84=EC=A3=BC?= Date: Mon, 2 Sep 2024 00:59:09 +0900 Subject: [PATCH 12/13] update :: essentialComplete uppercase check --- .../kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt index 427b5e87..9aabb840 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt @@ -279,7 +279,7 @@ class AdminServiceImpl( val endDate = row.getCell(12).stringCellValue.toLocalDateTime() val essentialComplete = row.getCell(16).stringCellValue - val lectureEssentialComplete = essentialComplete == "O" + val lectureEssentialComplete = essentialComplete.uppercase() == "O" val lecture = Lecture( id = UUID(0, 0), From 4eaba1d79f8c194b338de8f7c0383f44d93d6000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=85=B8=ED=98=84=EC=A3=BC?= Date: Mon, 2 Sep 2024 01:30:05 +0900 Subject: [PATCH 13/13] =?UTF-8?q?update=20::=20workbook=20=EB=B0=96?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B9=BC=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/admin/service/AdminServiceImpl.kt | 276 +++++++++--------- 1 file changed, 141 insertions(+), 135 deletions(-) diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt index 95e4ff4c..e67bcf9d 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/admin/service/AdminServiceImpl.kt @@ -139,41 +139,43 @@ class AdminServiceImpl( */ @Transactional(rollbackFor = [Exception::class]) override fun uploadStudentListExcel(file: MultipartFile) { - file.inputStream.use { - val sheet = try { + val workbook = file.inputStream.use { + try { WorkbookFactory.create(it) } catch (e: IndexOutOfBoundsException) { throw InvalidCellTypeException("전화번호 셀 서식을 텍스트로 바꿔주세요.") } catch (e: Exception) { throw InternalServerException("엑셀 파일 처리 중 문제가 발생했습니다. info : [ errorMessage = ${e.message} ]") - }.getSheetAt(0) + } + } - sheet.forEachIndexed { index, row -> - if (index == 0) - return@forEachIndexed + val sheet = workbook.getSheetAt(0) - if (row.getCell(0).stringCellValue == "") - return + sheet.forEachIndexed { index, row -> + if (index == 0) + return@forEachIndexed - val email = row.getCell(0).stringCellValue - val name = row.getCell(1).stringCellValue - val phoneNumber = row.getCell(2).stringCellValue - val password = row.getCell(3).stringCellValue - val clubName = row.getCell(4).stringCellValue - val grade = row.getCell(5).numericCellValue.toInt() - val classRoom = row.getCell(6).numericCellValue.toInt() - val number = row.getCell(7).numericCellValue.toInt() - val admissionNumber = row.getCell(8).numericCellValue.toInt() - val subscriptionGrade = row.getCell(9).numericCellValue.toInt() + if (row.getCell(0).stringCellValue == "") + return - validateExcelStudentData(email, phoneNumber, password) + val email = row.getCell(0).stringCellValue + val name = row.getCell(1).stringCellValue + val phoneNumber = row.getCell(2).stringCellValue + val password = row.getCell(3).stringCellValue + val clubName = row.getCell(4).stringCellValue + val grade = row.getCell(5).numericCellValue.toInt() + val classRoom = row.getCell(6).numericCellValue.toInt() + val number = row.getCell(7).numericCellValue.toInt() + val admissionNumber = row.getCell(8).numericCellValue.toInt() + val subscriptionGrade = row.getCell(9).numericCellValue.toInt() - val user = userUtil.createUser(email, name, phoneNumber, password, Authority.ROLE_STUDENT) + validateExcelStudentData(email, phoneNumber, password) - val club = clubRepository findByName clubName + val user = userUtil.createUser(email, name, phoneNumber, password, Authority.ROLE_STUDENT) - studentUtil.createStudent(user, club, grade, classRoom, number, admissionNumber, subscriptionGrade) - } + val club = clubRepository findByName clubName + + studentUtil.createStudent(user, club, grade, classRoom, number, admissionNumber, subscriptionGrade) } } @@ -183,47 +185,49 @@ class AdminServiceImpl( */ @Transactional(rollbackFor = [Exception::class]) override fun uploadClubListExcel(file: MultipartFile) { - file.inputStream.use { - val sheet = try { + val workbook = file.inputStream.use { + try { WorkbookFactory.create(it) } catch (e: Exception) { throw InternalServerException("엑셀 파일 처리 중 문제가 발생했습니다. info : [ errorMessage = ${e.message} ]") - }.getSheetAt(0) + } + } - sheet.forEachIndexed { index, row -> - if (index == 0) - return@forEachIndexed + val sheet = workbook.getSheetAt(0) - if (row.getCell(0).stringCellValue == "") - return + sheet.forEachIndexed { index, row -> + if (index == 0) + return@forEachIndexed - val schoolName = row.getCell(0).stringCellValue - val clubName = row.getCell(1).stringCellValue - val field = row.getCell(2).stringCellValue + if (row.getCell(0).stringCellValue == "") + return - val school = schoolRepository.findByName(schoolName) - ?: throw SchoolNotFoundException("존재하지 않는 학교입니다. info : [ schoolName = $schoolName ]") + val schoolName = row.getCell(0).stringCellValue + val clubName = row.getCell(1).stringCellValue + val field = row.getCell(2).stringCellValue - if (clubRepository.existsByName(clubName)) { - throw AlreadyExistClubException("이미 존재하는 동아리입니다. info : [ clubName = $clubName ]") - } + val school = schoolRepository.findByName(schoolName) + ?: throw SchoolNotFoundException("존재하지 않는 학교입니다. info : [ schoolName = $schoolName ]") - val clubField = when (field) { - FUTURISTIC_TRANSPORTATION_EQUIPMENT -> Field.FUTURISTIC_TRANSPORTATION_EQUIPMENT - ENERGY -> Field.ENERGY - MEDICAL_HEALTHCARE -> Field.MEDICAL_HEALTHCARE - AI_CONVERGENCE -> Field.AI_CONVERGENCE - CULTURE -> Field.CULTURE - else -> throw InvalidFieldException("유효하지 않은 동아리 분야입니다. info : [ clubField = $field ]") - } + if (clubRepository.existsByName(clubName)) { + throw AlreadyExistClubException("이미 존재하는 동아리입니다. info : [ clubName = $clubName ]") + } - val club = Club( - school = school, - name = clubName, - field = clubField - ) - clubRepository.save(club) + val clubField = when (field) { + FUTURISTIC_TRANSPORTATION_EQUIPMENT -> Field.FUTURISTIC_TRANSPORTATION_EQUIPMENT + ENERGY -> Field.ENERGY + MEDICAL_HEALTHCARE -> Field.MEDICAL_HEALTHCARE + AI_CONVERGENCE -> Field.AI_CONVERGENCE + CULTURE -> Field.CULTURE + else -> throw InvalidFieldException("유효하지 않은 동아리 분야입니다. info : [ clubField = $field ]") } + + val club = Club( + school = school, + name = clubName, + field = clubField + ) + clubRepository.save(club) } } @@ -233,102 +237,104 @@ class AdminServiceImpl( */ @Transactional(rollbackFor = [Exception::class]) override fun uploadLectureListExcel(file: MultipartFile) { - file.inputStream.use { - val sheet = try { + val workbook = file.inputStream.use { + try { WorkbookFactory.create(it) } catch (e: IndexOutOfBoundsException) { throw InvalidCellTypeException("셀 서식을 텍스트로 변경해주세요.") } catch (e: Exception) { throw InternalServerException("엑셀 파일 처리 중 문제가 발생했습니다. info : [ errorMessage = ${e.message} ]") - }.getSheetAt(0) + } + } - sheet.forEachIndexed { index, row -> - if (index == 0 || index == 1) - return@forEachIndexed + val sheet = workbook.getSheetAt(0) - if (row.getCell(0) == null || row.getCell(0).stringCellValue == "") - return + sheet.forEachIndexed { index, row -> + if (index == 0 || index == 1) + return@forEachIndexed - val name = row.getCell(0).stringCellValue - val content = row.getCell(1).stringCellValue + if (row.getCell(0) == null || row.getCell(0).stringCellValue == "") + return - val instructorName = row.getCell(2).stringCellValue - val instructorEmail = row.getCell(3).stringCellValue + val name = row.getCell(0).stringCellValue + val content = row.getCell(1).stringCellValue - val instructor = userRepository.findByEmail(instructorEmail) - ?: throw UserNotFoundException("존재하지 않는 강사입니다. info : [ email = $instructorEmail ]") + val instructorName = row.getCell(2).stringCellValue + val instructorEmail = row.getCell(3).stringCellValue - val type = row.getCell(4).stringCellValue - val credit = row.getCell(5).numericCellValue.toInt() - val semester = row.getCell(6).stringCellValue + val instructor = userRepository.findByEmail(instructorEmail) + ?: throw UserNotFoundException("존재하지 않는 강사입니다. info : [ email = $instructorEmail ]") - val lectureSemester = when(semester) { - FIRST_YEAR_FALL_SEMESTER -> Semester.FIRST_YEAR_FALL_SEMESTER - SECOND_YEAR_SPRING_SEMESTER -> Semester.SECOND_YEAR_SPRING_SEMESTER - SECOND_YEAR_FALL_SEMESTER -> Semester.SECOND_YEAR_FALL_SEMESTER - THIRD_YEAR_SPRING_SEMESTER -> Semester.THIRD_YEAR_SPRING_SEMESTER - else -> Semester.NOT_APPLICABLE - } + val type = row.getCell(4).stringCellValue + val credit = row.getCell(5).numericCellValue.toInt() + val semester = row.getCell(6).stringCellValue - val division = row.getCell(7).stringCellValue - val line = row.getCell(8).stringCellValue - val department = row.getCell(9).stringCellValue - val maxRegisteredUser = row.getCell(10).numericCellValue.toInt() - - val startDate = row.getCell(11).stringCellValue.toLocalDateTime() - val endDate = row.getCell(12).stringCellValue.toLocalDateTime() - val essentialComplete = row.getCell(16).stringCellValue - - val lectureEssentialComplete = essentialComplete.uppercase() == "O" - - val lecture = Lecture( - id = UUID(0, 0), - name = name, - content = content, - instructor = instructorName, - user = instructor, - lectureType = type, - credit = credit, - semester = lectureSemester, - division = division, - line = line, - department = department, - maxRegisteredUser = maxRegisteredUser, - startDate = startDate, - endDate = endDate, - essentialComplete = lectureEssentialComplete - ) - - lectureRepository.save(lecture) - - val lectureDates = row.getCell(13).stringCellValue - .split(",").map { - val timeZone = it.split(" ", "~") - LectureDate( - id = UUID(0, 0), - lecture = lecture, - completeDate = timeZone[0].toLocalDate(), - startTime = timeZone[1].toLocalTime(), - endTime = timeZone[2].toLocalTime() - ) - } - - lectureDateRepository.saveAll(lectureDates) - - val address = row.getCell(14).stringCellValue - val addressDetails = row.getCell(15).stringCellValue - - val coordinate = kakaoUtil.getCoordinate(address) - val lectureLocation = LectureLocation( - lectureId = lecture.id, - x = coordinate.first, - y = coordinate.second, - address = address, - details = addressDetails - ) - - lectureLocationRepository.save(lectureLocation) + val lectureSemester = when(semester) { + FIRST_YEAR_FALL_SEMESTER -> Semester.FIRST_YEAR_FALL_SEMESTER + SECOND_YEAR_SPRING_SEMESTER -> Semester.SECOND_YEAR_SPRING_SEMESTER + SECOND_YEAR_FALL_SEMESTER -> Semester.SECOND_YEAR_FALL_SEMESTER + THIRD_YEAR_SPRING_SEMESTER -> Semester.THIRD_YEAR_SPRING_SEMESTER + else -> Semester.NOT_APPLICABLE } + + val division = row.getCell(7).stringCellValue + val line = row.getCell(8).stringCellValue + val department = row.getCell(9).stringCellValue + val maxRegisteredUser = row.getCell(10).numericCellValue.toInt() + + val startDate = row.getCell(11).stringCellValue.toLocalDateTime() + val endDate = row.getCell(12).stringCellValue.toLocalDateTime() + val essentialComplete = row.getCell(16).stringCellValue + + val lectureEssentialComplete = essentialComplete.uppercase() == "O" + + val lecture = Lecture( + id = UUID(0, 0), + name = name, + content = content, + instructor = instructorName, + user = instructor, + lectureType = type, + credit = credit, + semester = lectureSemester, + division = division, + line = line, + department = department, + maxRegisteredUser = maxRegisteredUser, + startDate = startDate, + endDate = endDate, + essentialComplete = lectureEssentialComplete + ) + + lectureRepository.save(lecture) + + val lectureDates = row.getCell(13).stringCellValue + .split(",").map { + val timeZone = it.split(" ", "~") + LectureDate( + id = UUID(0, 0), + lecture = lecture, + completeDate = timeZone[0].toLocalDate(), + startTime = timeZone[1].toLocalTime(), + endTime = timeZone[2].toLocalTime() + ) + } + + lectureDateRepository.saveAll(lectureDates) + + val address = row.getCell(14).stringCellValue + val addressDetails = row.getCell(15).stringCellValue + + val coordinate = kakaoUtil.getCoordinate(address) + val lectureLocation = LectureLocation( + lectureId = lecture.id, + x = coordinate.first, + y = coordinate.second, + address = address, + details = addressDetails + ) + + lectureLocationRepository.save(lectureLocation) } }