Skip to content
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

fix: notice 패키지 프론트에 맞게 협의 #54

Merged
merged 18 commits into from
Sep 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ class ProfessorServiceImpl(

if(mainImage != null) {
mainImageService.uploadMainImage(professor, mainImage)
} else {
professor.mainImage = null
}

// 학력 업데이트
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ class StaffServiceImpl(

if(mainImage != null) {
mainImageService.uploadMainImage(staff, mainImage)
} else {
staff.mainImage = null
}

// 주요 업무 업데이트
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class NewsEntity(

var isSlide: Boolean,

var isImportant: Boolean,

@OneToOne
var mainImage: MainImageEntity? = null,

Expand All @@ -41,6 +43,7 @@ class NewsEntity(
description = newsDto.description,
isPublic = newsDto.isPublic,
isSlide = newsDto.isSlide,
isImportant = newsDto.isImportant,
)
}
}
Expand All @@ -49,5 +52,6 @@ class NewsEntity(
this.description = updateNewsRequest.description
this.isPublic = updateNewsRequest.isPublic
this.isSlide = updateNewsRequest.isSlide
this.isImportant = updateNewsRequest.isImportant
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ data class NewsDto(
val modifiedAt: LocalDateTime?,
val isPublic: Boolean,
val isSlide: Boolean,
val isImportant: Boolean,
val prevId: Long?,
val prevTitle: String?,
val nextId: Long?,
Expand All @@ -31,6 +32,7 @@ data class NewsDto(
modifiedAt = this.modifiedAt,
isPublic = this.isPublic,
isSlide = this.isSlide,
isImportant = this.isImportant,
prevId = prevNext?.get(0)?.id,
prevTitle = prevNext?.get(0)?.title,
nextId = prevNext?.get(1)?.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,15 @@ class NewsServiceImpl(

if(mainImage != null) {
mainImageService.uploadMainImage(news, mainImage)
} else {
news.mainImage = null
}
Comment on lines +94 to 96
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 알기로는 MainImage로의 OneToOne 관계에서 Cascade관련 설정이 되어있지 않은 것으로 알고 있습니다.
따로 정의되지 않은 경우 기본적으로 Cascading이 일어나지 않아서 여기서 null을 처리하더라도 mainImage 자체는 제거되지 않는 것으로 알고 있습니다.
이미 테스트를 해보셨고 잘 된다면 상관없지만, 아니라면 이 부분이 수정이 필요해 보입니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mainImage가 잘나타나긴 하는데, 제거는 또 그거랑 다른 거라고 이해했습니다.
슬랙에서 말한 거 따라서 mainImage의 isDeleted을 true로 만들었습니다.


if(attachments != null) {
news.attachments.clear()
attachmentService.uploadAllAttachments(news, attachments)
} else {
news.attachments.clear()
}

val oldTags = news.newsTags.map { it.tag.name }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import jakarta.validation.Valid
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartFile

@RequestMapping("/api/v1/notice")
@RestController
Expand Down Expand Up @@ -34,17 +35,19 @@ class NoticeController(
@AuthenticatedStaff
@PostMapping
fun createNotice(
@Valid @RequestBody request: NoticeDto
@Valid @RequestPart("request") request: NoticeDto,
@RequestPart("attachments") attachments: List<MultipartFile>?
): ResponseEntity<NoticeDto> {
return ResponseEntity.ok(noticeService.createNotice(request))
return ResponseEntity.ok(noticeService.createNotice(request, attachments))
}

@PatchMapping("/{noticeId}")
fun updateNotice(
@PathVariable noticeId: Long,
@Valid @RequestBody request: NoticeDto,
@Valid @RequestPart("request") request: NoticeDto,
@RequestPart("attachments") attachments: List<MultipartFile>?,
): ResponseEntity<NoticeDto> {
return ResponseEntity.ok(noticeService.updateNotice(noticeId, request))
return ResponseEntity.ok(noticeService.updateNotice(noticeId, request, attachments))
}

@DeleteMapping("/{noticeId}")
Expand All @@ -54,6 +57,19 @@ class NoticeController(
noticeService.deleteNotice(noticeId)
}

@PatchMapping
fun unpinManyNotices(
@RequestBody request: NoticeIdListRequest
) {
noticeService.unpinManyNotices(request.idList)
}
@DeleteMapping
fun deleteManyNotices(
@RequestBody request: NoticeIdListRequest
) {
noticeService.deleteManyNotices(request.idList)
}

@PostMapping("/tag")
fun enrollTag(
@RequestBody tagName: Map<String, String>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,41 @@
package com.wafflestudio.csereal.core.notice.database

import com.wafflestudio.csereal.common.config.BaseTimeEntity
import com.wafflestudio.csereal.common.controller.AttachmentContentEntityType
import com.wafflestudio.csereal.core.notice.dto.NoticeDto
import com.wafflestudio.csereal.core.resource.attachment.database.AttachmentEntity
import com.wafflestudio.csereal.core.user.database.UserEntity
import jakarta.persistence.*


@Entity(name = "notice")
class NoticeEntity(

var isDeleted: Boolean = false,

var title: String,

var description: String,

var isPublic: Boolean,

var isPinned: Boolean,
var isImportant: Boolean,

@OneToMany(mappedBy = "notice", cascade = [CascadeType.ALL])
var noticeTags: MutableSet<NoticeTagEntity> = mutableSetOf(),

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "users_id")
val author: UserEntity
) : BaseTimeEntity() {
val author: UserEntity,

@OneToMany(mappedBy = "notice", cascade = [CascadeType.ALL], orphanRemoval = true)
var attachments: MutableList<AttachmentEntity> = mutableListOf(),

) : BaseTimeEntity(), AttachmentContentEntityType {
override fun bringAttachments() = attachments

fun update(updateNoticeRequest: NoticeDto) {
this.title = updateNoticeRequest.title
this.description = updateNoticeRequest.description
this.isPublic = updateNoticeRequest.isPublic
this.isPinned = updateNoticeRequest.isPinned
this.isImportant = updateNoticeRequest.isImportant
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,20 @@ class NoticeRepositoryImpl(

val noticeEntityList = jpaQuery.orderBy(noticeEntity.isPinned.desc())
.orderBy(noticeEntity.createdAt.desc())
.offset(20*pageNum) //로컬 테스트를 위해 잠시 5로 둘 것, 원래는 20
.offset(20*pageNum)
.limit(20)
.distinct()
.fetch()

val noticeSearchDtoList : List<NoticeSearchDto> = noticeEntityList.map {
val hasAttachment : Boolean = it.attachments.isNotEmpty()

NoticeSearchDto(
id = it.id,
title = it.title,
createdAt = it.createdAt,
isPinned = it.isPinned,
hasAttachment = hasAttachment
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.wafflestudio.csereal.core.notice.dto

import com.wafflestudio.csereal.core.notice.database.NoticeEntity
import com.wafflestudio.csereal.core.resource.attachment.dto.AttachmentResponse
import java.time.LocalDateTime

data class NoticeDto(
Expand All @@ -13,14 +14,16 @@ data class NoticeDto(
val modifiedAt: LocalDateTime?,
val isPublic: Boolean,
val isPinned: Boolean,
val isImportant: Boolean,
val prevId: Long?,
val prevTitle: String?,
val nextId: Long?,
val nextTitle: String?
val nextTitle: String?,
val attachments: List<AttachmentResponse>?,
) {

companion object {
fun of(entity: NoticeEntity, prevNext: Array<NoticeEntity?>?): NoticeDto = entity.run {
fun of(entity: NoticeEntity, attachmentResponses: List<AttachmentResponse>, prevNext: Array<NoticeEntity?>?): NoticeDto = entity.run {
NoticeDto(
id = this.id,
title = this.title,
Expand All @@ -31,10 +34,12 @@ data class NoticeDto(
modifiedAt = this.modifiedAt,
isPublic = this.isPublic,
isPinned = this.isPinned,
isImportant = this.isImportant,
prevId = prevNext?.get(0)?.id,
prevTitle = prevNext?.get(0)?.title,
nextId = prevNext?.get(1)?.id,
nextTitle = prevNext?.get(1)?.title
nextTitle = prevNext?.get(1)?.title,
attachments = attachmentResponses,
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.wafflestudio.csereal.core.notice.dto

data class NoticeIdListRequest(
val idList: List<Long>
) {

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ data class NoticeSearchDto @QueryProjection constructor(
val title: String,
val createdAt: LocalDateTime?,
val isPinned: Boolean,
val hasAttachment: Boolean,
) {

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.wafflestudio.csereal.core.notice.service
import com.wafflestudio.csereal.common.CserealException
import com.wafflestudio.csereal.core.notice.database.*
import com.wafflestudio.csereal.core.notice.dto.*
import com.wafflestudio.csereal.core.resource.attachment.service.AttachmentService
import com.wafflestudio.csereal.core.user.database.UserEntity
import com.wafflestudio.csereal.core.user.database.UserRepository
import org.springframework.data.repository.findByIdOrNull
Expand All @@ -12,13 +13,16 @@ import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import org.springframework.web.context.request.RequestAttributes
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.multipart.MultipartFile

interface NoticeService {
fun searchNotice(tag: List<String>?, keyword: String?, pageNum: Long): NoticeSearchResponse
fun readNotice(noticeId: Long, tag: List<String>?, keyword: String?): NoticeDto
fun createNotice(request: NoticeDto): NoticeDto
fun updateNotice(noticeId: Long, request: NoticeDto): NoticeDto
fun createNotice(request: NoticeDto, attachments: List<MultipartFile>?): NoticeDto
fun updateNotice(noticeId: Long, request: NoticeDto, attachments: List<MultipartFile>?): NoticeDto
fun deleteNotice(noticeId: Long)
fun unpinManyNotices(idList: List<Long>)
fun deleteManyNotices(idList: List<Long>)
fun enrollTag(tagName: String)
}

Expand All @@ -27,7 +31,8 @@ class NoticeServiceImpl(
private val noticeRepository: NoticeRepository,
private val tagInNoticeRepository: TagInNoticeRepository,
private val noticeTagRepository: NoticeTagRepository,
private val userRepository: UserRepository
private val userRepository: UserRepository,
private val attachmentService: AttachmentService,
) : NoticeService {

@Transactional(readOnly = true)
Expand All @@ -50,13 +55,15 @@ class NoticeServiceImpl(

if (notice.isDeleted) throw CserealException.Csereal404("삭제된 공지사항입니다.(noticeId: $noticeId)")

val attachmentResponses = attachmentService.createAttachmentResponses(notice.attachments)

val prevNext = noticeRepository.findPrevNextId(noticeId, tag, keyword)

return NoticeDto.of(notice, prevNext)
return NoticeDto.of(notice, attachmentResponses, prevNext)
}

@Transactional
override fun createNotice(request: NoticeDto): NoticeDto {
override fun createNotice(request: NoticeDto, attachments: List<MultipartFile>?): NoticeDto {
var user = RequestContextHolder.getRequestAttributes()?.getAttribute(
"loggedInUser",
RequestAttributes.SCOPE_REQUEST
Expand All @@ -74,6 +81,7 @@ class NoticeServiceImpl(
description = request.description,
isPublic = request.isPublic,
isPinned = request.isPinned,
isImportant = request.isImportant,
author = user
)

Expand All @@ -82,20 +90,33 @@ class NoticeServiceImpl(
NoticeTagEntity.createNoticeTag(newNotice, tag)
}

if(attachments != null) {
attachmentService.uploadAllAttachments(newNotice, attachments)
}

noticeRepository.save(newNotice)

return NoticeDto.of(newNotice, null)
val attachmentResponses = attachmentService.createAttachmentResponses(newNotice.attachments)

return NoticeDto.of(newNotice, attachmentResponses, null)

}

@Transactional
override fun updateNotice(noticeId: Long, request: NoticeDto): NoticeDto {
override fun updateNotice(noticeId: Long, request: NoticeDto, attachments: List<MultipartFile>?): NoticeDto {
val notice: NoticeEntity = noticeRepository.findByIdOrNull(noticeId)
?: throw CserealException.Csereal404("존재하지 않는 공지사항입니다.(noticeId: $noticeId)")
if (notice.isDeleted) throw CserealException.Csereal404("삭제된 공지사항입니다.(noticeId: $noticeId)")

notice.update(request)

if(attachments != null) {
notice.attachments.clear()
attachmentService.uploadAllAttachments(notice, attachments)
} else {
notice.attachments.clear()
}

val oldTags = notice.noticeTags.map { it.tag.name }

val tagsToRemove = oldTags - request.tags
Expand All @@ -112,7 +133,9 @@ class NoticeServiceImpl(
NoticeTagEntity.createNoticeTag(notice, tag)
}

return NoticeDto.of(notice, null)
val attachmentResponses = attachmentService.createAttachmentResponses(notice.attachments)

return NoticeDto.of(notice, attachmentResponses, null)


}
Expand All @@ -126,6 +149,23 @@ class NoticeServiceImpl(

}

@Transactional
override fun unpinManyNotices(idList: List<Long>) {
for(noticeId in idList) {
val notice: NoticeEntity = noticeRepository.findByIdOrNull(noticeId)
?: throw CserealException.Csereal404("존재하지 않는 공지사항을 입력하였습니다.(noticeId: $noticeId)")
notice.isPinned = false
}
}
@Transactional
override fun deleteManyNotices(idList: List<Long>) {
for(noticeId in idList) {
val notice: NoticeEntity = noticeRepository.findByIdOrNull(noticeId)
?: throw CserealException.Csereal404("존재하지 않는 공지사항을 입력하였습니다.(noticeId: $noticeId)")
notice.isDeleted = true
}
}

override fun enrollTag(tagName: String) {
val newTag = TagInNoticeEntity(
name = tagName
Expand Down
Loading