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) {