diff --git a/src/main/kotlin/com/wafflestudio/csereal/core/about/api/req/CreateFacReq.kt b/src/main/kotlin/com/wafflestudio/csereal/core/about/api/req/CreateFacReq.kt index d9ead5e0..174ff3dc 100644 --- a/src/main/kotlin/com/wafflestudio/csereal/core/about/api/req/CreateFacReq.kt +++ b/src/main/kotlin/com/wafflestudio/csereal/core/about/api/req/CreateFacReq.kt @@ -1,12 +1,8 @@ package com.wafflestudio.csereal.core.about.api.req -data class CreateFacReq( - val ko: FacDto, - val en: FacDto -) +import com.wafflestudio.csereal.core.about.dto.FacReq -data class FacDto( - val name: String, - val description: String, - val locations: MutableList +data class CreateFacReq( + val ko: FacReq, + val en: FacReq ) diff --git a/src/main/kotlin/com/wafflestudio/csereal/core/about/api/req/CreateStatReq.kt b/src/main/kotlin/com/wafflestudio/csereal/core/about/api/req/CreateStatReq.kt new file mode 100644 index 00000000..d3413791 --- /dev/null +++ b/src/main/kotlin/com/wafflestudio/csereal/core/about/api/req/CreateStatReq.kt @@ -0,0 +1,18 @@ +package com.wafflestudio.csereal.core.about.api.req + +data class CreateStatReq( + val year: Int, + val statList: List +) + +data class StatDto( + val career: Career, + val bachelor: Int, + val master: Int, + val doctor: Int +) + +enum class Career(val krName: String) { + SAMSUNG("삼성"), LG("LG"), LARGE("기타 대기업"), + SMALL("중소기업"), GRADUATE("진학"), OTHER("기타") +} diff --git a/src/main/kotlin/com/wafflestudio/csereal/core/about/api/req/UpdateFacReq.kt b/src/main/kotlin/com/wafflestudio/csereal/core/about/api/req/UpdateFacReq.kt index 045c5340..808c5ec4 100644 --- a/src/main/kotlin/com/wafflestudio/csereal/core/about/api/req/UpdateFacReq.kt +++ b/src/main/kotlin/com/wafflestudio/csereal/core/about/api/req/UpdateFacReq.kt @@ -1,7 +1,9 @@ package com.wafflestudio.csereal.core.about.api.req +import com.wafflestudio.csereal.core.about.dto.FacReq + data class UpdateFacReq( - val ko: FacDto, - val en: FacDto, + val ko: FacReq, + val en: FacReq, val removeImage: Boolean ) diff --git a/src/main/kotlin/com/wafflestudio/csereal/core/about/api/v1/AboutController.kt b/src/main/kotlin/com/wafflestudio/csereal/core/about/api/v1/AboutController.kt index 756054ba..9949df3c 100644 --- a/src/main/kotlin/com/wafflestudio/csereal/core/about/api/v1/AboutController.kt +++ b/src/main/kotlin/com/wafflestudio/csereal/core/about/api/v1/AboutController.kt @@ -38,6 +38,7 @@ class AboutController( return ResponseEntity.ok(aboutService.readAllClubs(language)) } + @Deprecated("Use V2 API") @GetMapping("/facilities") fun readAllFacilities( @RequestParam(required = false, defaultValue = "ko") language: String @@ -45,6 +46,7 @@ class AboutController( return ResponseEntity.ok(aboutService.readAllFacilities(language)) } + @Deprecated("Use V2 API") @GetMapping("/directions") fun readAllDirections( @RequestParam(required = false, defaultValue = "ko") language: String diff --git a/src/main/kotlin/com/wafflestudio/csereal/core/about/api/v2/AboutController.kt b/src/main/kotlin/com/wafflestudio/csereal/core/about/api/v2/AboutController.kt index 16ffb2ed..50ba1cbd 100644 --- a/src/main/kotlin/com/wafflestudio/csereal/core/about/api/v2/AboutController.kt +++ b/src/main/kotlin/com/wafflestudio/csereal/core/about/api/v2/AboutController.kt @@ -47,6 +47,9 @@ class AboutController( fun createFacilities(@RequestPart request: CreateFacReq, @RequestPart mainImage: MultipartFile?) = aboutService.createFacilities(request, mainImage) + @GetMapping("/facilities") + fun readAllGroupedFacilities() = aboutService.readAllGroupedFacilities() + @AuthenticatedStaff @PutMapping("/facilities/{id}") fun updateFacility( @@ -59,11 +62,22 @@ class AboutController( @DeleteMapping("/facilities/{id}") fun deleteFacility(@PathVariable id: Long) = aboutService.deleteFacility(id) + @GetMapping("/directions") + fun readAllGroupedDirections() = aboutService.readAllGroupedDirections() + @AuthenticatedStaff @PutMapping("/directions/{id}") fun updateDirection(@PathVariable id: Long, @RequestBody request: UpdateDescriptionReq) = aboutService.updateDirection(id, request) + @AuthenticatedStaff + @PostMapping("/future-careers/stats") + fun createStats(@RequestBody request: CreateStatReq) = aboutService.createFutureCareersStat(request) + + @AuthenticatedStaff + @PutMapping("/future-careers/stats") + fun updateStats(@RequestBody request: CreateStatReq) = aboutService.updateFutureCareersStat(request) + @AuthenticatedStaff @PutMapping("/future-careers") fun updateFutureCareersPage(@RequestBody request: UpdateDescriptionReq) = diff --git a/src/main/kotlin/com/wafflestudio/csereal/core/about/database/AboutLanguageRepository.kt b/src/main/kotlin/com/wafflestudio/csereal/core/about/database/AboutLanguageRepository.kt index f4c06ed9..478173a2 100644 --- a/src/main/kotlin/com/wafflestudio/csereal/core/about/database/AboutLanguageRepository.kt +++ b/src/main/kotlin/com/wafflestudio/csereal/core/about/database/AboutLanguageRepository.kt @@ -5,4 +5,5 @@ import org.springframework.data.jpa.repository.JpaRepository interface AboutLanguageRepository : JpaRepository { fun findByKoAbout(koAboutEntity: AboutEntity): AboutLanguageEntity? fun findByEnAbout(enAboutEntity: AboutEntity): AboutLanguageEntity? + fun findAllByKoAboutPostType(postType: AboutPostType): List } diff --git a/src/main/kotlin/com/wafflestudio/csereal/core/about/dto/FacReq.kt b/src/main/kotlin/com/wafflestudio/csereal/core/about/dto/FacReq.kt new file mode 100644 index 00000000..224e2107 --- /dev/null +++ b/src/main/kotlin/com/wafflestudio/csereal/core/about/dto/FacReq.kt @@ -0,0 +1,34 @@ +package com.wafflestudio.csereal.core.about.dto + +import com.wafflestudio.csereal.core.about.database.AboutEntity + +data class FacReq( + val name: String, + val description: String, + val locations: MutableList +) + +data class FacDto( + val id: Long, + val name: String, + val description: String, + val locations: MutableList, + val imageURL: String? +) { + companion object { + fun of(aboutEntity: AboutEntity, imageURL: String?): FacDto { + return FacDto( + id = aboutEntity.id, + name = aboutEntity.name!!, + description = aboutEntity.description, + locations = aboutEntity.locations, + imageURL = imageURL + ) + } + } +} + +data class GroupedFacDto( + val ko: FacDto, + val en: FacDto +) diff --git a/src/main/kotlin/com/wafflestudio/csereal/core/about/dto/GroupedDirectionDto.kt b/src/main/kotlin/com/wafflestudio/csereal/core/about/dto/GroupedDirectionDto.kt new file mode 100644 index 00000000..69e3dbcd --- /dev/null +++ b/src/main/kotlin/com/wafflestudio/csereal/core/about/dto/GroupedDirectionDto.kt @@ -0,0 +1,24 @@ +package com.wafflestudio.csereal.core.about.dto + +import com.wafflestudio.csereal.core.about.database.AboutEntity + +data class GroupedDirectionDto( + val ko: DirDto, + val en: DirDto +) + +data class DirDto( + val id: Long, + val name: String, + val description: String +) { + companion object { + fun of(aboutEntity: AboutEntity): DirDto { + return DirDto( + id = aboutEntity.id, + name = aboutEntity.name!!, + description = aboutEntity.description + ) + } + } +} diff --git a/src/main/kotlin/com/wafflestudio/csereal/core/about/service/AboutService.kt b/src/main/kotlin/com/wafflestudio/csereal/core/about/service/AboutService.kt index dd660988..ed96b13f 100644 --- a/src/main/kotlin/com/wafflestudio/csereal/core/about/service/AboutService.kt +++ b/src/main/kotlin/com/wafflestudio/csereal/core/about/service/AboutService.kt @@ -36,9 +36,13 @@ interface AboutService { fun updateFacility(id: Long, request: UpdateFacReq, newMainImage: MultipartFile?) fun deleteFacility(id: Long) fun readAllFacilities(language: String): List + fun readAllGroupedFacilities(): List fun readAllDirections(language: String): List + fun readAllGroupedDirections(): List fun updateDirection(id: Long, request: UpdateDescriptionReq) fun updateFutureCareersPage(request: UpdateDescriptionReq) + fun createFutureCareersStat(request: CreateStatReq) + fun updateFutureCareersStat(request: CreateStatReq) fun readFutureCareers(language: String): FutureCareersPage fun createCompany(request: CreateCompanyReq) fun updateCompany(id: Long, request: CreateCompanyReq) @@ -214,7 +218,7 @@ class AboutServiceImpl( @Transactional(readOnly = true) override fun readAllGroupedClubs(): List { - val clubs = aboutLanguageRepository.findAll().filter { it.koAbout.postType == AboutPostType.STUDENT_CLUBS } + val clubs = aboutLanguageRepository.findAllByKoAboutPostType(AboutPostType.STUDENT_CLUBS) .sortedBy { it.koAbout.name } return clubs.map { val imageURL = mainImageService.createImageURL(it.koAbout.mainImage) @@ -285,10 +289,10 @@ class AboutServiceImpl( } } - private fun updateFacility(facility: AboutEntity, facDto: FacDto) { - facility.name = facDto.name - facility.description = facDto.description - facility.locations = facDto.locations + private fun updateFacility(facility: AboutEntity, facReq: FacReq) { + facility.name = facReq.name + facility.description = facReq.description + facility.locations = facReq.locations } @Transactional @@ -326,6 +330,17 @@ class AboutServiceImpl( return facilities } + @Transactional(readOnly = true) + override fun readAllGroupedFacilities(): List { + val facilities = + aboutLanguageRepository.findAllByKoAboutPostType(AboutPostType.FACILITIES).sortedBy { it.koAbout.name } + return facilities.map { + val koImageURL = mainImageService.createImageURL(it.koAbout.mainImage) + val enImageURL = mainImageService.createImageURL(it.enAbout.mainImage) + GroupedFacDto(ko = FacDto.of(it.koAbout, koImageURL), en = FacDto.of(it.enAbout, enImageURL)) + } + } + @Transactional(readOnly = true) override fun readAllDirections(language: String): List { val languageType = LanguageType.makeStringToLanguageType(language) @@ -342,6 +357,15 @@ class AboutServiceImpl( return directions } + @Transactional(readOnly = true) + override fun readAllGroupedDirections(): List { + val directions = + aboutLanguageRepository.findAllByKoAboutPostType(AboutPostType.DIRECTIONS).sortedBy { it.koAbout.name } + return directions.map { + GroupedDirectionDto(ko = DirDto.of(it.koAbout), en = DirDto.of(it.enAbout)) + } + } + @Transactional override fun updateDirection(id: Long, request: UpdateDescriptionReq) { val direction = aboutRepository.findByIdOrNull(id) ?: throw CserealException.Csereal404("direction not found") @@ -379,6 +403,37 @@ class AboutServiceImpl( en.syncSearchContent() } + @Transactional + override fun createFutureCareersStat(request: CreateStatReq) { + if (statRepository.findAllByYear(request.year).isNotEmpty()) { + throw CserealException.Csereal409("year already exist") + } + if (request.statList.size != 6) { + throw CserealException.Csereal400("모든 row data 필요") + } + for (stat in request.statList) { + statRepository.save(StatEntity(request.year, Degree.BACHELOR, stat.career.krName, stat.bachelor)) + statRepository.save(StatEntity(request.year, Degree.MASTER, stat.career.krName, stat.master)) + statRepository.save(StatEntity(request.year, Degree.DOCTOR, stat.career.krName, stat.doctor)) + } + } + + @Transactional + override fun updateFutureCareersStat(request: CreateStatReq) { + val stats = statRepository.findAllByYear(request.year) + val statsMap = stats.associateBy { it.name to it.degree } + + request.statList.forEach { update -> + listOf( + Degree.BACHELOR to update.bachelor, + Degree.MASTER to update.master, + Degree.DOCTOR to update.doctor + ).forEach { (degree, count) -> + statsMap[update.career.krName to degree]?.count = count + } + } + } + @Transactional override fun readFutureCareers(language: String): FutureCareersPage { val languageType = LanguageType.makeStringToLanguageType(language) diff --git a/src/main/kotlin/com/wafflestudio/csereal/core/research/dto/LabDto.kt b/src/main/kotlin/com/wafflestudio/csereal/core/research/dto/LabDto.kt index b0142310..f1477377 100644 --- a/src/main/kotlin/com/wafflestudio/csereal/core/research/dto/LabDto.kt +++ b/src/main/kotlin/com/wafflestudio/csereal/core/research/dto/LabDto.kt @@ -2,6 +2,8 @@ package com.wafflestudio.csereal.core.research.dto import com.wafflestudio.csereal.common.enums.LanguageType import com.wafflestudio.csereal.core.research.database.LabEntity +import com.wafflestudio.csereal.core.research.database.ResearchEntity +import com.wafflestudio.csereal.core.research.type.ResearchType import com.wafflestudio.csereal.core.resource.attachment.dto.AttachmentResponse data class LabDto( @@ -14,7 +16,7 @@ data class LabDto( val acronym: String?, val pdf: AttachmentResponse?, val youtube: String?, - val group: String?, + val group: LabGroupDto?, val description: String?, val websiteURL: String? ) { @@ -30,10 +32,25 @@ data class LabDto( acronym = this.acronym, pdf = pdf, youtube = this.youtube, - group = this.research?.name, + group = this.research?.let { LabGroupDto.of(it) }, description = this.description, websiteURL = this.websiteURL ) } } } + +data class LabGroupDto( + val id: Long, + val name: String +) { + companion object { + fun of(entity: ResearchEntity): LabGroupDto { + if (entity.postType != ResearchType.GROUPS) { + throw IllegalArgumentException("ResearchEntity is not a group") + } + + return LabGroupDto(entity.id, entity.name) + } + } +} diff --git a/src/main/kotlin/com/wafflestudio/csereal/core/reservation/database/ReservationRepository.kt b/src/main/kotlin/com/wafflestudio/csereal/core/reservation/database/ReservationRepository.kt index 2abad21c..b5ab44c1 100644 --- a/src/main/kotlin/com/wafflestudio/csereal/core/reservation/database/ReservationRepository.kt +++ b/src/main/kotlin/com/wafflestudio/csereal/core/reservation/database/ReservationRepository.kt @@ -1,15 +1,12 @@ package com.wafflestudio.csereal.core.reservation.database -import jakarta.persistence.LockModeType import org.springframework.data.jpa.repository.JpaRepository -import org.springframework.data.jpa.repository.Lock import org.springframework.data.jpa.repository.Query import java.time.LocalDateTime import java.util.UUID interface ReservationRepository : JpaRepository { - @Lock(LockModeType.PESSIMISTIC_WRITE) @Query( "SELECT r FROM reservation r " + "WHERE r.room.id = :roomId " + diff --git a/src/main/kotlin/com/wafflestudio/csereal/core/reservation/database/RoomRepository.kt b/src/main/kotlin/com/wafflestudio/csereal/core/reservation/database/RoomRepository.kt index f375afaf..898a3a6b 100644 --- a/src/main/kotlin/com/wafflestudio/csereal/core/reservation/database/RoomRepository.kt +++ b/src/main/kotlin/com/wafflestudio/csereal/core/reservation/database/RoomRepository.kt @@ -1,5 +1,10 @@ package com.wafflestudio.csereal.core.reservation.database +import jakarta.persistence.LockModeType import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Lock -interface RoomRepository : JpaRepository +interface RoomRepository : JpaRepository { + @Lock(LockModeType.PESSIMISTIC_WRITE) + fun findRoomById(id: Long): RoomEntity? +} diff --git a/src/main/kotlin/com/wafflestudio/csereal/core/reservation/service/ReservationService.kt b/src/main/kotlin/com/wafflestudio/csereal/core/reservation/service/ReservationService.kt index cf0235b0..3f83a9f4 100644 --- a/src/main/kotlin/com/wafflestudio/csereal/core/reservation/service/ReservationService.kt +++ b/src/main/kotlin/com/wafflestudio/csereal/core/reservation/service/ReservationService.kt @@ -47,7 +47,7 @@ class ReservationServiceImpl( } val room = - roomRepository.findByIdOrNull(reserveRequest.roomId) ?: throw CserealException.Csereal404("Room Not Found") + roomRepository.findRoomById(reserveRequest.roomId) ?: throw CserealException.Csereal404("Room Not Found") if (room.type == RoomType.LECTURE && user.role != Role.ROLE_STAFF) { throw CserealException.Csereal403("교수회의실 예약 행정실 문의 바람")