Skip to content

Commit

Permalink
Merge pull request #16 from Novel-Cloud/feature/NOVEL-69
Browse files Browse the repository at this point in the history
Feature/Novel-69: 작품 수정, 삭제 기능 추가
  • Loading branch information
min050410 committed Apr 24, 2023
2 parents 4230c2e + aa6ae4a commit 6d3d718
Show file tree
Hide file tree
Showing 19 changed files with 245 additions and 60 deletions.
15 changes: 12 additions & 3 deletions src/main/kotlin/com/novel/cloud/db/entity/artwork/Artwork.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class Artwork(
@Column(nullable = false)
@Enumerated(EnumType.STRING)
var artworkType: ArtworkType = artworkType
protected set

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(nullable = false)
Expand All @@ -58,20 +59,20 @@ class Artwork(
protected val mutableTags: MutableList<Tag> = tags.toMutableList()
val tags: List<Tag> get() = mutableTags.toList()

@OneToMany(fetch = FetchType.LAZY)
@OneToMany(fetch = FetchType.LAZY, cascade = [CascadeType.ALL])
@JoinColumn(nullable = false)
private val mutableComments: MutableList<Comment> = mutableListOf()
val comments: List<Comment> get() = mutableComments.toList()

@Column(length = 200, nullable = true)
var thumbnail: String? = null

@OneToMany(fetch = FetchType.LAZY)
@OneToMany(fetch = FetchType.LAZY, cascade = [CascadeType.ALL])
@JoinColumn(nullable = false)
private val mutableAttachFiles: MutableList<AttachFile> = mutableListOf()
val attachFiles: List<AttachFile> get() = mutableAttachFiles.toList()

@OneToMany(fetch = FetchType.LAZY)
@OneToMany(fetch = FetchType.LAZY, cascade = [CascadeType.ALL])
@JoinColumn(nullable = false)
private val mutableBookmarks: MutableList<Bookmark> = mutableListOf()
val bookmarks: List<Bookmark> get() = mutableBookmarks.toList()
Expand All @@ -80,6 +81,14 @@ class Artwork(
writer.writeArtwork(this)
}

fun update(title: String, content: String, artworkType: ArtworkType, tags: List<Tag>) {
this.title = title
this.content = content
this.artworkType = artworkType
this.mutableTags.removeAll(this.tags)
this.mutableTags.addAll(tags)
}

fun updateThumbnail(thumbnail: String) {
this.thumbnail = thumbnail
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ class AttachFile (
attachFileType: AttachFileType
): BaseEntity() {

@Column(length = 200, nullable = false)
@Column(length = 400, nullable = false)
var fileName: String = fileName
protected set

@Column(length = 200, nullable = false)
@Column(length = 1000, nullable = false)
var fileUidName: String = fileUidName
protected set

@Column(length = 200, nullable = false)
@Column(length = 1000, nullable = false)
var filePath: String = filePath
protected set

Expand Down
6 changes: 5 additions & 1 deletion src/main/kotlin/com/novel/cloud/db/entity/tag/Tag.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,12 @@ class Tag(
var writer: Member = writer
protected set

fun updateUsageCount() {
fun plusUsageCount() {
usageCount++
}

fun minusUsageCount() {
usageCount--
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ class SecurityConfig(
}
authorizeRequests().apply {
antMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**").permitAll()
antMatchers("/resources/**", "/").permitAll() // 에러 핸들러
antMatchers(ApiPath.ERROR_AUTH).permitAll() // 인증
antMatchers(ApiPath.LOGIN_OAUTH2).permitAll()
antMatchers("/resources/**", "/v1").permitAll()
antMatchers(ApiPath.ERROR_AUTH).permitAll() // 에러 핸들러
antMatchers(ApiPath.LOGIN_OAUTH2).permitAll() // 인증
antMatchers(ApiPath.REFRESH_TOKEN).permitAll()
antMatchers(ApiPath.VIEW_ARTWORK).permitAll() // 조회
antMatchers(ApiPath.ARTWORK_DETAIL).permitAll()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class Swagger2Config {
fun publicApi(): GroupedOpenApi? {
return GroupedOpenApi.builder()
.group("v1-documentation")
.pathsToMatch("/api/**")
.pathsToMatch("/api/v1/**")
.addOpenApiCustomiser(openApiCustomizer())
.build()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ object CorsPatternConstant {

const val CORS_LOCAL = "http*://localhost:[*]"

const val CORS_LOCAL_SWAGGER = "http*://10.150.151.237/:[*]"
const val CORS_LOCAL_SWAGGER = "http*://192.168.11.250/:[*]"

const val CORS_PR = "http*://ec2-54-180-163-47.ap-northeast-2.compute.amazonaws.com"
const val CORS_PR = "http*://example.com"

const val CORS_PR_FE = "http*://novel-cloud.kro.kr"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package com.novel.cloud.web.domain.artwork.controller
import com.novel.cloud.web.config.security.context.MemberContext
import com.novel.cloud.web.domain.artwork.controller.rq.CreateArtworkRq
import com.novel.cloud.web.domain.artwork.controller.rq.AutoSaveTemporaryArtworkRq
import com.novel.cloud.web.domain.artwork.controller.rq.DeleteArtworkRq
import com.novel.cloud.web.domain.artwork.controller.rq.UpdateArtworkRq
import com.novel.cloud.web.domain.artwork.controller.rq.UpdateArtworkViewRq
import com.novel.cloud.web.domain.artwork.service.ArtworkService
import com.novel.cloud.web.domain.file.service.FileService
Expand All @@ -12,7 +14,9 @@ import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestPart
import org.springframework.web.bind.annotation.RestController
Expand Down Expand Up @@ -40,16 +44,43 @@ class ArtworkController(
fun submitArtwork(
@AuthenticationPrincipal memberContext: MemberContext,
@Validated @RequestPart(value = "rq") rq: CreateArtworkRq,
@RequestPart(value = "thumbnail", required = false) thumbnail: MultipartFile?,
@RequestPart(value = "files", required = false) files: List<MultipartFile>?,
) {
val artwork = artworkService.submitArtwork(memberContext, rq)
// TODO::refactoring
thumbnail?.let {
files?.let {
FileValidateUtils.imageValidationCheck(files)
fileService.uploadArtworkImage(memberContext, artwork, thumbnail, files)
}
}
}

@Operation(summary = "작품 수정")
@PutMapping(ApiPath.ARTWORK_UPDATE)
fun updateArtwork(
@AuthenticationPrincipal memberContext: MemberContext,
@Validated @RequestPart(value = "rq") rq: UpdateArtworkRq,
@RequestPart(value = "thumbnail") thumbnail: MultipartFile,
@RequestPart(value = "files") files: List<MultipartFile>,
) {
FileValidateUtils.supportedFileValidationCheck(files)
val artwork = artworkService.submitArtwork(memberContext, rq)
fileService.uploadArtworkImage(memberContext, artwork, thumbnail, files)
FileValidateUtils.imageValidationCheck(files)
val artwork = artworkService.updateArtwork(memberContext, rq)
fileService.updateArtworkImage(memberContext, artwork, thumbnail, files)
}

@Operation(summary = "작품 삭제")
@DeleteMapping(ApiPath.ARTWORK_DELETE)
fun deleteArtwork(
@AuthenticationPrincipal memberContext: MemberContext,
@Validated @RequestBody rq: DeleteArtworkRq
) {
artworkService.deleteArtwork(memberContext, rq)
}

@Operation(summary = "조회수 증가")
@PostMapping(ApiPath.VIEW_ARTWORK)
@PutMapping(ApiPath.VIEW_ARTWORK)
fun updateArtworkView(@Validated @RequestBody rq: UpdateArtworkViewRq) {
artworkService.updateArtworkView(rq)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.novel.cloud.web.domain.artwork.controller.rq

import javax.validation.constraints.NotNull

data class DeleteArtworkRq (

@field:NotNull
val artworkId: Long

)
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.novel.cloud.web.domain.artwork.controller.rq

import com.novel.cloud.db.enums.ArtworkType
import javax.validation.constraints.NotEmpty
import javax.validation.constraints.NotNull

data class UpdateArtworkRq (

@field:NotNull
val artworkId: Long,

@field:NotEmpty
val title: String,

@field:NotEmpty
val content: String,

@field:NotNull
val artworkType: ArtworkType,

@field:NotNull
val tags: List<String>

)
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ package com.novel.cloud.web.domain.artwork.service

import com.novel.cloud.db.entity.artwork.Artwork
import com.novel.cloud.db.entity.artwork.TemporaryArtwork
import com.novel.cloud.db.entity.member.Member
import com.novel.cloud.web.config.security.context.MemberContext
import com.novel.cloud.web.domain.artwork.controller.rq.CreateArtworkRq
import com.novel.cloud.web.domain.artwork.controller.rq.AutoSaveTemporaryArtworkRq
import com.novel.cloud.web.domain.artwork.controller.rq.DeleteArtworkRq
import com.novel.cloud.web.domain.artwork.controller.rq.UpdateArtworkRq
import com.novel.cloud.web.domain.artwork.controller.rq.UpdateArtworkViewRq
import com.novel.cloud.web.domain.artwork.repository.ArtworkRepository
import com.novel.cloud.web.domain.artwork.repository.TemporaryArtworkRepository
import com.novel.cloud.web.domain.member.service.FindMemberService
import com.novel.cloud.web.domain.tag.service.ArtworkTagService
import com.novel.cloud.web.exception.DoNotHavePermissionToDeleteOrUpdateArtworkException
import com.novel.cloud.web.utils.DateUtils
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
Expand Down Expand Up @@ -47,6 +51,52 @@ class ArtworkService(
return artwork
}

/**
* 작품 수정
*/
fun updateArtwork(memberContext: MemberContext, rq: UpdateArtworkRq): Artwork {
val member = findMemberService.findLoginMemberOrElseThrow(memberContext)
val artwork = findArtworkService.findByIdOrElseThrow(rq.artworkId)

val tagContents = rq.tags.distinct()
val beforeTags = artwork.tags

val tags = artworkTagService.createTags(member, tagContents)

artwork.update(
title = rq.title,
content = rq.content,
artworkType = rq.artworkType,
tags = tags
)

artworkTagService.removeTags(member, beforeTags)
return artwork
}

/**
* 작품 삭제
*/
fun deleteArtwork(memberContext: MemberContext, rq: DeleteArtworkRq) {
val member = findMemberService.findLoginMemberOrElseThrow(memberContext)
val artwork = findArtworkService.findByIdOrElseThrow(rq.artworkId)
val tags = artwork.tags

artworkPermissionCheck(member, artwork)
artworkRepository.delete(artwork)

artworkTagService.removeTags(member, tags)
}

private fun artworkPermissionCheck(member: Member, artwork: Artwork) {
val writerId: Long? = artwork.writer.id
val memberId: Long? = member.id
if (writerId != memberId) {
throw DoNotHavePermissionToDeleteOrUpdateArtworkException()
}
}


/**
* 작품 조회수 증가
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class FileController(
fun editorImageUpload(
@RequestPart(value = "image") image: MultipartFile
): String {
FileValidateUtils.supportedFileValidationCheck(image)
FileValidateUtils.imageValidationCheck(image)
return s3UploadService.upload(image)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,24 @@ class FileService(
}
}

/**
* 이미지 업데이트
*/
fun updateArtworkImage(
memberContext: MemberContext,
artwork: Artwork,
thumbnail: MultipartFile,
multipartFiles: List<MultipartFile>
) {
deleteArtworkImages(artwork.attachFiles)
uploadThumbnail(artwork, thumbnail)
uploadImageFiles(artwork, multipartFiles)
}

private fun deleteArtworkImages(attachFiles: List<AttachFile>) {
attachFiles.map { attachFile ->
fileRepository.delete(attachFile)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class S3UploadService(
*/
@Throws(IOException::class)
fun upload(file: MultipartFile): String {
val fileName = UUID.randomUUID().toString() + "-" + file.originalFilename
val fileName = UUID.randomUUID().toString()
val objMeta = ObjectMetadata()

val bytes = IOUtils.toByteArray(file.inputStream)
Expand All @@ -42,8 +42,10 @@ class S3UploadService(

val byteArrayIs = ByteArrayInputStream(bytes)

s3Client.putObject(PutObjectRequest(bucket, dir + fileName, byteArrayIs, objMeta)
.withCannedAcl(CannedAccessControlList.PublicRead))
s3Client.putObject(
PutObjectRequest(bucket, dir + fileName, byteArrayIs, objMeta)
.withCannedAcl(CannedAccessControlList.PublicRead)
)

val path = s3Client.getUrl(bucket, dir + fileName).path
return urlToCloudFrontDomain(path)
Expand All @@ -54,5 +56,4 @@ class S3UploadService(
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class MemberController(
@AuthenticationPrincipal memberContext: MemberContext,
@RequestPart(value = "profile") profile: MultipartFile,
) {
FileValidateUtils.supportedFileValidationCheck(profile)
FileValidateUtils.profileImageValidationCheck(profile)
return memberService.updateMemberPicture(memberContext, profile)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,25 @@ class ArtworkTagService(
*/
fun createTags(member: Member, tags: List<String>): List<Tag> {
return tags.map { content ->
// 없으면 태그 생성
val tag = findArtworkTagService.findByContentOrElseNull(content) ?: Tag(
content = content,
writer = member
)
artworkTagRepository.save(tag)
tag.updateUsageCount()
tag.plusUsageCount()
tag
}.toList()
}

fun removeTags(member: Member, beforeTags: List<Tag>) {
beforeTags.map { tag ->
if (tag.usageCount == 1L) {
artworkTagRepository.delete(tag)
}
if (tag.usageCount > 1L) {
tag.minusUsageCount()
}
}
}

}
Loading

0 comments on commit 6d3d718

Please sign in to comment.