From 6fac32d59677109ba9af77f6e07ad316e97a00f8 Mon Sep 17 00:00:00 2001 From: DaxPay Dev Date: Wed, 1 Jul 2026 22:33:05 +0800 Subject: [PATCH] =?UTF-8?q?feat(notify):=20=E6=96=B0=E5=A2=9E=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=AB=AF=E9=80=9A=E7=9F=A5=E8=AF=A6=E6=83=85=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3(/notify/user/detail),=20=E5=85=AC=E5=91=8A=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E5=8F=AF=E8=A7=81=E6=80=A7=E4=B8=AA=E4=BA=BA=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E6=A0=A1=E9=AA=8C=E5=BD=92=E5=B1=9E=E9=98=B2=E8=B6=8A?= =?UTF-8?q?=E6=9D=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notice/NotifyUserController.java | 10 +++++ .../notice/NotifyUserNoticeService.java | 45 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/daxpay-platform/daxpay-platform-service/service-notify/src/main/java/cn/daxpay/open/platform/notify/controller/notice/NotifyUserController.java b/daxpay-platform/daxpay-platform-service/service-notify/src/main/java/cn/daxpay/open/platform/notify/controller/notice/NotifyUserController.java index 7cbf8712c..4616f6311 100644 --- a/daxpay-platform/daxpay-platform-service/service-notify/src/main/java/cn/daxpay/open/platform/notify/controller/notice/NotifyUserController.java +++ b/daxpay-platform/daxpay-platform-service/service-notify/src/main/java/cn/daxpay/open/platform/notify/controller/notice/NotifyUserController.java @@ -47,6 +47,16 @@ public class NotifyUserController { return Res.ok(userNoticeService.list(query)); } + /// 查看单条详情(公告校验可见性, 个人消息校验归属) + /// + /// 与列表数据隔离: 前端点击查看时独立请求, 保证数据新鲜且不依赖列表上下文. + @Operation(summary = "查看详情") + @GetMapping("/detail") + public Result detail(@NotBlank(message = "{validation.field.type.notBlank}") String type, + @NotNull(message = "{validation.field.id.notNull}") Long id) { + return Res.ok(userNoticeService.detail(type, id)); + } + @Operation(summary = "标记单条已读") @PostMapping("/read") public Result read(@NotBlank(message = "{validation.field.type.notBlank}") String type, diff --git a/daxpay-platform/daxpay-platform-service/service-notify/src/main/java/cn/daxpay/open/platform/notify/service/notice/NotifyUserNoticeService.java b/daxpay-platform/daxpay-platform-service/service-notify/src/main/java/cn/daxpay/open/platform/notify/service/notice/NotifyUserNoticeService.java index 9f7f35be9..f44724882 100644 --- a/daxpay-platform/daxpay-platform-service/service-notify/src/main/java/cn/daxpay/open/platform/notify/service/notice/NotifyUserNoticeService.java +++ b/daxpay-platform/daxpay-platform-service/service-notify/src/main/java/cn/daxpay/open/platform/notify/service/notice/NotifyUserNoticeService.java @@ -2,6 +2,7 @@ package cn.daxpay.open.platform.notify.service.notice; import cn.daxpay.open.platform.capability.auth.util.SecurityUtil; import cn.daxpay.open.platform.common.mybatisplus.base.MpIdEntity; +import cn.daxpay.open.platform.core.exception.DataNotExistException; import cn.daxpay.open.platform.notify.convert.notice.NotifyNoticeConvert; import cn.daxpay.open.platform.notify.dao.message.NotifyMessageManager; import cn.daxpay.open.platform.notify.dao.notice.NotifyNoticeManager; @@ -111,6 +112,50 @@ public class NotifyUserNoticeService { return list; } + /// 查看单条详情(公告校验可见性并渲染HTML, 个人消息校验归属当前用户) + /// + /// 与列表隔离, 防 id 直连: 草稿/下线/未生效/已过期的公告、他人个人消息一律视为不存在. + public NotifyNoticeBriefResult detail(String type, Long id) { + Long userId = SecurityUtil.getUserId(); + if (NotifyTypeEnum.notice.getCode().equals(type)) { + return detailNotice(id, userId); + } + if (NotifyTypeEnum.message.getCode().equals(type)) { + return detailMessage(id, userId); + } + // 未知类型 + throw new DataNotExistException("error.common.dataNotExist"); + } + + /// 公告详情(校验已发布且在生效期内, 渲染 Markdown 为 HTML, 填充当前用户阅读状态) + private NotifyNoticeBriefResult detailNotice(Long id, Long userId) { + NotifyNotice notice = noticeManager.findById(id) + .orElseThrow(() -> new DataNotExistException("error.notify.notice.notExist")); + // 可见性校验: 与 list 的 findVisibleNotices 保持一致 + OffsetDateTime now = OffsetDateTime.now(); + boolean visible = NotifyStatusEnum.published.getCode().equals(notice.getStatus()) + && (notice.getEffectiveTime() == null || !notice.getEffectiveTime().isAfter(now)) + && (notice.getExpireTime() == null || notice.getExpireTime().isAfter(now)); + if (!visible) { + throw new DataNotExistException("error.notify.notice.notExist"); + } + NotifyNoticeBriefResult brief = NotifyNoticeConvert.CONVERT.toBrief(notice); + // 服务端渲染 Markdown 正文为 HTML, 供前端直接展示 + brief.setHtmlContent(MarkdownRenderUtil.toHtml(notice.getContent())); + brief.setIsRead(readManager.findByUserAndNotice(userId, id).isPresent()); + return brief; + } + + /// 个人消息详情(校验归属当前用户, 防越权查看他人消息) + private NotifyNoticeBriefResult detailMessage(Long id, Long userId) { + NotifyMessage message = messageManager.findById(id) + .orElseThrow(() -> new DataNotExistException("error.common.dataNotExist")); + if (!userId.equals(message.getUserId())) { + throw new DataNotExistException("error.common.dataNotExist"); + } + return NotifyNoticeConvert.CONVERT.convert(message); + } + /// 标记单条已读 @Transactional(rollbackFor = Exception.class) public void markRead(String type, Long id) {