diff --git a/backend/src/main/java/com/openisle/controller/NotificationController.java b/backend/src/main/java/com/openisle/controller/NotificationController.java index b9cb282ba..03fe52e7e 100644 --- a/backend/src/main/java/com/openisle/controller/NotificationController.java +++ b/backend/src/main/java/com/openisle/controller/NotificationController.java @@ -62,4 +62,14 @@ public class NotificationController { public void updatePref(@RequestBody NotificationPreferenceUpdateRequest req, Authentication auth) { notificationService.updatePreference(auth.getName(), req.getType(), req.isEnabled()); } + + @GetMapping("/email-prefs") + public List emailPrefs(Authentication auth) { + return notificationService.listEmailPreferences(auth.getName()); + } + + @PostMapping("/email-prefs") + public void updateEmailPref(@RequestBody NotificationPreferenceUpdateRequest req, Authentication auth) { + notificationService.updateEmailPreference(auth.getName(), req.getType(), req.isEnabled()); + } } diff --git a/backend/src/main/java/com/openisle/model/User.java b/backend/src/main/java/com/openisle/model/User.java index e2001db8c..53b52d7a3 100644 --- a/backend/src/main/java/com/openisle/model/User.java +++ b/backend/src/main/java/com/openisle/model/User.java @@ -74,6 +74,12 @@ public class User { NotificationType.USER_ACTIVITY ); + @ElementCollection(targetClass = NotificationType.class) + @CollectionTable(name = "user_disabled_email_notification_types", joinColumns = @JoinColumn(name = "user_id")) + @Column(name = "notification_type") + @Enumerated(EnumType.STRING) + private Set disabledEmailNotificationTypes = EnumSet.noneOf(NotificationType.class); + @CreationTimestamp @Column(nullable = false, updatable = false, columnDefinition = "DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6)") diff --git a/backend/src/main/java/com/openisle/service/NotificationService.java b/backend/src/main/java/com/openisle/service/NotificationService.java index d6db3f029..dede4c2f7 100644 --- a/backend/src/main/java/com/openisle/service/NotificationService.java +++ b/backend/src/main/java/com/openisle/service/NotificationService.java @@ -19,6 +19,7 @@ import java.util.regex.Pattern; import java.util.regex.Matcher; import java.util.Set; import java.util.HashSet; +import java.util.EnumSet; import java.util.List; import java.util.ArrayList; @@ -40,6 +41,12 @@ public class NotificationService { private static final Pattern MENTION_PATTERN = Pattern.compile("@\\[([^\\]]+)\\]"); + private static final Set EMAIL_TYPES = EnumSet.of( + NotificationType.COMMENT_REPLY, + NotificationType.LOTTERY_WIN, + NotificationType.LOTTERY_DRAW + ); + private String buildPayload(String body, String url) { // Ensure push notifications contain a link to the related resource so // that verifications can assert its presence and users can navigate @@ -75,7 +82,8 @@ public class NotificationService { n = notificationRepository.save(n); // Runnable asyncTask = () -> { - if (type == NotificationType.COMMENT_REPLY && user.getEmail() != null && post != null && comment != null) { + if (type == NotificationType.COMMENT_REPLY && user.getEmail() != null && post != null && comment != null + && !user.getDisabledEmailNotificationTypes().contains(NotificationType.COMMENT_REPLY)) { String url = String.format("%s/posts/%d#comment-%d", websiteUrl, post.getId(), comment.getId()); emailSender.sendEmail(user.getEmail(), "有人回复了你", url); sendCustomPush(user, "有人回复了你", url); @@ -187,6 +195,35 @@ public class NotificationService { userRepository.save(user); } + public List listEmailPreferences(String username) { + User user = userRepository.findByUsername(username) + .orElseThrow(() -> new com.openisle.exception.NotFoundException("User not found")); + Set disabled = user.getDisabledEmailNotificationTypes(); + List prefs = new ArrayList<>(); + for (NotificationType nt : EMAIL_TYPES) { + NotificationPreferenceDto dto = new NotificationPreferenceDto(); + dto.setType(nt); + dto.setEnabled(!disabled.contains(nt)); + prefs.add(dto); + } + return prefs; + } + + public void updateEmailPreference(String username, NotificationType type, boolean enabled) { + if (!EMAIL_TYPES.contains(type)) { + return; + } + User user = userRepository.findByUsername(username) + .orElseThrow(() -> new com.openisle.exception.NotFoundException("User not found")); + Set disabled = user.getDisabledEmailNotificationTypes(); + if (enabled) { + disabled.remove(type); + } else { + disabled.add(type); + } + userRepository.save(user); + } + public List listNotifications(String username, Boolean read, int page, int size) { User user = userRepository.findByUsername(username) .orElseThrow(() -> new com.openisle.exception.NotFoundException("User not found")); diff --git a/backend/src/main/java/com/openisle/service/PostService.java b/backend/src/main/java/com/openisle/service/PostService.java index 3b088817c..b0016427e 100644 --- a/backend/src/main/java/com/openisle/service/PostService.java +++ b/backend/src/main/java/com/openisle/service/PostService.java @@ -374,14 +374,16 @@ public class PostService { lp.setWinners(winners); lotteryPostRepository.save(lp); for (User w : winners) { - if (w.getEmail() != null) { + if (w.getEmail() != null && + !w.getDisabledEmailNotificationTypes().contains(NotificationType.LOTTERY_WIN)) { emailSender.sendEmail(w.getEmail(), "你中奖了", "恭喜你在抽奖贴 \"" + lp.getTitle() + "\" 中获奖"); } notificationService.createNotification(w, NotificationType.LOTTERY_WIN, lp, null, null, lp.getAuthor(), null, null); notificationService.sendCustomPush(w, "你中奖了", String.format("%s/posts/%d", websiteUrl, lp.getId())); } if (lp.getAuthor() != null) { - if (lp.getAuthor().getEmail() != null) { + if (lp.getAuthor().getEmail() != null && + !lp.getAuthor().getDisabledEmailNotificationTypes().contains(NotificationType.LOTTERY_DRAW)) { emailSender.sendEmail(lp.getAuthor().getEmail(), "抽奖已开奖", "您的抽奖贴 \"" + lp.getTitle() + "\" 已开奖"); } notificationService.createNotification(lp.getAuthor(), NotificationType.LOTTERY_DRAW, lp, null, null, null, null, null); diff --git a/frontend_nuxt/pages/message.vue b/frontend_nuxt/pages/message.vue index bf252e616..e7456b780 100644 --- a/frontend_nuxt/pages/message.vue +++ b/frontend_nuxt/pages/message.vue @@ -23,6 +23,18 @@ +
+
邮件通知设置
+
+
+
{{ formatType(pref.type) }}
+ +
+
+