From a28991b5859c8760290142f17197ff39ad381e61 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 25 Jun 2026 22:15:20 +0900 Subject: [PATCH] =?UTF-8?q?feat(home-following):=20=ED=8C=94=EB=A1=9C?= =?UTF-8?q?=EC=9E=89=20=EC=86=8C=EC=8B=9D=20inbox=20=EC=A0=80=EC=9E=A5=20?= =?UTF-8?q?=EB=AA=A8=EB=8D=B8=EC=9D=84=20=EC=B6=94=EA=B0=80=ED=95=9C?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../out/persistence/HomeFollowingNewsInbox.kt | 73 +++++++++++++++++++ .../HomeFollowingNewsInboxJpaRepository.kt | 43 +++++++++++ .../port/out/HomeFollowingNewsInboxPort.kt | 26 +++++++ 3 files changed, 142 insertions(+) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/v2/home/following/adapter/out/persistence/HomeFollowingNewsInbox.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/v2/home/following/adapter/out/persistence/HomeFollowingNewsInboxJpaRepository.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/v2/home/following/port/out/HomeFollowingNewsInboxPort.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/home/following/adapter/out/persistence/HomeFollowingNewsInbox.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/home/following/adapter/out/persistence/HomeFollowingNewsInbox.kt new file mode 100644 index 00000000..a56ed0ab --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/home/following/adapter/out/persistence/HomeFollowingNewsInbox.kt @@ -0,0 +1,73 @@ +package kr.co.vividnext.sodalive.v2.home.following.adapter.out.persistence + +import kr.co.vividnext.sodalive.common.BaseEntity +import kr.co.vividnext.sodalive.v2.home.following.domain.FollowingNewsType +import java.time.LocalDateTime +import javax.persistence.Column +import javax.persistence.Entity +import javax.persistence.EnumType +import javax.persistence.Enumerated +import javax.persistence.Table +import javax.persistence.UniqueConstraint + +@Entity +@Table( + name = "home_following_news_inbox", + uniqueConstraints = [ + UniqueConstraint( + name = "uk_home_following_news_inbox_member_type_source", + columnNames = ["member_id", "news_type", "source_key"] + ) + ] +) +class HomeFollowingNewsInbox( + @Column(name = "member_id", nullable = false, updatable = false) + val memberId: Long, + + @Column(name = "creator_id", nullable = false, updatable = false) + val creatorId: Long, + + @Enumerated(EnumType.STRING) + @Column(name = "news_type", nullable = false, updatable = false, length = 30) + val newsType: FollowingNewsType, + + @Column(name = "source_key", nullable = false, updatable = false, length = 200) + val sourceKey: String, + + @Column(name = "target_id", nullable = false, updatable = false) + val targetId: Long, + + @Column(name = "occurred_at_utc", nullable = false, updatable = false) + val occurredAtUtc: LocalDateTime, + + @Column(name = "visible_from_at_utc", nullable = false, updatable = false) + val visibleFromAtUtc: LocalDateTime, + + @Column(name = "creator_nickname", nullable = false, updatable = false, length = 100) + val creatorNickname: String, + + @Column(name = "creator_profile_image_path", updatable = false, length = 500) + val creatorProfileImagePath: String?, + + @Column(name = "title", nullable = false, updatable = false, length = 255) + val title: String, + + @Column(name = "body", nullable = false, updatable = false, length = 1000) + val body: String, + + @Column(name = "thumbnail_image_path", updatable = false, length = 500) + val thumbnailImagePath: String?, + + @Column(name = "rank_no", updatable = false) + val rank: Int?, + + @Column(name = "is_adult", nullable = false, updatable = false) + val isAdult: Boolean, + + @Column(name = "is_active", nullable = false) + var isActive: Boolean = true +) : BaseEntity() { + fun deactivate() { + isActive = false + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/home/following/adapter/out/persistence/HomeFollowingNewsInboxJpaRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/home/following/adapter/out/persistence/HomeFollowingNewsInboxJpaRepository.kt new file mode 100644 index 00000000..4d75d52d --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/home/following/adapter/out/persistence/HomeFollowingNewsInboxJpaRepository.kt @@ -0,0 +1,43 @@ +package kr.co.vividnext.sodalive.v2.home.following.adapter.out.persistence + +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Modifying +import org.springframework.data.jpa.repository.Query +import org.springframework.data.repository.query.Param + +interface HomeFollowingNewsInboxJpaRepository : JpaRepository { + fun existsByMemberIdAndNewsTypeAndSourceKey( + memberId: Long, + newsType: kr.co.vividnext.sodalive.v2.home.following.domain.FollowingNewsType, + sourceKey: String + ): Boolean + + @Modifying(clearAutomatically = true, flushAutomatically = true) + @Query( + value = """ + update home_following_news_inbox + set is_active = false, + updated_at = current_timestamp + where member_id = :memberId + and creator_id = :creatorId + and is_active = true + """, + nativeQuery = true + ) + fun deactivateByMemberIdAndCreatorId( + @Param("memberId") memberId: Long, + @Param("creatorId") creatorId: Long + ): Int + + @Query( + value = """ + select cf.member_id + from creator_following cf + where cf.creator_id = :creatorId + and cf.is_active = true + order by cf.member_id asc + """, + nativeQuery = true + ) + fun findActiveFollowerIds(@Param("creatorId") creatorId: Long): List +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/home/following/port/out/HomeFollowingNewsInboxPort.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/home/following/port/out/HomeFollowingNewsInboxPort.kt new file mode 100644 index 00000000..1091d88c --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/home/following/port/out/HomeFollowingNewsInboxPort.kt @@ -0,0 +1,26 @@ +package kr.co.vividnext.sodalive.v2.home.following.port.out + +import java.time.LocalDateTime + +interface HomeFollowingNewsInboxPort { + fun insertIgnoreAll(records: List): Int + fun deactivateByMemberIdAndCreatorId(memberId: Long, creatorId: Long): Long + fun findActiveFollowerIds(creatorId: Long): List +} + +data class HomeFollowingNewsInboxRecord( + val memberId: Long, + val creatorId: Long, + val newsType: String, + val sourceKey: String, + val targetId: Long, + val occurredAtUtc: LocalDateTime, + val visibleFromAtUtc: LocalDateTime, + val creatorNickname: String, + val creatorProfileImagePath: String?, + val title: String, + val body: String, + val thumbnailImagePath: String?, + val rank: Int?, + val isAdult: Boolean +)