Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,6 @@ data class PostDetailResponse(
val comments: List<CommentResponse>,
@field:Schema(description = "첨부 파일 목록")
val fileUrls: List<FileResponse>,
@field:Schema(description = "신규 게시글 여부 (24시간 이내)")
val isNew: Boolean,
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.weeth.domain.board.application.dto.response

import com.weeth.domain.file.application.dto.response.FileResponse
import com.weeth.domain.user.application.dto.response.UserInfo
import io.swagger.v3.oas.annotations.media.Schema
import java.time.LocalDateTime
Expand All @@ -23,8 +24,8 @@ data class PostListResponse(
val commentCount: Int,
@field:Schema(description = "좋아요 정보")
val like: PostLikeResponse,
@field:Schema(description = "파일 첨부 여부")
val hasFile: Boolean,
@field:Schema(description = "첨부 파일 목록")
val fileUrls: List<FileResponse>,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
@field:Schema(description = "신규 게시글 여부 (24시간 이내)")
val isNew: Boolean,
)
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class PostMapper(
comments: List<CommentResponse>,
files: List<FileResponse>,
isLiked: Boolean,
now: LocalDateTime,
) = PostDetailResponse(
id = post.id,
boardId = post.board.id,
Expand All @@ -41,11 +42,12 @@ class PostMapper(
like = toLikeResponse(post, isLiked),
comments = comments,
fileUrls = files,
isNew = post.createdAt.isAfter(now.minusHours(24)),
Comment thread
hyxklee marked this conversation as resolved.
)

fun toListResponse(
post: Post,
hasFile: Boolean,
files: List<FileResponse>,
now: LocalDateTime,
isLiked: Boolean,
) = PostListResponse(
Expand All @@ -58,7 +60,7 @@ class PostMapper(
time = post.modifiedAt,
commentCount = post.commentCount,
like = toLikeResponse(post, isLiked),
hasFile = hasFile,
fileUrls = files,
isNew = post.createdAt.isAfter(now.minusHours(24)),
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ class ManagePostUseCase(
validateOwner(post, userId)
validateWritePermission(post.board, member)

markPostFilesDeleted(post.id)
deletePostFiles(post.id)
post.markDeleted()
}

Expand Down Expand Up @@ -135,7 +135,7 @@ class ManagePostUseCase(
if (files == null) {
return
}
markPostFilesDeleted(post.id)
deletePostFiles(post.id)
savePostFiles(post, files)
}

Expand All @@ -149,7 +149,11 @@ class ManagePostUseCase(
}
}

private fun markPostFilesDeleted(postId: Long) {
fileReader.findAll(FileOwnerType.POST, postId).forEach { it.markDeleted() }
private fun deletePostFiles(postId: Long) {
val files = fileReader.findAll(FileOwnerType.POST, postId)

if (files.isNotEmpty()) {
fileRepository.deleteAll(files)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.weeth.domain.board.application.exception.NoSearchResultException
import com.weeth.domain.board.application.exception.PageNotFoundException
import com.weeth.domain.board.application.exception.PostNotFoundException
import com.weeth.domain.board.application.mapper.PostMapper
import com.weeth.domain.board.domain.entity.Post
import com.weeth.domain.board.domain.repository.BoardRepository
import com.weeth.domain.board.domain.repository.PostLikeRepository
import com.weeth.domain.board.domain.repository.PostRepository
Expand All @@ -15,6 +16,7 @@ import com.weeth.domain.club.domain.service.ClubMemberPolicy
import com.weeth.domain.comment.application.usecase.query.GetCommentQueryService
import com.weeth.domain.comment.domain.repository.CommentReader
import com.weeth.domain.file.application.mapper.FileMapper
import com.weeth.domain.file.domain.entity.File
import com.weeth.domain.file.domain.enums.FileOwnerType
import com.weeth.domain.file.domain.repository.FileReader
import org.springframework.data.domain.PageRequest
Expand Down Expand Up @@ -56,11 +58,11 @@ class GetPostQueryService(

val files = fileReader.findAll(FileOwnerType.POST, post.id).map(fileMapper::toFileResponse)
val comments = commentReader.findAllByPostId(post.id)

val commentTree = getCommentQueryService.toCommentTreeResponses(comments)
val isLiked = postLikeRepository.existsByPostAndUserIdAndIsActiveTrue(post, userId)
val now = LocalDateTime.now()

return postMapper.toDetailResponse(post, commentTree, files, isLiked)
return postMapper.toDetailResponse(post, commentTree, files, isLiked, now)
}

fun findAllPosts(
Expand All @@ -85,19 +87,8 @@ class GetPostQueryService(
}

val posts = postRepository.findAllActiveByBoardIds(accessibleBoardIds, pageable)
val postIds = posts.content.map { it.id }
val fileExistsByPostId = buildFileExistsMap(postIds)
val likedPostIds = postLikeRepository.findLikedPostIds(postIds, userId)
val now = LocalDateTime.now()

return posts.map { post ->
postMapper.toListResponse(
post,
fileExistsByPostId[post.id] == true,
now,
post.id in likedPostIds,
)
}
return toPostListResponses(posts, userId)
}

fun findPosts(
Expand All @@ -114,19 +105,7 @@ class GetPostQueryService(
val pageable = PageRequest.of(pageNumber, pageSize, Sort.by(Sort.Direction.DESC, "id"))
val posts = postRepository.findAllActiveByBoardId(boardId, pageable)

val postIds = posts.content.map { it.id }
val fileExistsByPostId = buildFileExistsMap(postIds)
val likedPostIds = postLikeRepository.findLikedPostIds(postIds, userId)
val now = LocalDateTime.now()

return posts.map { post ->
postMapper.toListResponse(
post,
fileExistsByPostId[post.id] == true,
now,
post.id in likedPostIds,
)
}
return toPostListResponses(posts, userId)
}

fun searchPosts(
Expand All @@ -147,15 +126,22 @@ class GetPostQueryService(
throw NoSearchResultException()
}

return toPostListResponses(posts, userId)
}

private fun toPostListResponses(
posts: Slice<Post>,
userId: Long,
): Slice<PostListResponse> {
val postIds = posts.content.map { it.id }
val fileExistsByPostId = buildFileExistsMap(postIds)
val filesByPostId = buildFileMap(postIds)
val likedPostIds = postLikeRepository.findLikedPostIds(postIds, userId)
val now = LocalDateTime.now()

return posts.map { post ->
postMapper.toListResponse(
post,
fileExistsByPostId[post.id] == true,
filesByPostId[post.id]?.map(fileMapper::toFileResponse) ?: emptyList(),
now,
post.id in likedPostIds,
)
Expand All @@ -171,12 +157,9 @@ class GetPostQueryService(
}
}

private fun buildFileExistsMap(postIds: List<Long>): Map<Long, Boolean> {
if (postIds.isEmpty()) {
return emptyMap()
}
val filesGrouped = fileReader.findAll(FileOwnerType.POST, postIds).groupBy { it.ownerId }
return postIds.associateWith { filesGrouped.containsKey(it) }
private fun buildFileMap(postIds: List<Long>): Map<Long, List<File>> {
if (postIds.isEmpty()) return emptyMap()
return fileReader.findAll(FileOwnerType.POST, postIds).groupBy { it.ownerId }
}

private fun validateBoardVisibility( // todo: 볼 권한이 없는 경우 권한 관련 예외를 던져주는게 나을지 UX 상의 후 결정
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ data class ClubMemberProfileResponse(
@field:Schema(description = "이메일", example = "hong@example.com")
val email: String,
@field:Schema(description = "전화번호", example = "01012345678")
val tel: String,
val tel: String?,
@field:Schema(description = "학교", example = "가천대학교")
val school: String?,
@field:Schema(description = "학과", example = "컴퓨터공학과")
val department: String,
val department: String?,
@field:Schema(description = "학번", example = "20201234")
val studentId: String,
val studentId: String?,
@field:Schema(description = "소속 기수 목록", example = "[6, 7]")
val cardinals: List<Int>,
@field:Schema(description = "멤버 권한", example = "USER")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ data class ClubMemberResponse(
@field:Schema(description = "이메일", example = "hong@example.com")
val email: String,
@field:Schema(description = "전화번호", example = "01012345678")
val tel: String,
val tel: String?,
@field:Schema(description = "학교", example = "가천대학교")
val school: String?,
@field:Schema(description = "학과", example = "컴퓨터공학과")
val department: String,
val department: String?,
@field:Schema(description = "학번", example = "20201234")
val studentId: String,
val studentId: String?,
@field:Schema(description = "소속 기수 목록", example = "[6, 7]")
val cardinals: List<Int>,
@field:Schema(description = "멤버 상태", example = "ACTIVE")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,15 @@ class ManageClubMemberUsecase(
if (members.isEmpty()) throw ClubMemberNotFoundException()

request.profileImage?.let { profileImage ->
fileRepository
.findAllByOwnerTypeAndOwnerIdAndStatus(
val existingFiles =
fileRepository.findAllByOwnerTypeAndOwnerIdAndStatus(
FileOwnerType.CLUB_MEMBER_PROFILE,
userId,
FileStatus.UPLOADED,
).forEach { it.markDeleted() }
)
if (existingFiles.isNotEmpty()) {
fileRepository.deleteAll(existingFiles)
}
Comment thread
hyxklee marked this conversation as resolved.

val file =
File.createUploaded(
Expand All @@ -122,12 +125,15 @@ class ManageClubMemberUsecase(
val members = clubMemberRepository.findActiveByUserId(userId)
if (members.isEmpty()) throw ClubMemberNotFoundException()

fileRepository
.findAllByOwnerTypeAndOwnerIdAndStatus(
val existingFiles =
fileRepository.findAllByOwnerTypeAndOwnerIdAndStatus(
FileOwnerType.CLUB_MEMBER_PROFILE,
userId,
FileStatus.UPLOADED,
).forEach { it.markDeleted() }
)
if (existingFiles.isNotEmpty()) {
fileRepository.deleteAll(existingFiles)
}

members.forEach { it.removeProfileImage() }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,12 @@ class ManageClubUseCase(
}

request.profileImage?.let { image ->
markExistingFilesDeleted(FileOwnerType.CLUB_PROFILE, clubId)
deleteExistingFiles(FileOwnerType.CLUB_PROFILE, clubId)
saveFile(image, FileOwnerType.CLUB_PROFILE, clubId)
}

request.backgroundImage?.let { image ->
markExistingFilesDeleted(FileOwnerType.CLUB_BACKGROUND, clubId)
deleteExistingFiles(FileOwnerType.CLUB_BACKGROUND, clubId)
saveFile(image, FileOwnerType.CLUB_BACKGROUND, clubId)
}

Expand Down Expand Up @@ -184,7 +184,7 @@ class ManageClubUseCase(
clubPermissionPolicy.requireAdmin(clubId, userId)

val club = clubRepository.getClubById(clubId)
markExistingFilesDeleted(FileOwnerType.CLUB_PROFILE, clubId)
deleteExistingFiles(FileOwnerType.CLUB_PROFILE, clubId)
club.removeProfileImage()
}

Expand All @@ -196,7 +196,7 @@ class ManageClubUseCase(
clubPermissionPolicy.requireAdmin(clubId, userId)

val club = clubRepository.getClubById(clubId)
markExistingFilesDeleted(FileOwnerType.CLUB_BACKGROUND, clubId)
deleteExistingFiles(FileOwnerType.CLUB_BACKGROUND, clubId)
club.removeBackgroundImage()
}

Expand Down Expand Up @@ -225,13 +225,15 @@ class ManageClubUseCase(
fileRepository.save(file)
}

private fun markExistingFilesDeleted(
private fun deleteExistingFiles(
ownerType: FileOwnerType,
ownerId: Long,
) {
fileRepository
.findAllByOwnerTypeAndOwnerIdAndStatus(ownerType, ownerId, FileStatus.UPLOADED)
.forEach { it.markDeleted() }
val files = fileRepository.findAllByOwnerTypeAndOwnerIdAndStatus(ownerType, ownerId, FileStatus.UPLOADED)

if (files.isNotEmpty()) {
fileRepository.deleteAll(files)
}
Comment thread
hyxklee marked this conversation as resolved.
}

private fun validatePrimaryContactEmail(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ class ManageCommentUseCase(
return
}

markCommentFilesDeleted(comment.id)
deleteCommentFiles(comment.id)
saveCommentFiles(comment, files)
}

Expand Down Expand Up @@ -130,13 +130,15 @@ class ManageCommentUseCase(
}

private fun deleteCommentFiles(comment: Comment) {
markCommentFilesDeleted(comment.id)
deleteCommentFiles(comment.id)
}

private fun markCommentFilesDeleted(commentId: Long) {
fileReader
.findAll(FileOwnerType.COMMENT, commentId)
.forEach { it.markDeleted() }
private fun deleteCommentFiles(commentId: Long) {
val files = fileReader.findAll(FileOwnerType.COMMENT, commentId)

if (files.isNotEmpty()) {
fileRepository.deleteAll(files)
}
}

private fun ensureOwner(
Expand Down
5 changes: 1 addition & 4 deletions src/main/kotlin/com/weeth/domain/file/domain/entity/File.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,11 @@ class File(
val ownerId: Long,
@Column(nullable = false, length = 100)
val contentType: FileContentType,
// TODO: 하드 딜리트로 전환 완료되어 더 이상 사용되지 않음. DB 마이그레이션 후 status 컬럼 및 FileStatus enum 제거 예정
@Enumerated(EnumType.STRING)
@Column(nullable = false, length = 20)
var status: FileStatus = FileStatus.UPLOADED,
Comment thread
hyxklee marked this conversation as resolved.
) : BaseEntity() {
fun markDeleted() {
status = FileStatus.DELETED
}

companion object {
fun createUploaded(
fileName: String,
Expand Down
Loading