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

v20240521.0 버전 적용 #157

Merged
merged 32 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
cfa175d
Add Popup domain
tlsgmltjd Apr 24, 2024
66a4ef4
Update board save dto
tlsgmltjd Apr 25, 2024
e304a6b
Add popup save logic
tlsgmltjd Apr 25, 2024
a75faf3
Add QueryCurrentPopupUasCase interface
tlsgmltjd Apr 25, 2024
acd2040
Add PopupCustomRepositoryImpl query dsl
tlsgmltjd Apr 25, 2024
057a6ec
Update QueryCurrentPopupService use repository
tlsgmltjd Apr 25, 2024
e761132
Add popupcontroller
tlsgmltjd Apr 25, 2024
25d58c9
Update BoardSaveDto popupExp column validation
tlsgmltjd Apr 25, 2024
6aae634
Update query dsl innerjoin
tlsgmltjd Apr 25, 2024
14c5dcf
Update popup save category teacher
tlsgmltjd Apr 25, 2024
af54b7c
Delete blank line
tlsgmltjd Apr 25, 2024
deae563
게시글 상세조회 좋아요 상태 필드 추가
tlsgmltjd Apr 29, 2024
651be7d
Fix popup usecase typo
tlsgmltjd Apr 30, 2024
343375b
Hotfix 변경사항 반영
tlsgmltjd Apr 30, 2024
95f9a45
Merge pull request #151 from themoment-team/feature/popUp
tlsgmltjd Apr 30, 2024
23b92e9
Merge pull request #154 from themoment-team/feature/updateLikeStatusF…
tlsgmltjd Apr 30, 2024
eb94b25
Add board list dto like state field
tlsgmltjd May 7, 2024
6731823
Add Board list isLike state query dsl repository
tlsgmltjd May 7, 2024
16d2aa6
Merge pull request #155 from themoment-team/feature/boardListLikeStat…
tlsgmltjd May 8, 2024
613a37b
Add email dependency
tlsgmltjd May 15, 2024
7928623
Add
tlsgmltjd May 15, 2024
5aaf059
Add email-template html
tlsgmltjd May 15, 2024
ebd1eb9
이메일 템플릿 수정
tlsgmltjd May 20, 2024
6d0be7c
템플릿엔진, 이메일 의존성 추가
tlsgmltjd May 21, 2024
ab7ffab
메일 생성 함수 구현
tlsgmltjd May 21, 2024
f129acc
멘토 이메일 QueryDSL
tlsgmltjd May 21, 2024
73b36be
멘토 이메일 repository 조인식 변경
tlsgmltjd May 21, 2024
8abb8c5
테스트 컨트롤러, 설정 클래스 삭제
tlsgmltjd May 21, 2024
0781887
개행 추가
tlsgmltjd May 21, 2024
faab3f9
메일 메서드 네이밍 변경
tlsgmltjd May 21, 2024
43a27ca
메일 메서드 네이밍 변경
tlsgmltjd May 21, 2024
9cec890
Merge pull request #156 from themoment-team/feature/email-send
tlsgmltjd May 21, 2024
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
6 changes: 6 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ dependencies {
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

/* thymeleaf */
implementation("org.springframework.boot:spring-boot-starter-thymeleaf")

/** validation **/
implementation("org.springframework.boot:spring-boot-starter-validation")

Expand All @@ -65,6 +68,9 @@ dependencies {
/* aws */
implementation("io.awspring.cloud:spring-cloud-aws-starter-s3:3.0.1")

/* emali */
implementation("org.springframework.boot:spring-boot-starter-mail:2.6.7")

/* security */
implementation("org.springframework.boot:spring-boot-starter-security")
testImplementation("org.springframework.security:spring-security-test")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,14 @@ class BoardController (
@Min(value = 0, message = "pageSize는 0 이상이어야 합니다.") @Max(value = 20, message = "pageSize는 20 이하여야 합니다.") @RequestParam pageSize: Long,
@RequestParam(required = false) boardCategory: BoardCategory?
) : ResponseEntity<List<BoardListDto>> {
return ResponseEntity.ok(queryBoardListUseCase.queryBoardList(cursorId, pageSize, boardCategory))
val authenticationId = authenticatedUserManager.getName()
return ResponseEntity.ok(queryBoardListUseCase.queryBoardList(cursorId, pageSize, boardCategory, authenticationId))
}

@GetMapping("/{boardId}")
fun queryBoardInfo(@PathVariable boardId: Long): ResponseEntity<BoardInfoDto> {
return ResponseEntity.ok(queryBoardInfoUseCase.queryBoardInfo(boardId))
val authenticationId = authenticatedUserManager.getName()
return ResponseEntity.ok(queryBoardInfoUseCase.queryBoardInfo(boardId, authenticationId))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ data class BoardInfoDto (
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
val createdAt: LocalDateTime,
val comments: List<CommentListDto>,
val likeCount: Int
val likeCount: Int,
val isLike: Boolean
)
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ data class BoardListDto (
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
val createdAt: LocalDateTime,
val commentCount: Int,
val likeCount: Int
val likeCount: Int,
val isLike: Boolean
)
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package team.themoment.gsmNetworking.domain.board.dto

import io.micrometer.core.lang.Nullable
import team.themoment.gsmNetworking.domain.board.domain.BoardCategory
import javax.persistence.EnumType
import javax.persistence.Enumerated
import javax.validation.constraints.NotBlank
import javax.validation.constraints.NotNull
import javax.validation.constraints.Size
import javax.validation.constraints.*

data class BoardSaveDto (
@field:NotBlank
Expand All @@ -16,5 +15,9 @@ data class BoardSaveDto (
val content: String,
@field:NotNull
@Enumerated(EnumType.STRING)
val boardCategory: BoardCategory
val boardCategory: BoardCategory,
@field:Nullable
@field:Min(1)
@field:Max(30)
val popupExp: Int?
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package team.themoment.gsmNetworking.domain.board.repository

import team.themoment.gsmNetworking.domain.board.domain.BoardCategory
import team.themoment.gsmNetworking.domain.board.dto.BoardListDto
import team.themoment.gsmNetworking.domain.user.domain.User

interface BoardCustomRepository {
fun findPageByCursorId(cursorId: Long, pageSize: Long, boardCategory: BoardCategory?): List<BoardListDto>
fun findPageWithRecentBoard(pageSize: Long, boardCategory: BoardCategory?): List<BoardListDto>
fun findPageByCursorId(cursorId: Long, pageSize: Long, boardCategory: BoardCategory?, user: User): List<BoardListDto>
fun findPageWithRecentBoard(pageSize: Long, boardCategory: BoardCategory?, user: User): List<BoardListDto>
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
package team.themoment.gsmNetworking.domain.board.repository

import com.querydsl.core.types.ExpressionUtils
import com.querydsl.core.types.Projections
import com.querydsl.core.types.dsl.BooleanExpression
import com.querydsl.jpa.JPAExpressions
import com.querydsl.jpa.impl.JPAQueryFactory
import team.themoment.gsmNetworking.domain.board.domain.BoardCategory
import team.themoment.gsmNetworking.domain.board.domain.QBoard
import team.themoment.gsmNetworking.domain.board.domain.QBoard.board
import team.themoment.gsmNetworking.domain.board.dto.BoardListDto
import team.themoment.gsmNetworking.domain.comment.dto.AuthorDto
import team.themoment.gsmNetworking.domain.like.domain.QLike
import team.themoment.gsmNetworking.domain.like.domain.QLike.like
import team.themoment.gsmNetworking.domain.user.domain.User


class BoardCustomRepositoryImpl (
private val queryFactory: JPAQueryFactory
) : BoardCustomRepository {

override fun findPageByCursorId(cursorId: Long, pageSize: Long, boardCategory: BoardCategory?): List<BoardListDto> {
override fun findPageByCursorId(cursorId: Long, pageSize: Long, boardCategory: BoardCategory?, user: User): List<BoardListDto> {
return queryFactory.select(
Projections.constructor(
BoardListDto::class.java,
Expand All @@ -28,7 +35,8 @@ class BoardCustomRepositoryImpl (
),
board.createdAt,
board.comments.size(),
board.likes.size()
board.likes.size(),
likeCase(user)
)
)
.from(board)
Expand All @@ -38,7 +46,7 @@ class BoardCustomRepositoryImpl (
.fetch()
}

override fun findPageWithRecentBoard(pageSize: Long, boardCategory: BoardCategory?): List<BoardListDto> {
override fun findPageWithRecentBoard(pageSize: Long, boardCategory: BoardCategory?, user: User): List<BoardListDto> {
return queryFactory.select(
Projections.constructor(
BoardListDto::class.java,
Expand All @@ -54,7 +62,8 @@ class BoardCustomRepositoryImpl (
),
board.createdAt,
board.comments.size(),
board.likes.size()
board.likes.size(),
likeCase(user)
)
)
.from(board)
Expand All @@ -67,4 +76,13 @@ class BoardCustomRepositoryImpl (
private fun eqCategory(boardCategory: BoardCategory?): BooleanExpression? =
boardCategory?.let { board.boardCategory.eq(it) }

private fun likeCase(user: User): BooleanExpression =
JPAExpressions.selectOne()
.from(like)
.where(
like.board.eq(board),
like.user.eq(user)
)
.exists()

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package team.themoment.gsmNetworking.domain.board.service
import team.themoment.gsmNetworking.domain.board.dto.BoardInfoDto

interface QueryBoardInfoUseCase {
fun queryBoardInfo(boardId: Long): BoardInfoDto
fun queryBoardInfo(boardId: Long, authenticationId: Long): BoardInfoDto
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ import team.themoment.gsmNetworking.domain.board.domain.BoardCategory
import team.themoment.gsmNetworking.domain.board.dto.BoardListDto

interface QueryBoardListUseCase {
fun queryBoardList(cursorId: Long, pageSize: Long, boardCategory: BoardCategory?): List<BoardListDto>
fun queryBoardList(cursorId: Long, pageSize: Long, boardCategory: BoardCategory?, authenticationId: Long): List<BoardListDto>
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package team.themoment.gsmNetworking.domain.board.service.impl

import org.springframework.http.HttpStatus
import org.springframework.mail.javamail.JavaMailSender
import org.springframework.mail.javamail.MimeMessageHelper
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import org.thymeleaf.context.Context
import org.thymeleaf.spring5.ISpringTemplateEngine
import team.themoment.gsmNetworking.common.exception.ExpectedException
import team.themoment.gsmNetworking.domain.auth.domain.Authority
import team.themoment.gsmNetworking.domain.auth.repository.AuthenticationRepository
import team.themoment.gsmNetworking.domain.board.domain.BoardCategory
import team.themoment.gsmNetworking.domain.board.domain.Board
import team.themoment.gsmNetworking.domain.board.domain.BoardCategory.*
import team.themoment.gsmNetworking.domain.board.dto.BoardInfoDto
import team.themoment.gsmNetworking.domain.board.dto.BoardListDto
import team.themoment.gsmNetworking.domain.board.dto.BoardSaveDto
Expand All @@ -21,14 +26,23 @@ import team.themoment.gsmNetworking.domain.comment.dto.CommentListDto
import team.themoment.gsmNetworking.domain.comment.dto.ReplyDto
import team.themoment.gsmNetworking.domain.comment.dto.ReplyCommentInfo
import team.themoment.gsmNetworking.domain.comment.repository.CommentRepository
import team.themoment.gsmNetworking.domain.mentor.repository.MentorRepository
import team.themoment.gsmNetworking.domain.popup.domain.Popup
import team.themoment.gsmNetworking.domain.popup.repository.PopupRepository
import team.themoment.gsmNetworking.domain.user.repository.UserRepository
import java.time.LocalDateTime
import javax.mail.internet.MimeMessage

@Service
class BoardService (
private val boardRepository: BoardRepository,
private val userRepository: UserRepository,
private val mentorRepository: MentorRepository,
private val commentRepository: CommentRepository,
private val authenticationRepository: AuthenticationRepository
private val popupRepository: PopupRepository,
private val authenticationRepository: AuthenticationRepository,
private val mailSender: JavaMailSender,
private val templateEngine: ISpringTemplateEngine
) : SaveBoardUseCase,
QueryBoardListUseCase,
QueryBoardInfoUseCase {
Expand All @@ -41,7 +55,7 @@ class BoardService (
val authentication = authenticationRepository.findById(authenticationId)
.orElseThrow { throw ExpectedException("유저의 권한 정보를 찾을 수 없습니다.", HttpStatus.NOT_FOUND) }

if (boardSaveDto.boardCategory == BoardCategory.TEACHER
if (boardSaveDto.boardCategory == TEACHER
&& authentication.authority != Authority.TEACHER) {
throw ExpectedException("선생님이 아닌 유저는 선생님 카테고리를 이용할 수 없습니다.", HttpStatus.NOT_FOUND)
}
Expand All @@ -55,6 +69,23 @@ class BoardService (

val savedBoard = boardRepository.save(newBoard)

val popupExp = boardSaveDto.popupExp
if (popupExp != null && boardSaveDto.boardCategory == TEACHER) {
val currentDateTime = LocalDateTime.now()
val newPopupExpTime = currentDateTime.plusDays(popupExp.toLong())

val newPopup = Popup(
board = savedBoard,
expTime = newPopupExpTime
)

popupRepository.save(newPopup)
}

if (savedBoard.boardCategory == TEACHER) {
sendEmailToMentors(savedBoard.id, savedBoard.title)
}

return BoardListDto(
id = savedBoard.id,
title = savedBoard.title,
Expand All @@ -67,20 +98,33 @@ class BoardService (
),
createdAt = savedBoard.createdAt,
commentCount = 0,
likeCount = 0
likeCount = 0,
isLike = false
)

}

@Transactional(readOnly = true)
override fun queryBoardList(cursorId: Long, pageSize: Long, boardCategory: BoardCategory?): List<BoardListDto> =
if (cursorId == 0L)
boardRepository.findPageWithRecentBoard(pageSize, boardCategory)
else
boardRepository.findPageByCursorId(cursorId, pageSize, boardCategory)
override fun queryBoardList(cursorId: Long,
pageSize: Long,
boardCategory: BoardCategory?,
authenticationId: Long): List<BoardListDto> {

val currentUser = userRepository.findByAuthenticationId(authenticationId)
?: throw ExpectedException("유저를 찾을 수 없습니다.", HttpStatus.NOT_FOUND)


return if (cursorId == 0L)
boardRepository.findPageWithRecentBoard(pageSize, boardCategory, currentUser)
else
boardRepository.findPageByCursorId(cursorId, pageSize, boardCategory, currentUser)
}

@Transactional(readOnly = true)
override fun queryBoardInfo(boardId: Long): BoardInfoDto {
override fun queryBoardInfo(boardId: Long, authenticationId: Long): BoardInfoDto {
val currentUser = userRepository.findByAuthenticationId(authenticationId)
?: throw ExpectedException("유저를 찾을 수 없습니다.", HttpStatus.NOT_FOUND)

val currentBoard = boardRepository.findById(boardId)
.orElseThrow { ExpectedException("게시글을 찾을 수 없습니다.", HttpStatus.NOT_FOUND) }

Expand All @@ -99,7 +143,10 @@ class BoardService (
),
createdAt = currentBoard.createdAt,
comments = getFindComments(findComments),
likeCount = currentBoard.likes.size
likeCount = currentBoard.likes.size,
isLike = currentBoard.likes.stream().anyMatch {
like -> like.user == currentUser
}
)
}

Expand Down Expand Up @@ -134,4 +181,34 @@ class BoardService (
) }
}

private fun sendEmailToMentors(boardId: Long, postTitle: String) {
mentorRepository.findAllMentorEmailDto().forEach { mentor ->
sendMail(boardId, postTitle, mentor.email)
}
}

private fun sendMail(boardId: Long, postTitle: String, toEmail: String) {
mailSender.send(getMessage(boardId, postTitle, toEmail))
}

private fun getMessage(boardId: Long, postTitle: String, toEmail: String): MimeMessage {
val message = mailSender.createMimeMessage()
val messageHelper = MimeMessageHelper(message, "UTF-8")

messageHelper.setSubject("GSM-Networking에 새로운 게시글이 등록되었습니다!")
messageHelper.setText(createMailTemplate(boardId, postTitle), true)
messageHelper.setTo(toEmail)

return message
}

private fun createMailTemplate(boardId: Long, postTitle: String): String {
val context = Context()

context.setVariable("teacherBoardId", boardId)
context.setVariable("teacherPostTitle", postTitle)

return templateEngine.process("email-template", context)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package team.themoment.gsmNetworking.domain.mentor.dto

data class MentorEmailDto (
val email: String
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package team.themoment.gsmNetworking.domain.mentor.repository

import team.themoment.gsmNetworking.domain.mentor.dto.MentorEmailDto
import team.themoment.gsmNetworking.domain.mentor.dto.MentorInfoDto
import team.themoment.gsmNetworking.domain.mentor.dto.MyMentorInfoDto

Expand All @@ -8,4 +9,6 @@ interface MentorCustomRepository {
fun findAllMentorInfoDto(): List<MentorInfoDto>

fun findMyMentorInfoDto(authenticationId: Long): MyMentorInfoDto?

fun findAllMentorEmailDto(): List<MentorEmailDto>
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ import org.hibernate.NonUniqueResultException
import org.springframework.dao.IncorrectResultSizeDataAccessException
import team.themoment.gsmNetworking.domain.mentor.domain.QCareer.career
import team.themoment.gsmNetworking.domain.mentor.domain.QMentor.mentor
import team.themoment.gsmNetworking.domain.mentor.dto.CompanyInfoDto
import team.themoment.gsmNetworking.domain.mentor.dto.MentorInfoDto
import team.themoment.gsmNetworking.domain.mentor.dto.MyCareerInfoDto
import team.themoment.gsmNetworking.domain.mentor.dto.MyMentorInfoDto
import team.themoment.gsmNetworking.domain.mentor.dto.*
import team.themoment.gsmNetworking.domain.user.domain.QUser.user

/**
Expand Down Expand Up @@ -98,4 +95,16 @@ class MentorCustomRepositoryImpl(
else
myMentorInfoDto[0]
}

override fun findAllMentorEmailDto(): List<MentorEmailDto> {
return queryFactory
.select(Projections.constructor(
MentorEmailDto::class.java,
mentor.user.email
))
.from(mentor)
.innerJoin(mentor.user, user)
.on(mentor.user.id.eq(user.id))
.fetch()
}
}
Loading
Loading