Skip to content

Commit

Permalink
Merge pull request #571 from School-of-Company/568-add/club-excel
Browse files Browse the repository at this point in the history
568 동아리 일괄 삽입 기능 개발
  • Loading branch information
JuuuuHong authored Aug 29, 2024
2 parents b13c026 + 7762db6 commit c731c81
Show file tree
Hide file tree
Showing 10 changed files with 100 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package team.msg.domain.admin.presentation

import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartFile
Expand Down Expand Up @@ -40,9 +41,15 @@ class AdminController(
return ResponseEntity.noContent().build()
}

@PostMapping("/excel")
@PostMapping("/student/excel")
fun uploadStudentListExcel(@RequestPart file: MultipartFile): ResponseEntity<Unit> {
adminService.uploadStudentListExcel(file)
return ResponseEntity.ok().build()
return ResponseEntity.status(HttpStatus.CREATED).build()
}

@PostMapping("/club/excel")
fun uploadClubListExcel(@RequestPart file: MultipartFile): ResponseEntity<Unit> {
adminService.uploadClubListExcel(file)
return ResponseEntity.status(HttpStatus.CREATED).build()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ interface AdminService {
fun rejectUsers(userIds: List<UUID>)
fun forceWithdraw(userIds: List<UUID>)
fun uploadStudentListExcel(file: MultipartFile)
fun uploadClubListExcel(file: MultipartFile)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@ import org.springframework.stereotype.Service
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.StudentUtil
import team.msg.common.util.UserUtil
import team.msg.domain.admin.exception.InvalidCellTypeException
import team.msg.domain.admin.presentation.data.request.QueryUsersRequest
import team.msg.domain.club.exception.AlreadyExistClubException
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.school.exception.SchoolNotFoundException
import team.msg.domain.school.repository.SchoolRepository
import team.msg.domain.student.repository.StudentRepository
import team.msg.domain.user.enums.Authority
import team.msg.domain.user.exception.InvalidEmailException
Expand All @@ -31,7 +36,8 @@ class AdminServiceImpl(
private val userUtil: UserUtil,
private val studentUtil: StudentUtil,
private val clubRepository: ClubRepository,
private val studentRepository: StudentRepository
private val studentRepository: StudentRepository,
private val schoolRepository: SchoolRepository
) : AdminService {

/**
Expand Down Expand Up @@ -122,7 +128,7 @@ class AdminServiceImpl(
} catch (e: IndexOutOfBoundsException) {
throw InvalidCellTypeException("전화번호 셀 서식을 텍스트로 바꿔주세요.")
} catch (e: Exception) {
throw InternalServerException("엑셀 파일 처리 중 문제가 발생했습니다. info : [ errorMessage = ${e.message}")
throw InternalServerException("엑셀 파일 처리 중 문제가 발생했습니다. info : [ errorMessage = ${e.message} ]")
}

val sheet = workbook.getSheetAt(0)
Expand Down Expand Up @@ -156,6 +162,58 @@ class AdminServiceImpl(
}
}

/**
* 동아리 리스트 엑셀을 업로드 하는 비지니스 로직입니다
* @param 동아리 리스트 엑셀 업로드를 위한 MultipartFile
*/
@Transactional(rollbackFor = [Exception::class])
override fun uploadClubListExcel(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)
return@forEachIndexed

if (row.getCell(0).stringCellValue == "")
return

val schoolName = row.getCell(0).stringCellValue
val clubName = row.getCell(1).stringCellValue
val field = row.getCell(2).stringCellValue

val school = schoolRepository.findByName(schoolName)
?: throw SchoolNotFoundException("존재하지 않는 학교입니다. info : [ schoolName = $schoolName ]")

if (clubRepository.existsByName(clubName)) {
throw AlreadyExistClubException("이미 존재하는 동아리입니다. info : [ clubName = $clubName ]")
}

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)
}
}
}

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))
Expand All @@ -173,4 +231,12 @@ class AdminServiceImpl(
private infix fun ClubRepository.findByName(clubName: String): Club =
this.findByName(clubName) ?: throw ClubNotFoundException("존재하지 않는 동아리입니다. info : [ clubName = $clubName ]")


companion object {
const val FUTURISTIC_TRANSPORTATION_EQUIPMENT = "미래형 운송기기"
const val ENERGY = "에너지 산업"
const val MEDICAL_HEALTHCARE = "의료 헬스케어"
const val AI_CONVERGENCE = "AI 융복합"
const val CULTURE = "문화산업"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class CertificationServiceImpl(
val student = studentRepository findStudentById studentId

if (student.club != club && club != null)
throw ForbiddenCertificationException("자격증을 조회할 권한이 없습니다. info : [ club = $club ]")
throw ForbiddenCertificationException("자격증을 조회할 권한이 없습니다. info : [ club = ${club.name} ]")

val certifications = certificationRepository findAllByStudentIdOrderByAcquisitionDateDesc studentId

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package team.msg.domain.club.exception

import team.msg.domain.club.exception.constant.ClubErrorCode
import team.msg.global.error.exception.BitgouelException

class InvalidFieldException(
message: String
) : BitgouelException(message, ClubErrorCode.INVALID_FIELD.status)
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ enum class ClubErrorCode(
val status: Int
) {
NOT_EMPTY_CLUB(400),
INVALID_FIELD(400),
CLUB_NOT_FOUND(404),
ALREADY_EXIST_CLUB(409)
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class InquiryServiceImpl(
throw ForbiddenCommandInquiryException("문의사항을 삭제할 권한이 없습니다. info : [ userId = ${currentUser.id}, inquiryId = $id ]")

if(inquiry.answerStatus == AnswerStatus.ANSWERED) {
val inquiryAnswer = inquiryAnswerRepository findByInquiryId id
val inquiryAnswer = inquiryAnswerRepository findByInquiryId id
inquiryAnswerRepository.delete(inquiryAnswer)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ class SecurityConfig(
.mvcMatchers(HttpMethod.DELETE, "/admin/reject").hasRole(ADMIN)
.mvcMatchers(HttpMethod.GET, "/admin/{user_id}").hasRole(ADMIN)
.mvcMatchers(HttpMethod.DELETE, "/admin/withdraw").hasRole(ADMIN)
.mvcMatchers(HttpMethod.POST, "/admin/excel").hasRole(ADMIN)
.mvcMatchers(HttpMethod.POST, "/admin/student/excel").hasRole(ADMIN)
.mvcMatchers(HttpMethod.POST, "/admin/club/excel").hasRole(ADMIN)

// inquiry
.mvcMatchers(HttpMethod.POST, "/inquiry").authenticated()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@ import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.IsolationMode
import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.shouldBe
import io.mockk.*
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.verify
import team.msg.common.enums.ApproveStatus
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.school.repository.SchoolRepository
import team.msg.domain.student.model.Student
import team.msg.domain.student.repository.StudentRepository
import team.msg.domain.user.enums.Authority
Expand All @@ -30,12 +35,14 @@ class AdminServiceImplTest : BehaviorSpec({
val studentUtil = mockk<StudentUtil>()
val clubRepository = mockk<ClubRepository>()
val studentRepository = mockk<StudentRepository>()
val schoolRepository = mockk<SchoolRepository>()
val adminServiceImpl = AdminServiceImpl(
userRepository = userRepository,
userUtil = userUtil,
studentUtil = studentUtil,
clubRepository = clubRepository,
studentRepository = studentRepository
studentRepository = studentRepository,
schoolRepository = schoolRepository
)

// queryUsers 테스트 코드
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import javax.persistence.Enumerated
import javax.persistence.FetchType
import javax.persistence.JoinColumn
import javax.persistence.ManyToOne
import org.hibernate.annotations.ColumnDefault
import team.msg.common.entity.BaseUUIDEntity
import team.msg.domain.lecture.enums.LectureStatus
import team.msg.domain.lecture.enums.Semester
Expand Down

0 comments on commit c731c81

Please sign in to comment.