diff --git a/src/main/kotlin/team/cklob/mudda/MuddaApplication.kt b/src/main/kotlin/team/cklob/mudda/MuddaApplication.kt index 06cf583..1173478 100644 --- a/src/main/kotlin/team/cklob/mudda/MuddaApplication.kt +++ b/src/main/kotlin/team/cklob/mudda/MuddaApplication.kt @@ -2,7 +2,9 @@ package team.cklob.mudda import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication +import org.springframework.data.jpa.repository.config.EnableJpaAuditing +@EnableJpaAuditing @SpringBootApplication class MuddaApplication diff --git a/src/main/kotlin/team/cklob/mudda/domain/block/domain/entity/Block.kt b/src/main/kotlin/team/cklob/mudda/domain/block/domain/entity/Block.kt new file mode 100644 index 0000000..cdb7165 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/block/domain/entity/Block.kt @@ -0,0 +1,38 @@ +package team.cklob.mudda.domain.block.domain.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.Table +import jakarta.persistence.UniqueConstraint +import team.cklob.mudda.domain.member.domain.entity.Member +import team.cklob.mudda.global.common.entity.BaseCreatedAtEntity + +@Entity +@Table( + name = "tbl_block", + uniqueConstraints = [ + UniqueConstraint( + name = "uq_block_blocker_blocked", + columnNames = ["blocker_id", "blocked_id"], + ), + ], +) +class Block( + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "blocker_id", nullable = false) + val blocker: Member, + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "blocked_id", nullable = false) + val blocked: Member, + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, +) : BaseCreatedAtEntity() diff --git a/src/main/kotlin/team/cklob/mudda/domain/block/domain/repository/BlockRepository.kt b/src/main/kotlin/team/cklob/mudda/domain/block/domain/repository/BlockRepository.kt new file mode 100644 index 0000000..982d782 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/block/domain/repository/BlockRepository.kt @@ -0,0 +1,9 @@ +package team.cklob.mudda.domain.block.domain.repository + +import org.springframework.data.jpa.repository.JpaRepository +import team.cklob.mudda.domain.block.domain.entity.Block + +interface BlockRepository : JpaRepository { + fun existsByBlockerIdAndBlockedId(blockerId: Long, blockedId: Long): Boolean + fun findByBlockerId(blockerId: Long): List +} diff --git a/src/main/kotlin/team/cklob/mudda/domain/friend/domain/entity/Friend.kt b/src/main/kotlin/team/cklob/mudda/domain/friend/domain/entity/Friend.kt new file mode 100644 index 0000000..75b7595 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/friend/domain/entity/Friend.kt @@ -0,0 +1,49 @@ +package team.cklob.mudda.domain.friend.domain.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.EnumType +import jakarta.persistence.Enumerated +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.Table +import jakarta.persistence.UniqueConstraint +import team.cklob.mudda.domain.friend.domain.type.FriendRequestStatus +import team.cklob.mudda.domain.member.domain.entity.Member +import team.cklob.mudda.global.common.entity.BaseCreatedAtEntity +import java.time.LocalDateTime + +@Entity +@Table( + name = "tbl_friend", + uniqueConstraints = [ + UniqueConstraint( + name = "uq_friend_requester_receiver", + columnNames = ["requester_id", "receiver_id"], + ), + ], +) +class Friend( + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "requester_id", nullable = false) + val requester: Member, + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "receiver_id", nullable = false) + val receiver: Member, + + @Enumerated(EnumType.STRING) + @Column(nullable = false, length = 20) + var status: FriendRequestStatus, + + @Column(name = "accepted_at") + var acceptedAt: LocalDateTime? = null, + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, +) : BaseCreatedAtEntity() diff --git a/src/main/kotlin/team/cklob/mudda/domain/friend/domain/repository/FriendRepository.kt b/src/main/kotlin/team/cklob/mudda/domain/friend/domain/repository/FriendRepository.kt new file mode 100644 index 0000000..54d59d4 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/friend/domain/repository/FriendRepository.kt @@ -0,0 +1,11 @@ +package team.cklob.mudda.domain.friend.domain.repository + +import org.springframework.data.jpa.repository.JpaRepository +import team.cklob.mudda.domain.friend.domain.entity.Friend +import java.util.Optional + +interface FriendRepository : JpaRepository { + fun findByRequesterIdOrReceiverId(requesterId: Long, receiverId: Long): List + fun findByRequesterIdAndReceiverId(requesterId: Long, receiverId: Long): Optional + fun existsByRequesterIdAndReceiverId(requesterId: Long, receiverId: Long): Boolean +} diff --git a/src/main/kotlin/team/cklob/mudda/domain/friend/domain/type/FriendRequestStatus.kt b/src/main/kotlin/team/cklob/mudda/domain/friend/domain/type/FriendRequestStatus.kt new file mode 100644 index 0000000..5f23284 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/friend/domain/type/FriendRequestStatus.kt @@ -0,0 +1,7 @@ +package team.cklob.mudda.domain.friend.domain.type + +enum class FriendRequestStatus { + PENDING, + ACCEPTED, + REJECTED, +} diff --git a/src/main/kotlin/team/cklob/mudda/domain/friend/domain/type/FriendRequestType.kt b/src/main/kotlin/team/cklob/mudda/domain/friend/domain/type/FriendRequestType.kt new file mode 100644 index 0000000..decdf88 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/friend/domain/type/FriendRequestType.kt @@ -0,0 +1,6 @@ +package team.cklob.mudda.domain.friend.domain.type + +enum class FriendRequestType { + RECEIVED, + SENT, +} diff --git a/src/main/kotlin/team/cklob/mudda/domain/friend/domain/type/FriendStatus.kt b/src/main/kotlin/team/cklob/mudda/domain/friend/domain/type/FriendStatus.kt new file mode 100644 index 0000000..4e8bff9 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/friend/domain/type/FriendStatus.kt @@ -0,0 +1,8 @@ +package team.cklob.mudda.domain.friend.domain.type + +enum class FriendStatus { + NONE, + FRIEND, + REQUESTED, + RECEIVED, +} diff --git a/src/main/kotlin/team/cklob/mudda/domain/media/domain/entity/Media.kt b/src/main/kotlin/team/cklob/mudda/domain/media/domain/entity/Media.kt new file mode 100644 index 0000000..e291822 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/media/domain/entity/Media.kt @@ -0,0 +1,38 @@ +package team.cklob.mudda.domain.media.domain.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.EnumType +import jakarta.persistence.Enumerated +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.Table +import team.cklob.mudda.domain.media.domain.type.MediaType +import team.cklob.mudda.domain.timecapsule.domain.entity.TimeCapsule +import team.cklob.mudda.global.common.entity.BaseCreatedAtEntity + +@Entity +@Table(name = "tbl_media") +class Media( + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "time_capsule_id", nullable = false) + val timeCapsule: TimeCapsule, + + @Enumerated(EnumType.STRING) + @Column(name = "media_type", nullable = false, length = 20) + val mediaType: MediaType, + + @Column(name = "media_url", nullable = false, length = 255) + val mediaUrl: String, + + @Column(name = "s3_key", nullable = false, length = 255) + val s3Key: String, + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, +) : BaseCreatedAtEntity() diff --git a/src/main/kotlin/team/cklob/mudda/domain/media/domain/repository/MediaRepository.kt b/src/main/kotlin/team/cklob/mudda/domain/media/domain/repository/MediaRepository.kt new file mode 100644 index 0000000..8264d89 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/media/domain/repository/MediaRepository.kt @@ -0,0 +1,6 @@ +package team.cklob.mudda.domain.media.domain.repository + +import org.springframework.data.jpa.repository.JpaRepository +import team.cklob.mudda.domain.media.domain.entity.Media + +interface MediaRepository : JpaRepository diff --git a/src/main/kotlin/team/cklob/mudda/domain/media/domain/type/MediaTargetType.kt b/src/main/kotlin/team/cklob/mudda/domain/media/domain/type/MediaTargetType.kt new file mode 100644 index 0000000..1431cde --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/media/domain/type/MediaTargetType.kt @@ -0,0 +1,6 @@ +package team.cklob.mudda.domain.media.domain.type + +enum class MediaTargetType { + PROFILE, + CAPSULE, +} diff --git a/src/main/kotlin/team/cklob/mudda/domain/media/domain/type/MediaType.kt b/src/main/kotlin/team/cklob/mudda/domain/media/domain/type/MediaType.kt new file mode 100644 index 0000000..3935c25 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/media/domain/type/MediaType.kt @@ -0,0 +1,7 @@ +package team.cklob.mudda.domain.media.domain.type + +enum class MediaType { + IMAGE, + VIDEO, + VOICE, +} diff --git a/src/main/kotlin/team/cklob/mudda/domain/member/domain/entity/Member.kt b/src/main/kotlin/team/cklob/mudda/domain/member/domain/entity/Member.kt new file mode 100644 index 0000000..07b1cef --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/member/domain/entity/Member.kt @@ -0,0 +1,35 @@ +package team.cklob.mudda.domain.member.domain.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.Table +import team.cklob.mudda.global.common.entity.BaseTimeEntity + +@Entity +@Table(name = "tbl_member") +class Member( + @Column(nullable = false, length = 30) + var name: String, + + @Column(nullable = false, unique = true, length = 30) + var nickname: String, + + @Column(nullable = false, unique = true, length = 255) + val email: String, + + @Column(name = "profile_image_url", length = 255) + var profileImageUrl: String? = null, + + @Column(length = 100) + var bio: String? = null, + + @Column(name = "profile_visibility", nullable = false, length = 20) + var profileVisibility: String, + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, +) : BaseTimeEntity() diff --git a/src/main/kotlin/team/cklob/mudda/domain/member/domain/repository/MemberRepository.kt b/src/main/kotlin/team/cklob/mudda/domain/member/domain/repository/MemberRepository.kt new file mode 100644 index 0000000..ee87ca2 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/member/domain/repository/MemberRepository.kt @@ -0,0 +1,12 @@ +package team.cklob.mudda.domain.member.domain.repository + +import org.springframework.data.jpa.repository.JpaRepository +import team.cklob.mudda.domain.member.domain.entity.Member +import java.util.Optional + +interface MemberRepository : JpaRepository { + fun existsByEmail(email: String): Boolean + fun existsByNickname(nickname: String): Boolean + fun findByEmail(email: String): Optional + fun findByNickname(nickname: String): Optional +} diff --git a/src/main/kotlin/team/cklob/mudda/domain/member/domain/type/Gender.kt b/src/main/kotlin/team/cklob/mudda/domain/member/domain/type/Gender.kt new file mode 100644 index 0000000..c9f17df --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/member/domain/type/Gender.kt @@ -0,0 +1,6 @@ +package team.cklob.mudda.domain.member.domain.type + +enum class Gender { + MALE, + FEMALE, +} diff --git a/src/main/kotlin/team/cklob/mudda/domain/member/domain/type/OAuthProvider.kt b/src/main/kotlin/team/cklob/mudda/domain/member/domain/type/OAuthProvider.kt new file mode 100644 index 0000000..38cd3c5 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/member/domain/type/OAuthProvider.kt @@ -0,0 +1,7 @@ +package team.cklob.mudda.domain.member.domain.type + +enum class OAuthProvider { + KAKAO, + GOOGLE, + APPLE, +} diff --git a/src/main/kotlin/team/cklob/mudda/domain/notification/domain/entity/Notification.kt b/src/main/kotlin/team/cklob/mudda/domain/notification/domain/entity/Notification.kt new file mode 100644 index 0000000..2cdf764 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/notification/domain/entity/Notification.kt @@ -0,0 +1,46 @@ +package team.cklob.mudda.domain.notification.domain.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.EnumType +import jakarta.persistence.Enumerated +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.Table +import team.cklob.mudda.domain.member.domain.entity.Member +import team.cklob.mudda.domain.notification.domain.type.NotificationType +import team.cklob.mudda.domain.timecapsule.domain.entity.TimeCapsule +import team.cklob.mudda.global.common.entity.BaseCreatedAtEntity + +@Entity +@Table(name = "tbl_notification") +class Notification( + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "recipient_id", nullable = false) + val recipient: Member, + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "time_capsule_id") + val timeCapsule: TimeCapsule? = null, + + @Enumerated(EnumType.STRING) + @Column(name = "notification_type", nullable = false, length = 30) + val notificationType: NotificationType, + + @Column(nullable = false, length = 255) + val title: String, + + @Column(nullable = false, length = 255) + val body: String, + + @Column(name = "is_read", nullable = false) + var isRead: Boolean = false, + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, +) : BaseCreatedAtEntity() diff --git a/src/main/kotlin/team/cklob/mudda/domain/notification/domain/repository/NotificationRepository.kt b/src/main/kotlin/team/cklob/mudda/domain/notification/domain/repository/NotificationRepository.kt new file mode 100644 index 0000000..095f6f0 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/notification/domain/repository/NotificationRepository.kt @@ -0,0 +1,9 @@ +package team.cklob.mudda.domain.notification.domain.repository + +import org.springframework.data.jpa.repository.JpaRepository +import team.cklob.mudda.domain.notification.domain.entity.Notification + +interface NotificationRepository : JpaRepository { + fun findByRecipientIdOrderByCreatedAtDesc(recipientId: Long): List + fun findByRecipientIdAndIsReadFalseOrderByCreatedAtDesc(recipientId: Long): List +} diff --git a/src/main/kotlin/team/cklob/mudda/domain/notification/domain/type/NotificationTargetType.kt b/src/main/kotlin/team/cklob/mudda/domain/notification/domain/type/NotificationTargetType.kt new file mode 100644 index 0000000..f238276 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/notification/domain/type/NotificationTargetType.kt @@ -0,0 +1,7 @@ +package team.cklob.mudda.domain.notification.domain.type + +enum class NotificationTargetType { + CAPSULE, + MEMBER, + FRIEND_REQUEST, +} diff --git a/src/main/kotlin/team/cklob/mudda/domain/notification/domain/type/NotificationType.kt b/src/main/kotlin/team/cklob/mudda/domain/notification/domain/type/NotificationType.kt new file mode 100644 index 0000000..0290892 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/notification/domain/type/NotificationType.kt @@ -0,0 +1,8 @@ +package team.cklob.mudda.domain.notification.domain.type + +enum class NotificationType { + CAPSULE_OPENED, + CAPSULE_RECEIVED, + FRIEND_REQUESTED, + FRIEND_ACCEPTED, +} diff --git a/src/main/kotlin/team/cklob/mudda/domain/report/domain/entity/Report.kt b/src/main/kotlin/team/cklob/mudda/domain/report/domain/entity/Report.kt new file mode 100644 index 0000000..7e727ff --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/report/domain/entity/Report.kt @@ -0,0 +1,46 @@ +package team.cklob.mudda.domain.report.domain.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.Table +import jakarta.persistence.UniqueConstraint +import team.cklob.mudda.domain.member.domain.entity.Member +import team.cklob.mudda.global.common.entity.BaseCreatedAtEntity + +@Entity +@Table( + name = "tbl_report", + uniqueConstraints = [ + UniqueConstraint( + name = "uq_report_reporter_target", + columnNames = ["reporter_id", "target_type", "target_id"], + ), + ], +) +class Report( + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "reporter_id", nullable = false) + val reporter: Member, + + @Column(name = "target_type", nullable = false, length = 30) + val targetType: String, + + @Column(name = "target_id", nullable = false) + val targetId: Long, + + @Column(nullable = false, length = 30) + val reason: String, + + @Column(length = 500) + val description: String? = null, + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, +) : BaseCreatedAtEntity() diff --git a/src/main/kotlin/team/cklob/mudda/domain/report/domain/repository/ReportRepository.kt b/src/main/kotlin/team/cklob/mudda/domain/report/domain/repository/ReportRepository.kt new file mode 100644 index 0000000..44eb2db --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/report/domain/repository/ReportRepository.kt @@ -0,0 +1,12 @@ +package team.cklob.mudda.domain.report.domain.repository + +import org.springframework.data.jpa.repository.JpaRepository +import team.cklob.mudda.domain.report.domain.entity.Report + +interface ReportRepository : JpaRepository { + fun existsByReporterIdAndTargetTypeAndTargetId( + reporterId: Long, + targetType: String, + targetId: Long, + ): Boolean +} diff --git a/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/entity/CapsuleHistory.kt b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/entity/CapsuleHistory.kt new file mode 100644 index 0000000..9c57ddd --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/entity/CapsuleHistory.kt @@ -0,0 +1,32 @@ +package team.cklob.mudda.domain.timecapsule.domain.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.Table +import team.cklob.mudda.domain.member.domain.entity.Member +import team.cklob.mudda.global.common.entity.BaseCreatedAtEntity + +@Entity +@Table(name = "tbl_capsule_history") +class CapsuleHistory( + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "time_capsule_id", nullable = false) + val timeCapsule: TimeCapsule, + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + val member: Member, + + @Column(name = "event_type", nullable = false, length = 20) + val eventType: String, + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, +) : BaseCreatedAtEntity() diff --git a/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/entity/CapsuleOpen.kt b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/entity/CapsuleOpen.kt new file mode 100644 index 0000000..8c34e8a --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/entity/CapsuleOpen.kt @@ -0,0 +1,45 @@ +package team.cklob.mudda.domain.timecapsule.domain.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.Table +import jakarta.persistence.UniqueConstraint +import org.locationtech.jts.geom.Point +import team.cklob.mudda.domain.member.domain.entity.Member +import java.time.LocalDateTime + +@Entity +@Table( + name = "tbl_capsule_open", + uniqueConstraints = [ + UniqueConstraint( + name = "uq_capsule_open_time_capsule_member", + columnNames = ["time_capsule_id", "member_id"], + ), + ], +) +class CapsuleOpen( + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "time_capsule_id", nullable = false) + val timeCapsule: TimeCapsule, + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + val member: Member, + + @Column(name = "opened_at", nullable = false) + val openedAt: LocalDateTime, + + @Column(name = "open_location", columnDefinition = "geometry(Point,4326)") + val openLocation: Point? = null, + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, +) diff --git a/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/entity/CapsuleRecipient.kt b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/entity/CapsuleRecipient.kt new file mode 100644 index 0000000..b029aef --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/entity/CapsuleRecipient.kt @@ -0,0 +1,45 @@ +package team.cklob.mudda.domain.timecapsule.domain.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.Table +import jakarta.persistence.UniqueConstraint +import team.cklob.mudda.domain.member.domain.entity.Member +import team.cklob.mudda.global.common.entity.BaseCreatedAtEntity +import java.time.LocalDateTime + +@Entity +@Table( + name = "tbl_capsule_recipient", + uniqueConstraints = [ + UniqueConstraint( + name = "uq_capsule_recipient_time_capsule_member", + columnNames = ["time_capsule_id", "member_id"], + ), + ], +) +class CapsuleRecipient( + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + val member: Member, + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "time_capsule_id", nullable = false) + val timeCapsule: TimeCapsule, + + @Column(name = "has_opened", nullable = false) + var hasOpened: Boolean = false, + + @Column(name = "opened_at") + var openedAt: LocalDateTime? = null, + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, +) : BaseCreatedAtEntity() diff --git a/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/entity/Guestbook.kt b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/entity/Guestbook.kt new file mode 100644 index 0000000..528627a --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/entity/Guestbook.kt @@ -0,0 +1,37 @@ +package team.cklob.mudda.domain.timecapsule.domain.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.Lob +import jakarta.persistence.ManyToOne +import jakarta.persistence.Table +import team.cklob.mudda.domain.member.domain.entity.Member +import team.cklob.mudda.global.common.entity.BaseCreatedAtEntity + +@Entity +@Table(name = "tbl_guestbook") +class Guestbook( + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "time_capsule_id", nullable = false) + val timeCapsule: TimeCapsule, + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + val member: Member, + + @Lob + @Column(nullable = false, columnDefinition = "TEXT") + val content: String, + + @Column(name = "is_deleted", nullable = false) + var isDeleted: Boolean = false, + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, +) : BaseCreatedAtEntity() diff --git a/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/entity/TimeCapsule.kt b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/entity/TimeCapsule.kt new file mode 100644 index 0000000..11af3d5 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/entity/TimeCapsule.kt @@ -0,0 +1,80 @@ +package team.cklob.mudda.domain.timecapsule.domain.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.EnumType +import jakarta.persistence.Enumerated +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.Lob +import jakarta.persistence.ManyToOne +import jakarta.persistence.Table +import org.locationtech.jts.geom.Point +import team.cklob.mudda.domain.member.domain.entity.Member +import team.cklob.mudda.domain.timecapsule.domain.type.CapsuleLockType +import team.cklob.mudda.domain.timecapsule.domain.type.CapsuleVisibility +import team.cklob.mudda.global.common.entity.BaseTimeEntity +import java.time.LocalDateTime + +@Entity +@Table(name = "tbl_time_capsule") +class TimeCapsule( + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + val member: Member, + + @Column(nullable = false, length = 255) + val name: String, + + @Lob + @Column(columnDefinition = "TEXT") + val content: String? = null, + + @Column(name = "time_capsule_type", nullable = false, length = 20) + val timeCapsuleType: String, + + @Enumerated(EnumType.STRING) + @Column(nullable = false, length = 20) + val visibility: CapsuleVisibility, + + @Enumerated(EnumType.STRING) + @Column(name = "lock_type", nullable = false, length = 20) + val lockType: CapsuleLockType, + + @Column(name = "password_hash", length = 255) + val passwordHash: String? = null, + + @Column(length = 255) + val question: String? = null, + + @Column(name = "answer_hash", length = 255) + val answerHash: String? = null, + + @Column(nullable = false, columnDefinition = "geometry(Point,4326)") + val location: Point, + + @Column(name = "location_name", length = 255) + val locationName: String? = null, + + @Column(name = "open_radius_meter", nullable = false) + val openRadiusMeter: Int, + + @Column(name = "expires_at", nullable = false) + val expiresAt: LocalDateTime, + + @Column(name = "is_anonymous", nullable = false) + val isAnonymous: Boolean, + + @Column(name = "is_feed_public", nullable = false) + val isFeedPublic: Boolean, + + @Column(name = "is_deleted", nullable = false) + var isDeleted: Boolean = false, + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, +) : BaseTimeEntity() diff --git a/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/repository/CapsuleHistoryRepository.kt b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/repository/CapsuleHistoryRepository.kt new file mode 100644 index 0000000..1029d54 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/repository/CapsuleHistoryRepository.kt @@ -0,0 +1,6 @@ +package team.cklob.mudda.domain.timecapsule.domain.repository + +import org.springframework.data.jpa.repository.JpaRepository +import team.cklob.mudda.domain.timecapsule.domain.entity.CapsuleHistory + +interface CapsuleHistoryRepository : JpaRepository diff --git a/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/repository/CapsuleOpenRepository.kt b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/repository/CapsuleOpenRepository.kt new file mode 100644 index 0000000..8b3f8de --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/repository/CapsuleOpenRepository.kt @@ -0,0 +1,10 @@ +package team.cklob.mudda.domain.timecapsule.domain.repository + +import org.springframework.data.jpa.repository.JpaRepository +import team.cklob.mudda.domain.timecapsule.domain.entity.CapsuleOpen +import java.util.Optional + +interface CapsuleOpenRepository : JpaRepository { + fun findByTimeCapsuleIdAndMemberId(timeCapsuleId: Long, memberId: Long): Optional + fun existsByTimeCapsuleIdAndMemberId(timeCapsuleId: Long, memberId: Long): Boolean +} diff --git a/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/repository/CapsuleRecipientRepository.kt b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/repository/CapsuleRecipientRepository.kt new file mode 100644 index 0000000..3b46af4 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/repository/CapsuleRecipientRepository.kt @@ -0,0 +1,11 @@ +package team.cklob.mudda.domain.timecapsule.domain.repository + +import org.springframework.data.jpa.repository.JpaRepository +import team.cklob.mudda.domain.timecapsule.domain.entity.CapsuleRecipient +import java.util.Optional + +interface CapsuleRecipientRepository : JpaRepository { + fun findByMemberId(memberId: Long): List + fun findByTimeCapsuleIdAndMemberId(timeCapsuleId: Long, memberId: Long): Optional + fun existsByTimeCapsuleIdAndMemberId(timeCapsuleId: Long, memberId: Long): Boolean +} diff --git a/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/repository/GuestbookRepository.kt b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/repository/GuestbookRepository.kt new file mode 100644 index 0000000..66ed702 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/repository/GuestbookRepository.kt @@ -0,0 +1,8 @@ +package team.cklob.mudda.domain.timecapsule.domain.repository + +import org.springframework.data.jpa.repository.JpaRepository +import team.cklob.mudda.domain.timecapsule.domain.entity.Guestbook + +interface GuestbookRepository : JpaRepository { + fun findByTimeCapsuleIdAndIsDeletedFalseOrderByCreatedAtDesc(timeCapsuleId: Long): List +} diff --git a/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/repository/TimeCapsuleRepository.kt b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/repository/TimeCapsuleRepository.kt new file mode 100644 index 0000000..f2bc349 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/repository/TimeCapsuleRepository.kt @@ -0,0 +1,10 @@ +package team.cklob.mudda.domain.timecapsule.domain.repository + +import org.springframework.data.jpa.repository.JpaRepository +import team.cklob.mudda.domain.timecapsule.domain.entity.TimeCapsule +import team.cklob.mudda.domain.timecapsule.domain.type.CapsuleVisibility + +interface TimeCapsuleRepository : JpaRepository { + fun findByMemberIdAndIsDeletedFalse(memberId: Long): List + fun findByVisibilityAndIsDeletedFalse(visibility: CapsuleVisibility): List +} diff --git a/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/type/CapsuleLockType.kt b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/type/CapsuleLockType.kt new file mode 100644 index 0000000..ae105c2 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/type/CapsuleLockType.kt @@ -0,0 +1,7 @@ +package team.cklob.mudda.domain.timecapsule.domain.type + +enum class CapsuleLockType { + NONE, + PASSWORD, + QUESTION, +} diff --git a/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/type/CapsuleVisibility.kt b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/type/CapsuleVisibility.kt new file mode 100644 index 0000000..62be607 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/domain/timecapsule/domain/type/CapsuleVisibility.kt @@ -0,0 +1,7 @@ +package team.cklob.mudda.domain.timecapsule.domain.type + +enum class CapsuleVisibility { + PRIVATE, + FRIEND, + PUBLIC, +} diff --git a/src/main/kotlin/team/cklob/mudda/global/common/entity/BaseCreatedAtEntity.kt b/src/main/kotlin/team/cklob/mudda/global/common/entity/BaseCreatedAtEntity.kt new file mode 100644 index 0000000..1b50b8d --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/global/common/entity/BaseCreatedAtEntity.kt @@ -0,0 +1,18 @@ +package team.cklob.mudda.global.common.entity + +import jakarta.persistence.Column +import jakarta.persistence.EntityListeners +import jakarta.persistence.MappedSuperclass +import org.springframework.data.annotation.CreatedDate +import org.springframework.data.jpa.domain.support.AuditingEntityListener +import java.time.LocalDateTime + +@MappedSuperclass +@EntityListeners(AuditingEntityListener::class) +abstract class BaseCreatedAtEntity { + + @CreatedDate + @Column(name = "created_at", nullable = false, updatable = false) + var createdAt: LocalDateTime = LocalDateTime.now() + protected set +} diff --git a/src/main/kotlin/team/cklob/mudda/global/common/entity/BaseTimeEntity.kt b/src/main/kotlin/team/cklob/mudda/global/common/entity/BaseTimeEntity.kt new file mode 100644 index 0000000..7d6af39 --- /dev/null +++ b/src/main/kotlin/team/cklob/mudda/global/common/entity/BaseTimeEntity.kt @@ -0,0 +1,24 @@ +package team.cklob.mudda.global.common.entity + +import jakarta.persistence.Column +import jakarta.persistence.EntityListeners +import jakarta.persistence.MappedSuperclass +import org.springframework.data.annotation.CreatedDate +import org.springframework.data.annotation.LastModifiedDate +import org.springframework.data.jpa.domain.support.AuditingEntityListener +import java.time.LocalDateTime + +@MappedSuperclass +@EntityListeners(AuditingEntityListener::class) +abstract class BaseTimeEntity { + + @CreatedDate + @Column(name = "created_at", nullable = false, updatable = false) + var createdAt: LocalDateTime = LocalDateTime.now() + protected set + + @LastModifiedDate + @Column(name = "updated_at", nullable = false) + var updatedAt: LocalDateTime = LocalDateTime.now() + protected set +} diff --git a/src/main/resources/db/migration/V2__create_time_capsule_domain_tables.sql b/src/main/resources/db/migration/V2__create_time_capsule_domain_tables.sql new file mode 100644 index 0000000..9e35937 --- /dev/null +++ b/src/main/resources/db/migration/V2__create_time_capsule_domain_tables.sql @@ -0,0 +1,148 @@ +CREATE TABLE tbl_member ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name VARCHAR(30) NOT NULL, + nickname VARCHAR(30) NOT NULL, + email VARCHAR(255) NOT NULL, + profile_image_url VARCHAR(255), + bio VARCHAR(100), + profile_visibility VARCHAR(20) NOT NULL, + created_at TIMESTAMP(6) NOT NULL, + updated_at TIMESTAMP(6) NOT NULL, + CONSTRAINT uq_member_nickname UNIQUE (nickname), + CONSTRAINT uq_member_email UNIQUE (email) +); + +CREATE TABLE tbl_time_capsule ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + member_id BIGINT NOT NULL, + name VARCHAR(255) NOT NULL, + content TEXT, + time_capsule_type VARCHAR(20) NOT NULL, + visibility VARCHAR(20) NOT NULL, + lock_type VARCHAR(20) NOT NULL, + password_hash VARCHAR(255), + question VARCHAR(255), + answer_hash VARCHAR(255), + location GEOMETRY(Point, 4326) NOT NULL, + location_name VARCHAR(255), + open_radius_meter INTEGER NOT NULL, + expires_at TIMESTAMP(6) NOT NULL, + created_at TIMESTAMP(6) NOT NULL, + updated_at TIMESTAMP(6) NOT NULL, + is_anonymous BOOLEAN NOT NULL, + is_feed_public BOOLEAN NOT NULL, + is_deleted BOOLEAN NOT NULL, + CONSTRAINT fk_time_capsule_member FOREIGN KEY (member_id) REFERENCES tbl_member (id) +); + +CREATE TABLE tbl_media ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + time_capsule_id BIGINT NOT NULL, + media_type VARCHAR(20) NOT NULL, + media_url VARCHAR(255) NOT NULL, + s3_key VARCHAR(255) NOT NULL, + created_at TIMESTAMP(6) NOT NULL, + CONSTRAINT fk_media_time_capsule FOREIGN KEY (time_capsule_id) REFERENCES tbl_time_capsule (id) +); + +CREATE TABLE tbl_capsule_recipient ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + member_id BIGINT NOT NULL, + time_capsule_id BIGINT NOT NULL, + has_opened BOOLEAN NOT NULL, + opened_at TIMESTAMP(6), + created_at TIMESTAMP(6) NOT NULL, + CONSTRAINT fk_capsule_recipient_member FOREIGN KEY (member_id) REFERENCES tbl_member (id), + CONSTRAINT fk_capsule_recipient_time_capsule FOREIGN KEY (time_capsule_id) REFERENCES tbl_time_capsule (id), + CONSTRAINT uq_capsule_recipient_time_capsule_member UNIQUE (time_capsule_id, member_id) +); + +CREATE TABLE tbl_capsule_open ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + time_capsule_id BIGINT NOT NULL, + member_id BIGINT NOT NULL, + opened_at TIMESTAMP(6) NOT NULL, + open_location GEOMETRY(Point, 4326), + CONSTRAINT fk_capsule_open_time_capsule FOREIGN KEY (time_capsule_id) REFERENCES tbl_time_capsule (id), + CONSTRAINT fk_capsule_open_member FOREIGN KEY (member_id) REFERENCES tbl_member (id), + CONSTRAINT uq_capsule_open_time_capsule_member UNIQUE (time_capsule_id, member_id) +); + +CREATE TABLE tbl_capsule_history ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + time_capsule_id BIGINT NOT NULL, + member_id BIGINT NOT NULL, + event_type VARCHAR(20) NOT NULL, + created_at TIMESTAMP(6) NOT NULL, + CONSTRAINT fk_capsule_history_time_capsule FOREIGN KEY (time_capsule_id) REFERENCES tbl_time_capsule (id), + CONSTRAINT fk_capsule_history_member FOREIGN KEY (member_id) REFERENCES tbl_member (id) +); + +CREATE TABLE tbl_guestbook ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + time_capsule_id BIGINT NOT NULL, + member_id BIGINT NOT NULL, + content TEXT NOT NULL, + created_at TIMESTAMP(6) NOT NULL, + is_deleted BOOLEAN NOT NULL, + CONSTRAINT fk_guestbook_time_capsule FOREIGN KEY (time_capsule_id) REFERENCES tbl_time_capsule (id), + CONSTRAINT fk_guestbook_member FOREIGN KEY (member_id) REFERENCES tbl_member (id) +); + +CREATE TABLE tbl_friend ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + requester_id BIGINT NOT NULL, + receiver_id BIGINT NOT NULL, + status VARCHAR(20) NOT NULL, + created_at TIMESTAMP(6) NOT NULL, + accepted_at TIMESTAMP(6), + CONSTRAINT fk_friend_requester FOREIGN KEY (requester_id) REFERENCES tbl_member (id), + CONSTRAINT fk_friend_receiver FOREIGN KEY (receiver_id) REFERENCES tbl_member (id), + CONSTRAINT uq_friend_requester_receiver UNIQUE (requester_id, receiver_id) +); + +CREATE TABLE tbl_notification ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + recipient_id BIGINT NOT NULL, + time_capsule_id BIGINT, + notification_type VARCHAR(30) NOT NULL, + title VARCHAR(255) NOT NULL, + body VARCHAR(255) NOT NULL, + is_read BOOLEAN NOT NULL, + created_at TIMESTAMP(6) NOT NULL, + CONSTRAINT fk_notification_recipient FOREIGN KEY (recipient_id) REFERENCES tbl_member (id), + CONSTRAINT fk_notification_time_capsule FOREIGN KEY (time_capsule_id) REFERENCES tbl_time_capsule (id) +); + +CREATE TABLE tbl_report ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + reporter_id BIGINT NOT NULL, + target_type VARCHAR(30) NOT NULL, + target_id BIGINT NOT NULL, + reason VARCHAR(30) NOT NULL, + description VARCHAR(500), + created_at TIMESTAMP(6) NOT NULL, + CONSTRAINT fk_report_reporter FOREIGN KEY (reporter_id) REFERENCES tbl_member (id), + CONSTRAINT uq_report_reporter_target UNIQUE (reporter_id, target_type, target_id) +); + +CREATE TABLE tbl_block ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + blocker_id BIGINT NOT NULL, + blocked_id BIGINT NOT NULL, + created_at TIMESTAMP(6) NOT NULL, + CONSTRAINT fk_block_blocker FOREIGN KEY (blocker_id) REFERENCES tbl_member (id), + CONSTRAINT fk_block_blocked FOREIGN KEY (blocked_id) REFERENCES tbl_member (id), + CONSTRAINT uq_block_blocker_blocked UNIQUE (blocker_id, blocked_id) +); + +CREATE INDEX idx_time_capsule_member ON tbl_time_capsule (member_id); +CREATE INDEX idx_time_capsule_visibility_deleted ON tbl_time_capsule (visibility, is_deleted); +CREATE INDEX idx_time_capsule_location ON tbl_time_capsule USING GIST (location); +CREATE INDEX idx_media_time_capsule ON tbl_media (time_capsule_id); +CREATE INDEX idx_capsule_recipient_member ON tbl_capsule_recipient (member_id); +CREATE INDEX idx_capsule_open_member ON tbl_capsule_open (member_id); +CREATE INDEX idx_capsule_history_time_capsule ON tbl_capsule_history (time_capsule_id); +CREATE INDEX idx_guestbook_time_capsule_deleted_created ON tbl_guestbook (time_capsule_id, is_deleted, created_at DESC); +CREATE INDEX idx_friend_receiver ON tbl_friend (receiver_id); +CREATE INDEX idx_notification_recipient_created ON tbl_notification (recipient_id, created_at DESC);