Skip to content

Commit

Permalink
Merge branch 'develop' into feature/#176-공유_보관함_잠금_기능
Browse files Browse the repository at this point in the history
  • Loading branch information
Ji-Ha authored Aug 21, 2022
2 parents ff317ac + 61869b7 commit 080edf8
Show file tree
Hide file tree
Showing 21 changed files with 266 additions and 106 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,14 @@
<br>

## 프로젝트 구성도
<img width="838" alt="프로젝트 구조도" src="https://user-images.githubusercontent.com/50076031/147803664-473afc5e-9957-4fb0-be65-0a850c0914f9.png">

#### Now
<img width="960" alt="프로젝트 구조도" src="https://user-images.githubusercontent.com/50076031/179347019-56699b54-5ea8-4396-8ec7-07b409ddddb9.png">

<br><br>

#### Before
<img width="690" alt="프로젝트 구조도" src="https://user-images.githubusercontent.com/50076031/147803664-473afc5e-9957-4fb0-be65-0a850c0914f9.png">

<br>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.yapp.web2.util

import com.ninjasquad.springmockk.MockkBean
import com.yapp.web2.config.S3Uploader
import com.yapp.web2.config.S3Client
import com.yapp.web2.domain.ControllerTestUtil
import com.yapp.web2.domain.account.controller.AccountController
import com.yapp.web2.domain.account.service.AccountService
Expand Down Expand Up @@ -49,7 +49,7 @@ abstract class AbstractControllerTest {
internal lateinit var accountService: AccountService

@MockkBean
internal lateinit var s3Uploader: S3Uploader
internal lateinit var s3Client: S3Client

internal val util = ControllerTestUtil()

Expand Down
7 changes: 6 additions & 1 deletion src/main/kotlin/com/yapp/web2/batch/job/TrashRefreshJob.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.yapp.web2.batch.job

import com.yapp.web2.domain.bookmark.entity.Bookmark
import com.yapp.web2.domain.bookmark.repository.BookmarkRepository
import org.slf4j.LoggerFactory
import org.springframework.batch.core.Job
import org.springframework.batch.core.Step
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing
Expand All @@ -24,6 +25,8 @@ class TrashRefreshJob(
private val jobCompletionListener: JobCompletionListener
) {

private val log = LoggerFactory.getLogger(TrashRefreshJob::class.java)

@Bean("bookmarkTrashRefreshJob")
fun bookmarkTrashRefreshJob(): Job {
return jobBuilderFactory.get("bookmarkTrashRefreshJob")
Expand All @@ -45,16 +48,18 @@ class TrashRefreshJob(

@Bean
fun deleteBookmarkReader(): ListItemReader<Bookmark> {
val deleteBookmarkList = bookmarkRepository.findAllByDeletedIsTrueAndDeleteTimeIsAfter(
val deleteBookmarkList = bookmarkRepository.findAllByDeletedIsTrueAndDeleteTimeBefore(
LocalDateTime.now().minusDays(30)
)
log.info("휴지통에서 30일이 지난 북마크는 자동으로 삭제합니다. 삭제할 북마크 갯수: ${deleteBookmarkList.size}")

return ListItemReader(deleteBookmarkList)
}

@Bean
fun deleteBookmarkProcessor(): ItemProcessor<Bookmark, Bookmark> {
return ItemProcessor {
log.info("Bookmark to delete info => userId: ${it.userId}, folderId: ${it.folderId}, folderName: ${it.folderName} title: ${it.title}")
bookmarkRepository.delete(it)
it
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.amazonaws.services.s3.AmazonS3Client
import com.amazonaws.services.s3.model.CannedAccessControlList
import com.amazonaws.services.s3.model.PutObjectRequest
import com.yapp.web2.exception.BusinessException
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
import org.springframework.web.multipart.MultipartFile
Expand All @@ -13,11 +14,14 @@ import java.nio.file.Files
import java.util.*

@Component
class S3Uploader(
class S3Client(
private val amazonS3Client: AmazonS3Client,
@Value("\${cloud.aws.s3.bucket}")
val bucket: String
) {

private val log = LoggerFactory.getLogger(S3Client::class.java)

fun upload(multiPartFile: MultipartFile, dirName: String): String {
val uploadFile: File = convert(multiPartFile)

Expand All @@ -37,14 +41,21 @@ class S3Uploader(
}

private fun removeNewFile(targetFile: File) {
if (targetFile.delete()) return
throw BusinessException("삭제하려는 파일이 존재하지 않습니다")
if (targetFile.delete()) {
return
}
log.info("${targetFile.name} 파일이 존재하지 않습니다.")
throw BusinessException("삭제하려는 파일이 존재하지 않습니다.")
}

private fun convert(file: MultipartFile): File {
val convertFile = File(System.getProperty("user.dir") + "/" + file.originalFilename)
val contentType = Files.probeContentType(convertFile.toPath())
if (!contentType.startsWith("image")) throw BusinessException("이미지가 아닙니다")

if (!contentType.startsWith("image")) {
log.info("${convertFile.name} 파일의 $contentType 은 이미지 파일이 아닙니다.")
throw BusinessException("이미지가 아닙니다")
}

convertFile.createNewFile()
val fos = FileOutputStream(convertFile)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.yapp.web2.domain.account.controller

import com.amazonaws.services.s3.model.AmazonS3Exception
import com.yapp.web2.config.S3Uploader
import com.yapp.web2.config.S3Client
import com.yapp.web2.domain.account.entity.Account
import com.yapp.web2.domain.account.entity.AccountRequestDto
import com.yapp.web2.domain.account.service.AccountService
Expand All @@ -13,7 +13,14 @@ import io.swagger.annotations.ApiParam
import org.slf4j.LoggerFactory
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PatchMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.multipart.MultipartFile
import javax.servlet.http.HttpServletRequest
import javax.validation.Valid
Expand All @@ -22,7 +29,7 @@ import javax.validation.Valid
@RequestMapping("/api/v1/user")
class AccountController(
private val accountService: AccountService,
private val s3Uploader: S3Uploader
private val s3Client: S3Client
) {
companion object {
private const val DIR_NAME = "static"
Expand Down Expand Up @@ -66,7 +73,7 @@ class AccountController(
fun uploadProfileImage(@RequestBody image: MultipartFile): ResponseEntity<Account.ImageUrl> {
var imageUrl: Account.ImageUrl = Account.ImageUrl("")
try {
imageUrl = Account.ImageUrl(s3Uploader.upload(image, DIR_NAME))
imageUrl = Account.ImageUrl(s3Client.upload(image, DIR_NAME))
} catch (e: AmazonS3Exception) {
log.warn("Amazon S3 error (fileName: {}): {}", image.originalFilename, e.message, e)
}
Expand Down Expand Up @@ -212,7 +219,7 @@ class AccountController(
return ResponseEntity.status(HttpStatus.OK).body(Message.DELETE_ACCOUNT_SUCCEED)
}

@ApiOperation(value = "비밀번호 재설정 - 이메일이 존재 여부 확인 API")
@ApiOperation(value = "비밀번호 재설정 - 이메일 존재 여부 확인 API")
@PostMapping("/password/emailCheck")
fun checkEmailExist(
request: HttpServletRequest,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.yapp.web2.domain.account.service

import com.yapp.web2.config.S3Uploader
import com.yapp.web2.config.S3Client
import com.yapp.web2.domain.account.entity.Account
import com.yapp.web2.domain.account.entity.AccountRequestDto
import com.yapp.web2.domain.account.repository.AccountRepository
Expand All @@ -9,15 +9,14 @@ import com.yapp.web2.domain.folder.entity.Authority
import com.yapp.web2.domain.folder.service.FolderService
import com.yapp.web2.exception.BusinessException
import com.yapp.web2.exception.custom.AlreadyInvitedException
import com.yapp.web2.exception.custom.EmailNotFoundException
import com.yapp.web2.exception.custom.ExistNameException
import com.yapp.web2.exception.custom.FolderNotRootException
import com.yapp.web2.exception.custom.ImageNotFoundException
import com.yapp.web2.exception.custom.PasswordMismatchException
import com.yapp.web2.security.jwt.JwtProvider
import com.yapp.web2.security.jwt.TokenDto
import com.yapp.web2.util.Message
import org.apache.commons.lang3.RandomStringUtils
import com.yapp.web2.util.RandomUtils
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.mail.SimpleMailMessage
Expand All @@ -33,7 +32,7 @@ class AccountService(
private val folderService: FolderService,
private val accountRepository: AccountRepository,
private val jwtProvider: JwtProvider,
private val s3Uploader: S3Uploader,
private val s3Client: S3Client,
private val passwordEncoder: PasswordEncoder,
private val mailSender: JavaMailSender
) {
Expand Down Expand Up @@ -74,13 +73,14 @@ class AccountService(

account2 = when (existAccount) {
null -> {
log.info("OAuth2 login => ${account.email} account not exist!")
log.info("소셜로그인 => ${account.email} account not exist!")
isRegistered = false
val newAccount = createUser(account)
folderService.createDefaultFolder(account)
newAccount
}
else -> {
log.info("소셜로그인 => ${account.email} 계정이 이미 존재합니다.")
existAccount.fcmToken = account2.fcmToken
createUser(existAccount)
}
Expand All @@ -90,7 +90,7 @@ class AccountService(

fun signUp(dto: AccountRequestDto.SignUpRequest): Account.AccountLoginSuccess {
if (accountRepository.findByEmail(dto.email) != null) {
log.info("${dto.email} account already exist!")
log.info("${dto.email} 계정이 이미 존재하여 회원가입 할 수 없습니다.")
throw IllegalStateException(Message.EXIST_USER)
}

Expand Down Expand Up @@ -164,7 +164,11 @@ class AccountService(
@Transactional
fun changeProfileImage(token: String, profile: MultipartFile): String {
val account = jwtProvider.getAccountFromToken(token).let {
it.image = s3Uploader.upload(profile, DIR_NAME)
kotlin.runCatching {
it.image = s3Client.upload(profile, DIR_NAME)
}.onFailure {
log.warn("AmazonS3 upload error => directory name: $DIR_NAME ")
}
it
}
return account.image
Expand Down Expand Up @@ -227,10 +231,11 @@ class AccountService(
accountRepository.findByEmail(request.email) ?: throw IllegalStateException(Message.NOT_EXIST_EMAIL)

if (!passwordEncoder.matches(request.password, account.password)) {
log.info("${account.email} 계정의 비밀번호와 일치하지 않습니다.")
throw IllegalStateException(Message.USER_PASSWORD_MISMATCH)
}

log.info("base login => ${account.email} succeed")
log.info("${account.email} 계정으로 로그인에 성공하였습니다.")

return Account.AccountLoginSuccess(jwtProvider.createToken(account), account, true)
}
Expand Down Expand Up @@ -260,16 +265,21 @@ class AccountService(
}

fun checkEmailExist(token: String, request: AccountRequestDto.EmailCheckRequest): String {
accountRepository.findByEmail(request.email)?.let {
if (it.email != request.email) {
throw EmailNotFoundException()
accountRepository.findByEmail(request.email).let {
if (it?.email == request.email) {
return Message.SUCCESS_EXIST_EMAIL
}
}
return Message.SUCCESS_EXIST_EMAIL
log.info("${request.email} 계정이 존재하지 않습니다.")

return Message.NOT_EXIST_EMAIL
}

// 비밀번호는 8 ~ 16자 사이
fun createTempPassword(): String {
return RandomStringUtils.randomAlphanumeric(12) + "!"
val randomAlphanumeric = RandomUtils.getRandomAlphanumeric(14)
val randomSpecialCharacter = RandomUtils.getRandomSpecialCharacter()
return RandomUtils.shuffleCharacters(randomAlphanumeric + randomSpecialCharacter)
}

fun sendMail(token: String, tempPassword: String): String {
Expand All @@ -281,7 +291,7 @@ class AccountService(
mailMessage.setText("안녕하세요. \n\n 임시 비밀번호를 전달드립니다. \n\n 임시 비밀번호는: $tempPassword 입니다.")
mailSender.send(mailMessage)

log.info("Send mail temp password succeed to ${account.email}")
log.info("${account.email} 계정으로 임시 비밀번호를 발송하였습니다.")

return Message.SUCCESS_SEND_MAIL
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ import io.swagger.annotations.ApiOperation
import io.swagger.annotations.ApiParam
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PatchMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import javax.servlet.http.HttpServletRequest
import javax.validation.Valid

Expand Down Expand Up @@ -96,13 +103,15 @@ class BookmarkController(
return ResponseEntity.status(HttpStatus.OK).body(Message.MOVED)
}

@ApiOperation(value = "북마크 리마인드 On")
@GetMapping("/remindOn/{bookmarkId}")
fun toggleOnRemindBookmark(request: HttpServletRequest, @PathVariable bookmarkId: String): ResponseEntity<String> {
val token = ControllerUtil.extractAccessToken(request)
bookmarkService.toggleOnRemindBookmark(token, bookmarkId)
return ResponseEntity.status(HttpStatus.OK).body(Message.UPDATED)
}

@ApiOperation(value = "북마크 리마인드 Off")
@GetMapping("/remindOff/{bookmarkId}")
fun toggleOffRemindBookmark(request: HttpServletRequest, @PathVariable bookmarkId: String): ResponseEntity<String> {
val token = ControllerUtil.extractAccessToken(request)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,20 @@ interface BookmarkRepository : MongoRepository<Bookmark, String> {

fun findAllByUserIdAndRemindCheckIsFalseAndRemindStatusIsTrueAndRemindTimeIsNotNull(userId: Long): List<Bookmark>

fun findAllByDeletedIsTrueAndDeleteTimeIsAfter(time: LocalDateTime): List<Bookmark>
fun findAllByDeletedIsTrueAndDeleteTimeBefore(time: LocalDateTime): List<Bookmark>

@Query(value = "{ 'remindList': { \$elemMatch: { 'fcmToken' : ?0 } } }")
fun findAllBookmarkByFcmToken(fcmToken: String): List<Bookmark>

@Query(value = "{\$and: [{folderId: {\$in: ?1}}, {deleted: false}, {remindList: {\$elemMatch: {userId : ?0}}}]}")
fun findRemindBookmarkInFolder(userId: Long, folderIdList: List<Long>): List<Bookmark>
fun findRemindBookmarkInFolder(userId: Long, folderIdList: List<Long>, pageable: Pageable): Page<Bookmark>

@Query(value = "{\$or: [{folderId: {\$in: ?1}}, {userId: ?0}]}")
fun findAllBookmark(userId: Long, folderIdList: List<Long>): List<Bookmark>
fun findAllBookmark(userId: Long, folderIdList: List<Long>, pageable: Pageable): Page<Bookmark>

@Query(value = "{\$and: [{remindList: {\$elemMatch: {userId: ?0}}}, {remindList: {\$elemMatch: {remindTime: ?1}}}]}")
fun findTodayRemindBookmark(userId: Long, today: String): List<Bookmark>

@Query(value = "{ 'remindList': { \$elemMatch: { 'userId' : ?0 } } }")
fun findRemindBookmark(userId: Long): List<Bookmark>
fun findRemindBookmark(userId: Long, pageable: Pageable): Page<Bookmark>
}
Loading

0 comments on commit 080edf8

Please sign in to comment.