크리에이터 관리자 - 콘텐츠 리스트, 로그인 API 추가
This commit is contained in:
parent
d0aebe906b
commit
c3cb8fe93b
|
@ -68,6 +68,7 @@ class SecurityConfig(
|
||||||
.antMatchers("/member/check/nickname").permitAll()
|
.antMatchers("/member/check/nickname").permitAll()
|
||||||
.antMatchers("/member/signup").permitAll()
|
.antMatchers("/member/signup").permitAll()
|
||||||
.antMatchers("/member/login").permitAll()
|
.antMatchers("/member/login").permitAll()
|
||||||
|
.antMatchers("/creator-admin/member/login").permitAll()
|
||||||
.antMatchers("/member/forgot-password").permitAll()
|
.antMatchers("/member/forgot-password").permitAll()
|
||||||
.antMatchers("/stplat/terms_of_service").permitAll()
|
.antMatchers("/stplat/terms_of_service").permitAll()
|
||||||
.antMatchers("/stplat/privacy_policy").permitAll()
|
.antMatchers("/stplat/privacy_policy").permitAll()
|
||||||
|
|
|
@ -10,6 +10,8 @@ class WebConfig : WebMvcConfigurer {
|
||||||
registry.addMapping("/**")
|
registry.addMapping("/**")
|
||||||
.allowedOrigins(
|
.allowedOrigins(
|
||||||
"http://localhost:8888",
|
"http://localhost:8888",
|
||||||
|
"https://creator.sodalive.net",
|
||||||
|
"https://test-creator.sodalive.net",
|
||||||
"https://test-admin.sodalive.net",
|
"https://test-admin.sodalive.net",
|
||||||
"https://admin.sodalive.net"
|
"https://admin.sodalive.net"
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
package kr.co.vividnext.sodalive.creator_admin.content
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.admin.content.UpdateAdminContentRequest
|
||||||
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
|
import kr.co.vividnext.sodalive.common.SodaException
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import org.springframework.data.domain.Pageable
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize
|
||||||
|
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.PutMapping
|
||||||
|
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
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@PreAuthorize("hasRole('CREATOR')")
|
||||||
|
@RequestMapping("/creator-admin/audio-content")
|
||||||
|
class CreatorAdminContentController(private val service: CreatorAdminContentService) {
|
||||||
|
@GetMapping("/list")
|
||||||
|
fun getAudioContentList(
|
||||||
|
pageable: Pageable,
|
||||||
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||||
|
) = run {
|
||||||
|
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
|
|
||||||
|
ApiResponse.ok(service.getAudioContentList(pageable, member))
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/search")
|
||||||
|
fun searchAudioContent(
|
||||||
|
@RequestParam(value = "search_word") searchWord: String,
|
||||||
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
|
||||||
|
pageable: Pageable
|
||||||
|
) = run {
|
||||||
|
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
|
|
||||||
|
ApiResponse.ok(service.searchAudioContent(searchWord, member, pageable))
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping
|
||||||
|
fun modifyAudioContent(
|
||||||
|
@RequestBody request: UpdateAdminContentRequest,
|
||||||
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||||
|
) = run {
|
||||||
|
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
|
|
||||||
|
ApiResponse.ok(service.updateAudioContent(request, member))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
package kr.co.vividnext.sodalive.creator_admin.content
|
||||||
|
|
||||||
|
import com.querydsl.core.types.dsl.DateTimePath
|
||||||
|
import com.querydsl.core.types.dsl.Expressions
|
||||||
|
import com.querydsl.core.types.dsl.StringTemplate
|
||||||
|
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||||
|
import kr.co.vividnext.sodalive.content.AudioContent
|
||||||
|
import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
|
||||||
|
import kr.co.vividnext.sodalive.content.hashtag.QAudioContentHashTag.audioContentHashTag
|
||||||
|
import kr.co.vividnext.sodalive.content.hashtag.QHashTag.hashTag
|
||||||
|
import kr.co.vividnext.sodalive.content.theme.QAudioContentTheme.audioContentTheme
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
interface CreatorAdminContentRepository : JpaRepository<AudioContent, Long>, CreatorAdminAudioContentQueryRepository
|
||||||
|
|
||||||
|
interface CreatorAdminAudioContentQueryRepository {
|
||||||
|
fun getAudioContentTotalCount(memberId: Long, searchWord: String = ""): Int
|
||||||
|
fun getAudioContentList(
|
||||||
|
memberId: Long,
|
||||||
|
offset: Long,
|
||||||
|
limit: Long,
|
||||||
|
searchWord: String = ""
|
||||||
|
): List<GetCreatorAdminContentListItem>
|
||||||
|
|
||||||
|
fun getHashTagList(audioContentId: Long): List<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
class CreatorAdminAudioContentQueryRepositoryImpl(
|
||||||
|
private val queryFactory: JPAQueryFactory
|
||||||
|
) : CreatorAdminAudioContentQueryRepository {
|
||||||
|
override fun getAudioContentTotalCount(memberId: Long, searchWord: String): Int {
|
||||||
|
var where = audioContent.duration.isNotNull
|
||||||
|
.and(audioContent.member.isNotNull)
|
||||||
|
.and(audioContent.isActive.isTrue)
|
||||||
|
.and(audioContent.member.id.eq(memberId))
|
||||||
|
|
||||||
|
if (searchWord.trim().length > 1) {
|
||||||
|
where = where.and(
|
||||||
|
audioContent.title.contains(searchWord)
|
||||||
|
.or(audioContent.member.nickname.contains(searchWord))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryFactory
|
||||||
|
.select(audioContent.id)
|
||||||
|
.from(audioContent)
|
||||||
|
.where(where)
|
||||||
|
.fetch()
|
||||||
|
.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAudioContentList(
|
||||||
|
memberId: Long,
|
||||||
|
offset: Long,
|
||||||
|
limit: Long,
|
||||||
|
searchWord: String
|
||||||
|
): List<GetCreatorAdminContentListItem> {
|
||||||
|
var where = audioContent.duration.isNotNull
|
||||||
|
.and(audioContent.member.isNotNull)
|
||||||
|
.and(audioContent.isActive.isTrue)
|
||||||
|
.and(audioContent.member.id.eq(memberId))
|
||||||
|
|
||||||
|
if (searchWord.trim().length > 1) {
|
||||||
|
where = where.and(
|
||||||
|
audioContent.title.contains(searchWord)
|
||||||
|
.or(audioContent.member.nickname.contains(searchWord))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryFactory
|
||||||
|
.select(
|
||||||
|
QGetCreatorAdminContentListItem(
|
||||||
|
audioContent.id,
|
||||||
|
audioContent.title,
|
||||||
|
audioContent.detail,
|
||||||
|
audioContent.coverImage,
|
||||||
|
audioContent.member!!.nickname,
|
||||||
|
audioContentTheme.theme,
|
||||||
|
audioContent.price,
|
||||||
|
audioContent.isAdult,
|
||||||
|
audioContent.duration,
|
||||||
|
audioContent.content,
|
||||||
|
formattedDateExpression(audioContent.createdAt)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.from(audioContent)
|
||||||
|
.innerJoin(audioContent.theme, audioContentTheme)
|
||||||
|
.where(where)
|
||||||
|
.offset(offset)
|
||||||
|
.limit(limit)
|
||||||
|
.orderBy(audioContent.id.desc())
|
||||||
|
.fetch()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getHashTagList(audioContentId: Long): List<String> {
|
||||||
|
return queryFactory
|
||||||
|
.select(hashTag.tag)
|
||||||
|
.from(audioContentHashTag)
|
||||||
|
.innerJoin(audioContentHashTag.hashTag, hashTag)
|
||||||
|
.innerJoin(audioContentHashTag.audioContent, audioContent)
|
||||||
|
.where(
|
||||||
|
audioContent.duration.isNotNull
|
||||||
|
.and(audioContent.member.isNotNull)
|
||||||
|
.and(audioContentHashTag.audioContent.id.eq(audioContentId))
|
||||||
|
)
|
||||||
|
.fetch()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun formattedDateExpression(
|
||||||
|
dateTime: DateTimePath<LocalDateTime>,
|
||||||
|
format: String = "%Y-%m-%d"
|
||||||
|
): StringTemplate {
|
||||||
|
return Expressions.stringTemplate(
|
||||||
|
"DATE_FORMAT({0}, {1})",
|
||||||
|
Expressions.dateTimeTemplate(
|
||||||
|
LocalDateTime::class.java,
|
||||||
|
"CONVERT_TZ({0},{1},{2})",
|
||||||
|
dateTime,
|
||||||
|
"UTC",
|
||||||
|
"Asia/Seoul"
|
||||||
|
),
|
||||||
|
format
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
package kr.co.vividnext.sodalive.creator_admin.content
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.admin.content.UpdateAdminContentRequest
|
||||||
|
import kr.co.vividnext.sodalive.aws.cloudfront.AudioContentCloudFront
|
||||||
|
import kr.co.vividnext.sodalive.common.SodaException
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import org.springframework.beans.factory.annotation.Value
|
||||||
|
import org.springframework.data.domain.Pageable
|
||||||
|
import org.springframework.data.repository.findByIdOrNull
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class CreatorAdminContentService(
|
||||||
|
private val repository: CreatorAdminContentRepository,
|
||||||
|
private val audioContentCloudFront: AudioContentCloudFront,
|
||||||
|
|
||||||
|
@Value("\${cloud.aws.cloud-front.host}")
|
||||||
|
private val coverImageHost: String
|
||||||
|
) {
|
||||||
|
fun getAudioContentList(pageable: Pageable, member: Member): GetCreatorAdminContentListResponse {
|
||||||
|
val totalCount = repository.getAudioContentTotalCount(memberId = member.id!!)
|
||||||
|
val audioContentAndThemeList = repository.getAudioContentList(
|
||||||
|
memberId = member.id!!,
|
||||||
|
offset = pageable.offset,
|
||||||
|
limit = pageable.pageSize.toLong()
|
||||||
|
)
|
||||||
|
|
||||||
|
val audioContentList = audioContentAndThemeList
|
||||||
|
.asSequence()
|
||||||
|
.map {
|
||||||
|
val tags = repository
|
||||||
|
.getHashTagList(audioContentId = it.audioContentId)
|
||||||
|
.joinToString(" ") { tag -> tag }
|
||||||
|
it.tags = tags
|
||||||
|
it
|
||||||
|
}
|
||||||
|
.map {
|
||||||
|
it.contentUrl = audioContentCloudFront.generateSignedURL(
|
||||||
|
resourcePath = it.contentUrl,
|
||||||
|
expirationTime = 1000 * 60 * 60 * (it.remainingTime.split(":")[0].toLong() + 2)
|
||||||
|
)
|
||||||
|
it
|
||||||
|
}
|
||||||
|
.map {
|
||||||
|
it.coverImageUrl = "$coverImageHost/${it.coverImageUrl}"
|
||||||
|
it
|
||||||
|
}
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
return GetCreatorAdminContentListResponse(totalCount, audioContentList)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun searchAudioContent(searchWord: String, member: Member, pageable: Pageable): GetCreatorAdminContentListResponse {
|
||||||
|
if (searchWord.length < 2) throw SodaException("2글자 이상 입력하세요.")
|
||||||
|
val totalCount = repository.getAudioContentTotalCount(
|
||||||
|
memberId = member.id!!,
|
||||||
|
searchWord
|
||||||
|
)
|
||||||
|
val audioContentAndThemeList = repository.getAudioContentList(
|
||||||
|
memberId = member.id!!,
|
||||||
|
offset = pageable.offset,
|
||||||
|
limit = pageable.pageSize.toLong(),
|
||||||
|
searchWord = searchWord
|
||||||
|
)
|
||||||
|
|
||||||
|
val audioContentList = audioContentAndThemeList
|
||||||
|
.asSequence()
|
||||||
|
.map {
|
||||||
|
val tags = repository
|
||||||
|
.getHashTagList(audioContentId = it.audioContentId)
|
||||||
|
.joinToString(" ") { tag -> tag }
|
||||||
|
it.tags = tags
|
||||||
|
it
|
||||||
|
}
|
||||||
|
.map {
|
||||||
|
it.contentUrl = audioContentCloudFront.generateSignedURL(
|
||||||
|
resourcePath = it.contentUrl,
|
||||||
|
expirationTime = 1000 * 60 * 60 * (it.remainingTime.split(":")[0].toLong() + 2)
|
||||||
|
)
|
||||||
|
it
|
||||||
|
}
|
||||||
|
.map {
|
||||||
|
it.coverImageUrl = "$coverImageHost/${it.coverImageUrl}"
|
||||||
|
it
|
||||||
|
}
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
return GetCreatorAdminContentListResponse(totalCount, audioContentList)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
fun updateAudioContent(request: UpdateAdminContentRequest, member: Member) {
|
||||||
|
val audioContent = repository.findByIdOrNull(id = request.id)
|
||||||
|
?: throw SodaException("잘못된 콘텐츠 입니다.")
|
||||||
|
|
||||||
|
if (request.isDefaultCoverImage) {
|
||||||
|
audioContent.coverImage = "profile/default_profile.png"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.isActive != null) {
|
||||||
|
audioContent.isActive = request.isActive
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.isAdult != null) {
|
||||||
|
audioContent.isAdult = request.isAdult
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.isCommentAvailable != null) {
|
||||||
|
audioContent.isCommentAvailable = request.isCommentAvailable
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.title != null) {
|
||||||
|
audioContent.title = request.title
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.detail != null) {
|
||||||
|
audioContent.detail = request.detail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package kr.co.vividnext.sodalive.creator_admin.content
|
||||||
|
|
||||||
|
import com.querydsl.core.annotations.QueryProjection
|
||||||
|
|
||||||
|
data class GetCreatorAdminContentListResponse(
|
||||||
|
val totalCount: Int,
|
||||||
|
val items: List<GetCreatorAdminContentListItem>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class GetCreatorAdminContentListItem @QueryProjection constructor(
|
||||||
|
val audioContentId: Long,
|
||||||
|
val title: String,
|
||||||
|
val detail: String,
|
||||||
|
var coverImageUrl: String,
|
||||||
|
val creatorNickname: String,
|
||||||
|
val theme: String,
|
||||||
|
val price: Int,
|
||||||
|
val isAdult: Boolean,
|
||||||
|
val remainingTime: String,
|
||||||
|
var contentUrl: String,
|
||||||
|
val date: String
|
||||||
|
) {
|
||||||
|
var tags: String = ""
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package kr.co.vividnext.sodalive.creator_admin.member
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
|
import kr.co.vividnext.sodalive.common.SodaException
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import kr.co.vividnext.sodalive.member.login.LoginRequest
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize
|
||||||
|
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody
|
||||||
|
import org.springframework.web.bind.annotation.RequestHeader
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/creator-admin/member")
|
||||||
|
class CreatorAdminMemberController(private val service: CreatorAdminMemberService) {
|
||||||
|
@PostMapping("/login")
|
||||||
|
fun login(@RequestBody loginRequest: LoginRequest) = service.login(loginRequest)
|
||||||
|
|
||||||
|
@PostMapping("/logout")
|
||||||
|
@PreAuthorize("hasRole('CREATOR')")
|
||||||
|
fun logout(
|
||||||
|
@RequestHeader("Authorization") token: String,
|
||||||
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||||
|
) = run {
|
||||||
|
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
|
|
||||||
|
ApiResponse.ok(service.logout(token.removePrefix("Bearer "), member.id!!))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
package kr.co.vividnext.sodalive.creator_admin.member
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
|
import kr.co.vividnext.sodalive.common.SodaException
|
||||||
|
import kr.co.vividnext.sodalive.jwt.TokenProvider
|
||||||
|
import kr.co.vividnext.sodalive.member.MemberRepository
|
||||||
|
import kr.co.vividnext.sodalive.member.MemberRole
|
||||||
|
import kr.co.vividnext.sodalive.member.login.LoginRequest
|
||||||
|
import kr.co.vividnext.sodalive.member.login.LoginResponse
|
||||||
|
import kr.co.vividnext.sodalive.member.token.MemberTokenRepository
|
||||||
|
import org.springframework.beans.factory.annotation.Value
|
||||||
|
import org.springframework.data.repository.findByIdOrNull
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||||
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
|
import kotlin.concurrent.write
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class CreatorAdminMemberService(
|
||||||
|
private val repository: MemberRepository,
|
||||||
|
private val tokenRepository: MemberTokenRepository,
|
||||||
|
private val tokenProvider: TokenProvider,
|
||||||
|
private val authenticationManagerBuilder: AuthenticationManagerBuilder,
|
||||||
|
|
||||||
|
@Value("\${cloud.aws.cloud-front.host}")
|
||||||
|
private val cloudFrontHost: String
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val tokenLocks: MutableMap<Long, ReentrantReadWriteLock> = mutableMapOf()
|
||||||
|
|
||||||
|
fun login(request: LoginRequest): ApiResponse<LoginResponse> {
|
||||||
|
return ApiResponse.ok(
|
||||||
|
message = "로그인 되었습니다.",
|
||||||
|
data = login(request.email, request.password)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
fun logout(token: String, memberId: Long) {
|
||||||
|
val member = repository.findByIdOrNull(memberId)
|
||||||
|
?: throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
|
|
||||||
|
member.pushToken = null
|
||||||
|
|
||||||
|
val lock = getOrCreateLock(memberId = memberId)
|
||||||
|
lock.write {
|
||||||
|
val memberToken = tokenRepository.findByIdOrNull(memberId)
|
||||||
|
?: throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
|
|
||||||
|
memberToken.tokenSet.remove(token)
|
||||||
|
tokenRepository.save(memberToken)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun login(email: String, password: String): LoginResponse {
|
||||||
|
val member = repository.findByEmail(email = email) ?: throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
|
if (!member.isActive) {
|
||||||
|
throw SodaException("탈퇴한 계정입니다.\n고객센터로 문의해 주시기 바랍니다.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (member.role != MemberRole.CREATOR) {
|
||||||
|
throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
|
}
|
||||||
|
|
||||||
|
val authenticationToken = UsernamePasswordAuthenticationToken(email, password)
|
||||||
|
val authentication = authenticationManagerBuilder.`object`.authenticate(authenticationToken)
|
||||||
|
SecurityContextHolder.getContext().authentication = authentication
|
||||||
|
|
||||||
|
val jwt = tokenProvider.createToken(
|
||||||
|
authentication = authentication,
|
||||||
|
memberId = member.id!!
|
||||||
|
)
|
||||||
|
|
||||||
|
return LoginResponse(
|
||||||
|
userId = member.id!!,
|
||||||
|
token = jwt,
|
||||||
|
nickname = member.nickname,
|
||||||
|
email = member.email,
|
||||||
|
profileImage = if (member.profileImage != null) {
|
||||||
|
"$cloudFrontHost/${member.profileImage}"
|
||||||
|
} else {
|
||||||
|
"$cloudFrontHost/profile/default-profile.png"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getOrCreateLock(memberId: Long): ReentrantReadWriteLock {
|
||||||
|
return tokenLocks.computeIfAbsent(memberId) { ReentrantReadWriteLock() }
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,6 @@ import org.springframework.web.bind.annotation.RestController
|
||||||
@RequestMapping("/menu")
|
@RequestMapping("/menu")
|
||||||
class MenuController(private val service: MenuService) {
|
class MenuController(private val service: MenuService) {
|
||||||
@GetMapping
|
@GetMapping
|
||||||
@PreAuthorize("hasAnyRole('AGENT', 'ADMIN')")
|
@PreAuthorize("hasAnyRole('AGENT', 'ADMIN', 'CREATOR')")
|
||||||
fun getMenus(@AuthenticationPrincipal user: User) = ApiResponse.ok(service.getMenus(user))
|
fun getMenus(@AuthenticationPrincipal user: User) = ApiResponse.ok(service.getMenus(user))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue