diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/application/dto/request/NotificationRequest.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/application/dto/request/NotificationRequest.java new file mode 100644 index 00000000..a13b2f84 --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/application/dto/request/NotificationRequest.java @@ -0,0 +1,4 @@ +package com.whereyouad.WhereYouAd.domains.notification.application.dto.request; + +public class NotificationRequest { +} diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/application/dto/response/NotificationResponse.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/application/dto/response/NotificationResponse.java new file mode 100644 index 00000000..fbd0cee4 --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/application/dto/response/NotificationResponse.java @@ -0,0 +1,4 @@ +package com.whereyouad.WhereYouAd.domains.notification.application.dto.response; + +public class NotificationResponse { +} diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/application/mapper/NotificationConverter.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/application/mapper/NotificationConverter.java new file mode 100644 index 00000000..a8936e79 --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/application/mapper/NotificationConverter.java @@ -0,0 +1,4 @@ +package com.whereyouad.WhereYouAd.domains.notification.application.mapper; + +public class NotificationConverter { +} diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/constant/.gitkeep b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/constant/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/constant/DeliveryChannel.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/constant/DeliveryChannel.java new file mode 100644 index 00000000..6e8bebcc --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/constant/DeliveryChannel.java @@ -0,0 +1,9 @@ +package com.whereyouad.WhereYouAd.domains.notification.domain.constant; + +public enum DeliveryChannel { + EMAIL, + SLACK, + DISCORD, + BROWSER_PUSH, + IN_APP +} diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/constant/DeliveryStatus.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/constant/DeliveryStatus.java new file mode 100644 index 00000000..922a82e2 --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/constant/DeliveryStatus.java @@ -0,0 +1,7 @@ +package com.whereyouad.WhereYouAd.domains.notification.domain.constant; + +public enum DeliveryStatus { + PENDING, + SUCCESS, + FAILED +} diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/NotificationService.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/NotificationService.java new file mode 100644 index 00000000..2eaea0bd --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/NotificationService.java @@ -0,0 +1,4 @@ +package com.whereyouad.WhereYouAd.domains.notification.domain.service; + +public interface NotificationService { +} diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/NotificationServiceImpl.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/NotificationServiceImpl.java new file mode 100644 index 00000000..158e92a5 --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/NotificationServiceImpl.java @@ -0,0 +1,9 @@ +package com.whereyouad.WhereYouAd.domains.notification.domain.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class NotificationServiceImpl implements NotificationService { +} diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/exception/NotificationException.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/exception/NotificationException.java new file mode 100644 index 00000000..fb8d18d7 --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/exception/NotificationException.java @@ -0,0 +1,10 @@ +package com.whereyouad.WhereYouAd.domains.notification.exception; + +import com.whereyouad.WhereYouAd.global.exception.AppException; +import com.whereyouad.WhereYouAd.global.exception.BaseErrorCode; + +public class NotificationException extends AppException { + public NotificationException(BaseErrorCode code) { + super(code); + } +} diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/exception/code/NotificationErrorCode.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/exception/code/NotificationErrorCode.java new file mode 100644 index 00000000..8cb99c55 --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/exception/code/NotificationErrorCode.java @@ -0,0 +1,17 @@ +package com.whereyouad.WhereYouAd.domains.notification.exception.code; + +import com.whereyouad.WhereYouAd.global.exception.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum NotificationErrorCode implements BaseErrorCode { + + ; + + private final HttpStatus httpStatus; + private final String code; + private final String message; +} diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/Notification.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/Notification.java new file mode 100644 index 00000000..2d9cc60e --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/Notification.java @@ -0,0 +1,45 @@ +package com.whereyouad.WhereYouAd.domains.notification.persistence.entity; + +import com.whereyouad.WhereYouAd.domains.organization.persistence.entity.Organization; +import jakarta.persistence.*; +import lombok.*; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.time.LocalDateTime; + +@Entity +@Table(name = "notification") +@EntityListeners(AuditingEntityListener.class) +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Notification { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "notification_id") + private Long id; + + @Column(name = "title", nullable = false, length = 200) + private String title; + + @Column(name = "message", nullable = false, length = 2000) + private String message; + + @Column(name = "link_url", length = 1024) + private String linkUrl; + + @Column(name = "metadata_json", length = 4000) + private String metadataJson; + + @CreatedDate + @Column(name = "created_at", updatable = false) + private LocalDateTime createdAt; + + // 연관 관계 + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "org_id", nullable = false) + private Organization organization; +} diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/NotificationDelivery.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/NotificationDelivery.java new file mode 100644 index 00000000..fab4f69a --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/NotificationDelivery.java @@ -0,0 +1,66 @@ +package com.whereyouad.WhereYouAd.domains.notification.persistence.entity; + +import com.whereyouad.WhereYouAd.domains.notification.domain.constant.DeliveryChannel; +import com.whereyouad.WhereYouAd.domains.notification.domain.constant.DeliveryStatus; +import com.whereyouad.WhereYouAd.domains.organization.persistence.entity.OrgMember; +import com.whereyouad.WhereYouAd.global.common.BaseEntity; +import jakarta.persistence.*; +import lombok.*; + +import java.time.LocalDateTime; + +@Entity +@Table(name = "notification_delivery") +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class NotificationDelivery extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "delivery_id") + private Long id; + + @Enumerated(EnumType.STRING) + @Column(name = "channel", nullable = false, length = 20) + private DeliveryChannel channel; + + @Builder.Default + @Enumerated(EnumType.STRING) + @Column(name = "status", nullable = false, length = 10) + private DeliveryStatus status = DeliveryStatus.PENDING; + + @Column(name = "sent_at") + private LocalDateTime sentAt; + + @Column(name = "failed_at") + private LocalDateTime failedAt; + + @Column(name = "failure_reason", length = 500) + private String failureReason; + + @Builder.Default + @Column(name = "retry_count", nullable = false) + private int retryCount = 0; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "notification_id", nullable = false) + private Notification notification; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "membership_id", nullable = false) + private OrgMember orgMember; + + public void markSuccess() { + this.status = DeliveryStatus.SUCCESS; + this.sentAt = LocalDateTime.now(); + } + + public void markFailed(String reason) { + this.status = DeliveryStatus.FAILED; + this.failedAt = LocalDateTime.now(); + this.failureReason = reason; + this.retryCount++; + } +} diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/OrgMemberNotificationSetting.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/OrgMemberNotificationSetting.java new file mode 100644 index 00000000..349be66e --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/OrgMemberNotificationSetting.java @@ -0,0 +1,51 @@ +package com.whereyouad.WhereYouAd.domains.notification.persistence.entity; + +import com.whereyouad.WhereYouAd.domains.organization.persistence.entity.OrgMember; +import com.whereyouad.WhereYouAd.global.common.BaseEntity; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Table(name = "org_member_notification_setting") +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class OrgMemberNotificationSetting extends BaseEntity { + + @Id + @Column(name = "membership_id") + private Long membershipId; + + @OneToOne(fetch = FetchType.LAZY) + @MapsId // PK == FK(식별 관계) + @JoinColumn(name = "membership_id") + private OrgMember orgMember; + + @Column(name = "is_master_enabled", nullable = false) + private boolean isMasterEnabled; + + @Column(name = "is_browser_push_enabled", nullable = false) + private boolean isBrowserPushEnabled; + + @Column(name = "is_email_enabled", nullable = false) + private boolean isEmailEnabled; + + @Column(name = "is_slack_enabled", nullable = false) + private boolean isSlackEnabled; + + @Column(name = "is_discord_enabled", nullable = false) + private boolean isDiscordEnabled; + + @Column(name = "alert_budget_50", nullable = false) + private boolean alertBudget50; + + @Column(name = "alert_budget_80", nullable = false) + private boolean alertBudget80; + + @Column(name = "alert_budget_100", nullable = false) + private boolean alertBudget100; + + @Column(name = "alert_rapid_clicks", nullable = false) + private boolean alertRapidClicks; +} diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/OrgNotificationSetting.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/OrgNotificationSetting.java new file mode 100644 index 00000000..b3d910b3 --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/OrgNotificationSetting.java @@ -0,0 +1,30 @@ +package com.whereyouad.WhereYouAd.domains.notification.persistence.entity; + +import com.whereyouad.WhereYouAd.domains.organization.persistence.entity.Organization; +import com.whereyouad.WhereYouAd.global.common.BaseEntity; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Table(name = "org_notification_setting") +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class OrgNotificationSetting extends BaseEntity { + + @Id + @Column(name = "org_id") + private Long orgId; + + @OneToOne(fetch = FetchType.LAZY) + @MapsId // PK == FK(식별 관계) + @JoinColumn(name = "org_id") + private Organization organization; + + @Column(name = "slack_webhook_url", length = 512) + private String slackWebhookUrl; + + @Column(name = "discord_webhook_url", length = 512) + private String discordWebhookUrl; +} diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/UserNotification.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/UserNotification.java new file mode 100644 index 00000000..52e3b9d5 --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/UserNotification.java @@ -0,0 +1,38 @@ +package com.whereyouad.WhereYouAd.domains.notification.persistence.entity; + +import com.whereyouad.WhereYouAd.domains.user.persistence.entity.User; +import jakarta.persistence.*; +import lombok.*; +import org.hibernate.annotations.ColumnDefault; + +import java.time.LocalDateTime; + +@Entity +@Table(name = "user_notification") +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class UserNotification { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "user_notification_id") + private Long id; + + @ColumnDefault("false") + @Column(name = "is_read", nullable = false) + private boolean isRead; + + @Column(name = "read_at") + private LocalDateTime readAt; + + // 연관관계 + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "notification_id", nullable = false) + private Notification notification; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id", nullable = false) + private User user; +} diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/NotificationDeliveryRepository.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/NotificationDeliveryRepository.java new file mode 100644 index 00000000..0db71843 --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/NotificationDeliveryRepository.java @@ -0,0 +1,9 @@ +package com.whereyouad.WhereYouAd.domains.notification.persistence.repository; + +import com.whereyouad.WhereYouAd.domains.notification.persistence.entity.NotificationDelivery; +import org.springframework.data.jpa.repository.JpaRepository; + + +public interface NotificationDeliveryRepository extends JpaRepository { + +} diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/NotificationRepository.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/NotificationRepository.java new file mode 100644 index 00000000..20aa94fe --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/NotificationRepository.java @@ -0,0 +1,7 @@ +package com.whereyouad.WhereYouAd.domains.notification.persistence.repository; + +import com.whereyouad.WhereYouAd.domains.notification.persistence.entity.Notification; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface NotificationRepository extends JpaRepository { +} diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/OrgMemberNotificationSettingRepository.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/OrgMemberNotificationSettingRepository.java new file mode 100644 index 00000000..2ba139d8 --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/OrgMemberNotificationSettingRepository.java @@ -0,0 +1,7 @@ +package com.whereyouad.WhereYouAd.domains.notification.persistence.repository; + +import com.whereyouad.WhereYouAd.domains.notification.persistence.entity.OrgMemberNotificationSetting; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface OrgMemberNotificationSettingRepository extends JpaRepository { +} diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/OrgNotificationSettingRepository.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/OrgNotificationSettingRepository.java new file mode 100644 index 00000000..eb4ca8f6 --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/OrgNotificationSettingRepository.java @@ -0,0 +1,7 @@ +package com.whereyouad.WhereYouAd.domains.notification.persistence.repository; + +import com.whereyouad.WhereYouAd.domains.notification.persistence.entity.OrgNotificationSetting; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface OrgNotificationSettingRepository extends JpaRepository { +} diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/UserNotificationRepository.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/UserNotificationRepository.java new file mode 100644 index 00000000..035b6f84 --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/UserNotificationRepository.java @@ -0,0 +1,7 @@ +package com.whereyouad.WhereYouAd.domains.notification.persistence.repository; + +import com.whereyouad.WhereYouAd.domains.notification.persistence.entity.UserNotification; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserNotificationRepository extends JpaRepository { +} diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/presentation/NotificationController.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/presentation/NotificationController.java new file mode 100644 index 00000000..4ffe3890 --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/presentation/NotificationController.java @@ -0,0 +1,12 @@ +package com.whereyouad.WhereYouAd.domains.notification.presentation; + +import com.whereyouad.WhereYouAd.domains.notification.presentation.docs.NotificationControllerDocs; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/notification") +@RequiredArgsConstructor +public class NotificationController implements NotificationControllerDocs { +} diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/presentation/docs/NotificationControllerDocs.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/presentation/docs/NotificationControllerDocs.java new file mode 100644 index 00000000..b4a32165 --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/presentation/docs/NotificationControllerDocs.java @@ -0,0 +1,4 @@ +package com.whereyouad.WhereYouAd.domains.notification.presentation.docs; + +public interface NotificationControllerDocs { +}