feat(system): 添加公告通知已读记录功能

- 新增 SysNoticeRead 实体类用于存储公告/通知已读记录
- 实现 SysNoticeReadMapper 数据访问层接口及 XML 映射文件
- 创建 ISysNoticeReadService 服务接口及实现类
- 添加数据库表 sys_notice_read 存储用户阅读状态
- 添加发布状态字段到公告表支持公告发布控制
- 实现前端 NoticePanel 组件支持未读标记和阅读状态显示
- 提供标记已读、批量标记、未读数量统计等功能
- 优化公告列表按已读状态和时间排序显示
This commit is contained in:
2025-12-30 13:43:28 +08:00
parent 58449fc2f9
commit 430adc2112
9 changed files with 665 additions and 0 deletions

View File

@@ -0,0 +1,61 @@
package com.core.system.domain;
import com.core.common.core.domain.BaseEntity;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
/**
* 公告/通知已读记录 sys_notice_read
*
* @author system
*/
public class SysNoticeRead extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 阅读ID */
@JsonSerialize(using = ToStringSerializer.class)
private Long readId;
/** 公告/通知ID */
@JsonSerialize(using = ToStringSerializer.class)
private Long noticeId;
/** 用户ID */
@JsonSerialize(using = ToStringSerializer.class)
private Long userId;
/** 阅读时间 */
private String readTime;
public Long getReadId() {
return readId;
}
public void setReadId(Long readId) {
this.readId = readId;
}
public Long getNoticeId() {
return noticeId;
}
public void setNoticeId(Long noticeId) {
this.noticeId = noticeId;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getReadTime() {
return readTime;
}
public void setReadTime(String readTime) {
this.readTime = readTime;
}
}

View File

@@ -0,0 +1,70 @@
package com.core.system.mapper;
import java.util.List;
import com.core.system.domain.SysNoticeRead;
/**
* 公告/通知已读记录 Mapper接口
*
* @author system
*/
public interface SysNoticeReadMapper {
/**
* 查询公告/通知已读记录
*
* @param readId 阅读ID
* @return 公告/通知已读记录
*/
public SysNoticeRead selectNoticeReadById(Long readId);
/**
* 查询用户的已读公告/通知ID列表
*
* @param userId 用户ID
* @return 已读公告/通知ID列表
*/
public List<Long> selectReadNoticeIdsByUserId(Long userId);
/**
* 查询公告/通知的已读用户数量
*
* @param noticeId 公告/通知ID
* @return 已读用户数量
*/
public int countReadByNoticeId(Long noticeId);
/**
* 新增公告/通知已读记录
*
* @param noticeRead 公告/通知已读记录
* @return 结果
*/
public int insertNoticeRead(SysNoticeRead noticeRead);
/**
* 删除公告/通知已读记录
*
* @param readId 阅读ID
* @return 结果
*/
public int deleteNoticeReadById(Long readId);
/**
* 批量删除公告/通知已读记录
*
* @param readIds 需要删除的阅读ID
* @return 结果
*/
public int deleteNoticeReadByIds(Long[] readIds);
/**
* 检查用户是否已阅读公告/通知
*
* @param noticeId 公告/通知ID
* @param userId 用户ID
* @return 是否已阅读
*/
public boolean checkNoticeRead(Long noticeId, Long userId);
}

View File

@@ -0,0 +1,58 @@
package com.core.system.service;
import java.util.List;
import com.core.common.core.domain.AjaxResult;
import com.core.system.domain.SysNotice;
import com.core.system.domain.SysNoticeRead;
/**
* 公告/通知已读记录 服务层
*
* @author system
*/
public interface ISysNoticeReadService {
/**
* 查询用户的未读公告/通知数量
*
* @param userId 用户ID
* @return 未读数量
*/
public int getUnreadCount(Long userId);
/**
* 标记公告/通知为已读
*
* @param noticeId 公告/通知ID
* @param userId 用户ID
* @return 结果
*/
public AjaxResult markAsRead(Long noticeId, Long userId);
/**
* 批量标记公告/通知为已读
*
* @param noticeIds 公告/通知ID列表
* @param userId 用户ID
* @return 结果
*/
public AjaxResult markAllAsRead(Long[] noticeIds, Long userId);
/**
* 查询用户的已读公告/通知ID列表
*
* @param userId 用户ID
* @return 已读公告/通知ID列表
*/
public List<Long> selectReadNoticeIdsByUserId(Long userId);
/**
* 查询带已读状态的公告列表
*
* @param notice 公告信息
* @param userId 用户ID
* @return 公告集合
*/
public List<SysNotice> selectNoticeListWithReadStatus(SysNotice notice, Long userId);
}

View File

@@ -0,0 +1,130 @@
package com.core.system.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.core.common.core.domain.AjaxResult;
import com.core.system.domain.SysNotice;
import com.core.system.domain.SysNoticeRead;
import com.core.system.mapper.SysNoticeMapper;
import com.core.system.mapper.SysNoticeReadMapper;
import com.core.system.service.ISysNoticeReadService;
/**
* 公告/通知已读记录 服务层实现
*
* @author system
*/
@Service
public class SysNoticeReadServiceImpl implements ISysNoticeReadService {
@Autowired
private SysNoticeReadMapper noticeReadMapper;
@Autowired
private SysNoticeMapper noticeMapper;
/**
* 查询用户的未读公告/通知数量
*
* @param userId 用户ID
* @return 未读数量
*/
@Override
public int getUnreadCount(Long userId) {
// 查询所有状态为正常0的公告/通知
SysNotice notice = new SysNotice();
notice.setStatus("0");
List<SysNotice> allNotices = noticeMapper.selectNoticeList(notice);
// 查询用户已读的公告/通知ID
List<Long> readNoticeIds = noticeReadMapper.selectReadNoticeIdsByUserId(userId);
// 计算未读数量
int unreadCount = 0;
for (SysNotice n : allNotices) {
if (!readNoticeIds.contains(n.getNoticeId())) {
unreadCount++;
}
}
return unreadCount;
}
/**
* 标记公告/通知为已读
*
* @param noticeId 公告/通知ID
* @param userId 用户ID
* @return 结果
*/
@Override
public AjaxResult markAsRead(Long noticeId, Long userId) {
// 检查是否已读
boolean isRead = noticeReadMapper.checkNoticeRead(noticeId, userId);
if (isRead) {
return AjaxResult.success("已标记为已读");
}
// 插入已读记录
SysNoticeRead noticeRead = new SysNoticeRead();
noticeRead.setNoticeId(noticeId);
noticeRead.setUserId(userId);
int result = noticeReadMapper.insertNoticeRead(noticeRead);
if (result > 0) {
return AjaxResult.success("标记成功");
}
return AjaxResult.error("标记失败");
}
/**
* 批量标记公告/通知为已读
*
* @param noticeIds 公告/通知ID列表
* @param userId 用户ID
* @return 结果
*/
@Override
public AjaxResult markAllAsRead(Long[] noticeIds, Long userId) {
int successCount = 0;
for (Long noticeId : noticeIds) {
boolean isRead = noticeReadMapper.checkNoticeRead(noticeId, userId);
if (!isRead) {
SysNoticeRead noticeRead = new SysNoticeRead();
noticeRead.setNoticeId(noticeId);
noticeRead.setUserId(userId);
noticeReadMapper.insertNoticeRead(noticeRead);
successCount++;
}
}
return AjaxResult.success("成功标记" + successCount + "条记录为已读");
}
/**
* 查询用户的已读公告/通知ID列表
*
* @param userId 用户ID
* @return 已读公告/通知ID列表
*/
@Override
public List<Long> selectReadNoticeIdsByUserId(Long userId) {
return noticeReadMapper.selectReadNoticeIdsByUserId(userId);
}
/**
* 查询带已读状态的公告列表
*
* @param notice 公告信息
* @param userId 用户ID
* @return 公告集合
*/
@Override
public List<SysNotice> selectNoticeListWithReadStatus(SysNotice notice, Long userId) {
// 这里可以扩展为在查询结果中添加已读状态标记
// 暂时返回普通列表
return noticeMapper.selectNoticeList(notice);
}
}

View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.core.system.mapper.SysNoticeReadMapper">
<resultMap type="SysNoticeRead" id="SysNoticeReadResult">
<result property="readId" column="read_id"/>
<result property="noticeId" column="notice_id"/>
<result property="userId" column="user_id"/>
<result property="readTime" column="read_time"/>
</resultMap>
<select id="selectNoticeReadById" parameterType="Long" resultMap="SysNoticeReadResult">
select read_id, notice_id, user_id, read_time
from sys_notice_read
where read_id = #{readId}
</select>
<select id="selectReadNoticeIdsByUserId" parameterType="Long" resultType="Long">
select notice_id
from sys_notice_read
where user_id = #{userId}
</select>
<select id="countReadByNoticeId" parameterType="Long" resultType="int">
select count(1)
from sys_notice_read
where notice_id = #{noticeId}
</select>
<select id="checkNoticeRead" resultType="boolean">
select count(1) > 0
from sys_notice_read
where notice_id = #{noticeId} and user_id = #{userId}
</select>
<insert id="insertNoticeRead" parameterType="SysNoticeRead">
insert into sys_notice_read (
read_id,
notice_id,
user_id,
read_time
) values (
(SELECT COALESCE(MAX(read_id), 0) + 1 FROM sys_notice_read),
#{noticeId},
#{userId},
now()
)
</insert>
<delete id="deleteNoticeReadById" parameterType="Long">
delete from sys_notice_read where read_id = #{readId}
</delete>
<delete id="deleteNoticeReadByIds" parameterType="Long">
delete from sys_notice_read where read_id in
<foreach item="readId" collection="array" open="(" separator="," close=")">
#{readId}
</foreach>
</delete>
</mapper>