test #214
| @@ -603,6 +603,7 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) | ||||
|             .and(audioContent.member.role.eq(MemberRole.CREATOR)) | ||||
|             .and(audioContent.duration.isNotNull) | ||||
|             .and(audioContentTheme.isActive.isTrue) | ||||
|             .and(audioContent.limited.isNull) | ||||
|  | ||||
|         if (!isAdult) { | ||||
|             where = where.and(audioContent.isAdult.isFalse) | ||||
|   | ||||
| @@ -450,8 +450,10 @@ class AudioContentService( | ||||
|         val creator = explorerQueryRepository.getMember(creatorId) | ||||
|             ?: throw SodaException("없는 사용자 입니다.") | ||||
|  | ||||
|         val notificationUserIds = explorerQueryRepository.getNotificationUserIds(creatorId) | ||||
|         val isFollowing = notificationUserIds.contains(member.id) | ||||
|         val creatorFollowing = explorerQueryRepository.getCreatorFollowing( | ||||
|             creatorId = creatorId, | ||||
|             memberId = member.id!! | ||||
|         ) | ||||
|  | ||||
|         // 구매 여부 확인 | ||||
|         val isExistsBundleAudioContent = bundleAudioContentList | ||||
| @@ -640,7 +642,9 @@ class AudioContentService( | ||||
|                 } else { | ||||
|                     "$coverImageHost/profile/default-profile.png" | ||||
|                 }, | ||||
|                 isFollowing = isFollowing | ||||
|                 isFollowing = creatorFollowing?.isFollow ?: false, | ||||
|                 isFollow = creatorFollowing?.isFollow ?: false, | ||||
|                 isNotify = creatorFollowing?.isNotify ?: false | ||||
|             ) | ||||
|         ) | ||||
|     } | ||||
|   | ||||
| @@ -47,5 +47,7 @@ data class AudioContentCreator( | ||||
|     val creatorId: Long, | ||||
|     val nickname: String, | ||||
|     val profileImageUrl: String, | ||||
|     val isFollowing: Boolean | ||||
|     val isFollowing: Boolean, | ||||
|     val isFollow: Boolean, | ||||
|     val isNotify: Boolean | ||||
| ) | ||||
|   | ||||
| @@ -61,7 +61,7 @@ class ContentSeriesService( | ||||
|             throw SodaException("잘못된 시리즈 입니다.\n다시 시도해 주세요") | ||||
|         } | ||||
|  | ||||
|         val isFollow = explorerQueryRepository.isFollow( | ||||
|         val creatorFollowing = explorerQueryRepository.getCreatorFollowing( | ||||
|             creatorId = series.member!!.id!!, | ||||
|             memberId = member.id!! | ||||
|         ) | ||||
| @@ -102,7 +102,8 @@ class ContentSeriesService( | ||||
|                 creatorId = series.member!!.id!!, | ||||
|                 nickname = series.member!!.nickname, | ||||
|                 profileImage = "$coverImageHost/${series.member!!.profileImage}", | ||||
|                 isFollow = isFollow | ||||
|                 isFollow = creatorFollowing?.isFollow ?: false, | ||||
|                 isNotify = creatorFollowing?.isNotify ?: false | ||||
|             ), | ||||
|             rentalMinPrice = rentalMinPrice, | ||||
|             rentalMaxPrice = rentalMaxPrice, | ||||
|   | ||||
| @@ -27,6 +27,7 @@ data class GetSeriesDetailResponse( | ||||
|         val creatorId: Long, | ||||
|         val nickname: String, | ||||
|         val profileImage: String, | ||||
|         val isFollow: Boolean | ||||
|         val isFollow: Boolean, | ||||
|         val isNotify: Boolean | ||||
|     ) | ||||
| } | ||||
|   | ||||
| @@ -10,6 +10,8 @@ data class CreatorResponse( | ||||
|     val youtubeUrl: String? = null, | ||||
|     val websiteUrl: String? = null, | ||||
|     val blogUrl: String? = null, | ||||
|     val isFollow: Boolean, | ||||
|     val isNotify: Boolean, | ||||
|     val isNotification: Boolean, | ||||
|     val notificationRecipientCount: Int | ||||
| ) | ||||
|   | ||||
| @@ -46,6 +46,18 @@ class ExplorerQueryRepository( | ||||
|     @Value("\${cloud.aws.cloud-front.host}") | ||||
|     private val cloudFrontHost: String | ||||
| ) { | ||||
|     fun getCreatorFollowing(creatorId: Long, memberId: Long): GetCreatorFollowingResponse? { | ||||
|         return queryFactory | ||||
|             .select(QGetCreatorFollowingResponse(creatorFollowing.isActive, creatorFollowing.isNotify)) | ||||
|             .from(creatorFollowing) | ||||
|             .where( | ||||
|                 creatorFollowing.isActive.isTrue | ||||
|                     .and(creatorFollowing.creator.id.eq(creatorId)) | ||||
|                     .and(creatorFollowing.member.id.eq(memberId)) | ||||
|             ) | ||||
|             .fetchFirst() | ||||
|     } | ||||
|  | ||||
|     fun getNotificationUserIds(creatorId: Long): List<Long> { | ||||
|         return queryFactory | ||||
|             .select(creatorFollowing.member.id) | ||||
|   | ||||
| @@ -145,7 +145,7 @@ class ExplorerService( | ||||
|         if (isBlocked) throw SodaException("${creatorAccount.nickname}님의 요청으로 채널 접근이 제한됩니다.") | ||||
|  | ||||
|         val notificationUserIds = queryRepository.getNotificationUserIds(creatorId) | ||||
|         val isNotification = notificationUserIds.contains(member.id) | ||||
|         val creatorFollowing = queryRepository.getCreatorFollowing(creatorId = creatorId, memberId = member.id!!) | ||||
|         val notificationRecipientCount = notificationUserIds.size | ||||
|  | ||||
|         // 후원랭킹 | ||||
| @@ -223,7 +223,9 @@ class ExplorerService( | ||||
|                 youtubeUrl = creatorAccount.youtubeUrl, | ||||
|                 websiteUrl = creatorAccount.websiteUrl, | ||||
|                 blogUrl = creatorAccount.blogUrl, | ||||
|                 isNotification = isNotification, | ||||
|                 isFollow = creatorFollowing?.isFollow ?: false, | ||||
|                 isNotify = creatorFollowing?.isNotify ?: false, | ||||
|                 isNotification = creatorFollowing?.isFollow ?: false, | ||||
|                 notificationRecipientCount = notificationRecipientCount | ||||
|             ), | ||||
|             userDonationRanking = memberDonationRanking, | ||||
| @@ -305,8 +307,11 @@ class ExplorerService( | ||||
|  | ||||
|         val followerList = queryRepository.getFollowerList(creatorId, pageable.offset, pageable.pageSize.toLong()) | ||||
|             .map { | ||||
|                 val isFollow = if (it.role == MemberRole.CREATOR) { | ||||
|                     queryRepository.isFollow(creatorId = it.userId, memberId = member.id!!) | ||||
|                 val creatorFollowing = if (it.role == MemberRole.CREATOR) { | ||||
|                     queryRepository.getCreatorFollowing( | ||||
|                         creatorId = it.userId, | ||||
|                         memberId = member.id!! | ||||
|                     ) | ||||
|                 } else { | ||||
|                     null | ||||
|                 } | ||||
| @@ -315,7 +320,16 @@ class ExplorerService( | ||||
|                     userId = it.userId, | ||||
|                     profileImage = it.profileImage, | ||||
|                     nickname = it.nickname, | ||||
|                     isFollow = isFollow | ||||
|                     isFollow = creatorFollowing?.isFollow ?: if (it.role == MemberRole.CREATOR) { | ||||
|                         false | ||||
|                     } else { | ||||
|                         null | ||||
|                     }, | ||||
|                     isNotify = creatorFollowing?.isNotify ?: if (it.role == MemberRole.CREATOR) { | ||||
|                         false | ||||
|                     } else { | ||||
|                         null | ||||
|                     } | ||||
|                 ) | ||||
|             } | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,5 @@ | ||||
| package kr.co.vividnext.sodalive.explorer | ||||
|  | ||||
| import com.querydsl.core.annotations.QueryProjection | ||||
|  | ||||
| data class GetCreatorFollowingResponse @QueryProjection constructor(val isFollow: Boolean, val isNotify: Boolean) | ||||
| @@ -9,5 +9,6 @@ data class GetFollowerListResponseItem( | ||||
|     val userId: Long, | ||||
|     val profileImage: String, | ||||
|     val nickname: String, | ||||
|     val isFollow: Boolean? | ||||
|     val isFollow: Boolean?, | ||||
|     val isNotify: Boolean? | ||||
| ) | ||||
|   | ||||
| @@ -1,13 +1,16 @@ | ||||
| package kr.co.vividnext.sodalive.live.recommend | ||||
|  | ||||
| import com.querydsl.core.annotations.QueryProjection | ||||
|  | ||||
| data class GetCreatorFollowingAllListResponse( | ||||
|     val totalCount: Int, | ||||
|     val items: List<GetCreatorFollowingAllListItem> | ||||
| ) | ||||
|  | ||||
| data class GetCreatorFollowingAllListItem( | ||||
| data class GetCreatorFollowingAllListItem @QueryProjection constructor( | ||||
|     val creatorId: Long, | ||||
|     val nickname: String, | ||||
|     val profileImageUrl: String, | ||||
|     val isFollow: Boolean | ||||
|     val isFollow: Boolean, | ||||
|     val isNotify: Boolean | ||||
| ) | ||||
|   | ||||
| @@ -239,12 +239,12 @@ class LiveRecommendRepository( | ||||
|  | ||||
|         return queryFactory | ||||
|             .select( | ||||
|                 Projections.constructor( | ||||
|                     GetCreatorFollowingAllListItem::class.java, | ||||
|                 QGetCreatorFollowingAllListItem( | ||||
|                     member.id, | ||||
|                     member.nickname, | ||||
|                     member.profileImage.prepend("/").prepend(cloudFrontHost), | ||||
|                     Expressions.asBoolean(true) | ||||
|                     Expressions.asBoolean(true), | ||||
|                     creatorFollowing.isNotify | ||||
|                 ) | ||||
|             ) | ||||
|             .from(creatorFollowing) | ||||
|   | ||||
| @@ -162,7 +162,7 @@ class LiveRoomController( | ||||
|     ) = run { | ||||
|         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") | ||||
|  | ||||
|         ApiResponse.ok(service.getDonationTotal(roomId)) | ||||
|         ApiResponse.ok(service.getDonationTotal(roomId, memberId = member.id!!)) | ||||
|     } | ||||
|  | ||||
|     @PutMapping("/info/set/speaker") | ||||
| @@ -232,7 +232,7 @@ class LiveRoomController( | ||||
|     ) = run { | ||||
|         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") | ||||
|  | ||||
|         ApiResponse.ok(service.getDonationStatus(roomId, member)) | ||||
|         ApiResponse.ok(service.getDonationStatus(roomId, memberId = member.id!!)) | ||||
|     } | ||||
|  | ||||
|     @PostMapping("/quit") | ||||
|   | ||||
| @@ -11,6 +11,7 @@ import kr.co.vividnext.sodalive.live.room.QQuarterLiveRankings.quarterLiveRankin | ||||
| import kr.co.vividnext.sodalive.live.room.donation.GetLiveRoomDonationItem | ||||
| import kr.co.vividnext.sodalive.live.room.donation.QGetLiveRoomDonationItem | ||||
| import kr.co.vividnext.sodalive.member.QMember.member | ||||
| import org.springframework.beans.factory.annotation.Value | ||||
| import org.springframework.data.jpa.repository.JpaRepository | ||||
| import org.springframework.stereotype.Repository | ||||
| import java.time.LocalDateTime | ||||
| @@ -47,13 +48,18 @@ interface LiveRoomQueryRepository { | ||||
|  | ||||
|     fun getLiveRoom(id: Long): LiveRoom? | ||||
|     fun getLiveRoomAndAccountId(roomId: Long, memberId: Long): LiveRoom? | ||||
|     fun getRecentRoomInfo(memberId: Long, cloudFrontHost: String): GetRecentRoomInfoResponse? | ||||
|     fun getDonationTotal(roomId: Long): Int? | ||||
|     fun getDonationList(roomId: Long, cloudFrontHost: String): List<GetLiveRoomDonationItem> | ||||
|     fun getRecentRoomInfo(memberId: Long): GetRecentRoomInfoResponse? | ||||
|     fun getDonationTotal(roomId: Long, isLiveCreator: Boolean): Int? | ||||
|     fun getDonationList(roomId: Long, isLiveCreator: Boolean): List<GetLiveRoomDonationItem> | ||||
|     fun getRoomActiveAndChannelNameIsNotNull(memberId: Long): List<LiveRoom> | ||||
| } | ||||
|  | ||||
| class LiveRoomQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : LiveRoomQueryRepository { | ||||
| class LiveRoomQueryRepositoryImpl( | ||||
|     private val queryFactory: JPAQueryFactory, | ||||
|  | ||||
|     @Value("\${cloud.aws.cloud-front.host}") | ||||
|     private val cloudFrontHost: String | ||||
| ) : LiveRoomQueryRepository { | ||||
|     override fun getLiveRoomListNow( | ||||
|         offset: Long, | ||||
|         limit: Long, | ||||
| @@ -200,7 +206,7 @@ class LiveRoomQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : L | ||||
|             .fetchFirst() | ||||
|     } | ||||
|  | ||||
|     override fun getRecentRoomInfo(memberId: Long, cloudFrontHost: String): GetRecentRoomInfoResponse? { | ||||
|     override fun getRecentRoomInfo(memberId: Long): GetRecentRoomInfoResponse? { | ||||
|         return queryFactory | ||||
|             .select( | ||||
|                 Projections.constructor( | ||||
| @@ -219,22 +225,33 @@ class LiveRoomQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : L | ||||
|             .fetchFirst() | ||||
|     } | ||||
|  | ||||
|     override fun getDonationTotal(roomId: Long): Int? { | ||||
|     override fun getDonationTotal(roomId: Long, isLiveCreator: Boolean): Int? { | ||||
|         var where = liveRoom.id.eq(roomId) | ||||
|             .and(useCan.canUsage.eq(CanUsage.DONATION).or(useCan.canUsage.eq(CanUsage.SPIN_ROULETTE))) | ||||
|             .and(useCan.isRefund.isFalse) | ||||
|  | ||||
|         if (!isLiveCreator) { | ||||
|             where = where.and(useCan.isSecret.isFalse) | ||||
|         } | ||||
|  | ||||
|         return queryFactory | ||||
|             .select(useCanCalculate.can.sum()) | ||||
|             .from(useCanCalculate) | ||||
|             .innerJoin(useCanCalculate.useCan, useCan) | ||||
|             .innerJoin(useCan.room, liveRoom) | ||||
|             .where( | ||||
|                 liveRoom.id.eq(roomId) | ||||
|                     .and(useCan.canUsage.eq(CanUsage.DONATION).or(useCan.canUsage.eq(CanUsage.SPIN_ROULETTE))) | ||||
|                     .and(useCan.isRefund.isFalse) | ||||
|                     .and(useCan.isSecret.isFalse) | ||||
|             ) | ||||
|             .where(where) | ||||
|             .fetchOne() | ||||
|     } | ||||
|  | ||||
|     override fun getDonationList(roomId: Long, cloudFrontHost: String): List<GetLiveRoomDonationItem> { | ||||
|     override fun getDonationList(roomId: Long, isLiveCreator: Boolean): List<GetLiveRoomDonationItem> { | ||||
|         var where = liveRoom.id.eq(roomId) | ||||
|             .and(useCan.canUsage.eq(CanUsage.DONATION).or(useCan.canUsage.eq(CanUsage.SPIN_ROULETTE))) | ||||
|             .and(useCan.isRefund.isFalse) | ||||
|  | ||||
|         if (!isLiveCreator) { | ||||
|             where = where.and(useCan.isSecret.isFalse) | ||||
|         } | ||||
|  | ||||
|         return queryFactory | ||||
|             .select( | ||||
|                 QGetLiveRoomDonationItem( | ||||
| @@ -250,12 +267,7 @@ class LiveRoomQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : L | ||||
|             .from(useCan) | ||||
|             .join(useCan.member, member) | ||||
|             .groupBy(useCan.member) | ||||
|             .where( | ||||
|                 useCan.room.id.eq(roomId) | ||||
|                     .and(useCan.canUsage.eq(CanUsage.DONATION).or(useCan.canUsage.eq(CanUsage.SPIN_ROULETTE))) | ||||
|                     .and(useCan.isRefund.isFalse) | ||||
|                     .and(useCan.isSecret.isFalse) | ||||
|             ) | ||||
|             .where(where) | ||||
|             .orderBy(useCan.can.sum().add(useCan.rewardCan.sum()).desc()) | ||||
|             .fetch() | ||||
|     } | ||||
|   | ||||
| @@ -690,7 +690,7 @@ class LiveRoomService( | ||||
|     } | ||||
|  | ||||
|     fun getRecentRoomInfo(member: Member): GetRecentRoomInfoResponse { | ||||
|         return repository.getRecentRoomInfo(memberId = member.id!!, cloudFrontHost = cloudFrontHost) | ||||
|         return repository.getRecentRoomInfo(memberId = member.id!!) | ||||
|             ?: throw SodaException("최근 데이터가 없습니다.") | ||||
|     } | ||||
|  | ||||
| @@ -965,9 +965,12 @@ class LiveRoomService( | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     fun getDonationTotal(roomId: Long): GetLiveRoomDonationTotalResponse { | ||||
|     fun getDonationTotal(roomId: Long, memberId: Long): GetLiveRoomDonationTotalResponse { | ||||
|         val room = repository.getLiveRoom(roomId) | ||||
|             ?: return GetLiveRoomDonationTotalResponse(0) | ||||
|         val isLiveCreator = room.member!!.id == memberId | ||||
|         return GetLiveRoomDonationTotalResponse( | ||||
|             totalDonationCan = repository.getDonationTotal(roomId = roomId) ?: 0 | ||||
|             totalDonationCan = repository.getDonationTotal(roomId = roomId, isLiveCreator = isLiveCreator) ?: 0 | ||||
|         ) | ||||
|     } | ||||
|  | ||||
| @@ -1166,9 +1169,10 @@ class LiveRoomService( | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun getDonationStatus(roomId: Long, member: Member): GetLiveRoomDonationStatusResponse { | ||||
|     fun getDonationStatus(roomId: Long, memberId: Long): GetLiveRoomDonationStatusResponse { | ||||
|         val room = repository.getLiveRoom(roomId) ?: throw SodaException("잘못된 요청입니다.") | ||||
|         val donationList = repository.getDonationList(roomId = room.id!!, cloudFrontHost = cloudFrontHost) | ||||
|         val isLiveCreator = room.member!!.id == memberId | ||||
|         val donationList = repository.getDonationList(roomId = room.id!!, isLiveCreator = isLiveCreator) | ||||
|         val totalCan = donationList.sumOf { it.can } | ||||
|  | ||||
|         return GetLiveRoomDonationStatusResponse( | ||||
|   | ||||
| @@ -142,7 +142,14 @@ class MemberController(private val service: MemberService) { | ||||
|     ) = run { | ||||
|         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") | ||||
|  | ||||
|         ApiResponse.ok(service.creatorFollow(creatorId = request.creatorId, memberId = member.id!!)) | ||||
|         ApiResponse.ok( | ||||
|             service.creatorFollow( | ||||
|                 creatorId = request.creatorId, | ||||
|                 isNotify = request.isNotify ?: true, | ||||
|                 isActive = request.isActive ?: true, | ||||
|                 memberId = member.id!! | ||||
|             ) | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     @PostMapping("/creator/unfollow") | ||||
|   | ||||
| @@ -146,6 +146,7 @@ class MemberQueryRepositoryImpl( | ||||
|                 ) | ||||
|             ) | ||||
|             .and(creatorFollowing.member.pushToken.isNotNull) | ||||
|             .and(creatorFollowing.isNotify.isTrue) | ||||
|             .or(member.id.eq(4).and(member.pushToken.isNotNull)) | ||||
|  | ||||
|         if (isAuth) { | ||||
| @@ -190,6 +191,7 @@ class MemberQueryRepositoryImpl( | ||||
|                 ) | ||||
|             ) | ||||
|             .and(creatorFollowing.member.pushToken.isNotNull) | ||||
|             .and(creatorFollowing.isNotify.isTrue) | ||||
|             .or(creatorFollowing.member.id.eq(4).and(member.pushToken.isNotNull)) | ||||
|  | ||||
|         if (isAuth) { | ||||
| @@ -259,6 +261,7 @@ class MemberQueryRepositoryImpl( | ||||
|                 ) | ||||
|             ) | ||||
|             .and(creatorFollowing.member.pushToken.isNotNull) | ||||
|             .and(creatorFollowing.isNotify.isTrue) | ||||
|             .or(member.id.eq(4).and(member.pushToken.isNotNull)) | ||||
|  | ||||
|         if (isAuth) { | ||||
| @@ -372,6 +375,7 @@ class MemberQueryRepositoryImpl( | ||||
|                 ) | ||||
|             ) | ||||
|             .and(creatorFollowing.member.pushToken.isNotNull) | ||||
|             .and(creatorFollowing.isNotify.isTrue) | ||||
|  | ||||
|         val aosPushTokens = queryFactory | ||||
|             .select(creatorFollowing.member.pushToken) | ||||
|   | ||||
| @@ -331,7 +331,7 @@ class MemberService( | ||||
|     } | ||||
|  | ||||
|     @Transactional | ||||
|     fun creatorFollow(creatorId: Long, memberId: Long) { | ||||
|     fun creatorFollow(creatorId: Long, isNotify: Boolean, isActive: Boolean, memberId: Long) { | ||||
|         val creatorFollowing = creatorFollowingRepository.findByCreatorIdAndMemberId( | ||||
|             creatorId = creatorId, | ||||
|             memberId = memberId | ||||
| @@ -340,9 +340,14 @@ class MemberService( | ||||
|         if (creatorFollowing == null) { | ||||
|             val creator = repository.findByIdOrNull(creatorId) ?: throw SodaException("크리에이터 정보를 확인해주세요.") | ||||
|             val member = repository.findByIdOrNull(memberId) ?: throw SodaException("로그인 정보를 확인해주세요.") | ||||
|             creatorFollowingRepository.save(CreatorFollowing(creator = creator, member = member)) | ||||
|  | ||||
|             val newCreatorFollowing = CreatorFollowing() | ||||
|             newCreatorFollowing.member = member | ||||
|             newCreatorFollowing.creator = creator | ||||
|             creatorFollowingRepository.save(newCreatorFollowing) | ||||
|         } else { | ||||
|             creatorFollowing.isActive = true | ||||
|             creatorFollowing.isNotify = isNotify && isActive | ||||
|             creatorFollowing.isActive = isActive | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,3 +1,7 @@ | ||||
| package kr.co.vividnext.sodalive.member.following | ||||
|  | ||||
| data class CreatorFollowRequest(val creatorId: Long) | ||||
| data class CreatorFollowRequest( | ||||
|     val creatorId: Long, | ||||
|     val isNotify: Boolean?, | ||||
|     val isActive: Boolean? | ||||
| ) | ||||
|   | ||||
| @@ -8,16 +8,17 @@ import javax.persistence.JoinColumn | ||||
| import javax.persistence.ManyToOne | ||||
|  | ||||
| @Entity | ||||
| class CreatorFollowing( | ||||
| data class CreatorFollowing( | ||||
|     var isNotify: Boolean = true, | ||||
|     var isActive: Boolean = true | ||||
| ) : BaseEntity() { | ||||
|     // 유저가 알림받기 한 크리에이터 | ||||
|     @ManyToOne(fetch = FetchType.LAZY) | ||||
|     @JoinColumn(name = "creator_id", nullable = false) | ||||
|     var creator: Member, | ||||
|     var creator: Member? = null | ||||
|  | ||||
|     // 크리에이터를 알림받기 한 유저 | ||||
|     @ManyToOne(fetch = FetchType.LAZY) | ||||
|     @JoinColumn(name = "member_id", nullable = false) | ||||
|     var member: Member, | ||||
|  | ||||
|     var isActive: Boolean = true | ||||
| ) : BaseEntity() | ||||
|     var member: Member? = null | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user