mirror of
https://gitee.com/dromara/dax-pay
synced 2026-08-09 22:45:58 +08:00
feat(notify): 新增用户端通知详情接口(/notify/user/detail), 公告校验可见性个人消息校验归属防越权
This commit is contained in:
@@ -47,6 +47,16 @@ public class NotifyUserController {
|
||||
return Res.ok(userNoticeService.list(query));
|
||||
}
|
||||
|
||||
/// 查看单条详情(公告校验可见性, 个人消息校验归属)
|
||||
///
|
||||
/// 与列表数据隔离: 前端点击查看时独立请求, 保证数据新鲜且不依赖列表上下文.
|
||||
@Operation(summary = "查看详情")
|
||||
@GetMapping("/detail")
|
||||
public Result<NotifyNoticeBriefResult> 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<Void> read(@NotBlank(message = "{validation.field.type.notBlank}") String type,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user