消息通知

This commit is contained in:
wanghao
2026-02-05 19:18:12 +08:00
parent b57482b0be
commit 6b13ab307a
19 changed files with 1138 additions and 0 deletions
@@ -0,0 +1,35 @@
-- 消息通知表
CREATE TABLE `sys_message_notice` (
`id` varchar(32) NOT NULL COMMENT 'id',
`title` varchar(255) DEFAULT NULL COMMENT '标题',
`content` text COMMENT '内容',
`scope_type` int(2) DEFAULT NULL COMMENT '范围(1-指定部门,2-指定人群,3-既指定部门又指定人群)',
`org_code` varchar(100) DEFAULT NULL COMMENT '创建机构',
`notice_status` int(2) DEFAULT '1' COMMENT '通知状态(0 1)',
`start_time` datetime DEFAULT NULL COMMENT '通知开始时间',
`end_time` datetime DEFAULT NULL COMMENT '通知结束时间',
`del_flag` varchar(1) DEFAULT '0' COMMENT '删除状态(0-正常,1-已删除)',
`create_by` varchar(50) DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(50) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_org_code` (`org_code`),
KEY `idx_notice_status` (`notice_status`),
KEY `idx_start_time` (`start_time`),
KEY `idx_end_time` (`end_time`),
KEY `idx_del_flag` (`del_flag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='消息通知表';
-- 消息通知范围表
CREATE TABLE `sys_message_notice_scope` (
`id` varchar(32) NOT NULL COMMENT 'id',
`notice_id` varchar(32) NOT NULL COMMENT '消息id',
`type` int(2) DEFAULT NULL COMMENT '类别(1-指定部门,2-指定人群)',
`scope_id` varchar(100) DEFAULT NULL COMMENT '限制范围标识(:部门Code,人员ID)',
`record` varchar(255) DEFAULT NULL COMMENT '记录值(:部门名称,人员名称)',
PRIMARY KEY (`id`),
KEY `idx_notice_id` (`notice_id`),
KEY `idx_type` (`type`),
KEY `idx_scope_id` (`scope_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='消息通知范围表';
@@ -0,0 +1,44 @@
package org.jeecg.constant;
/**
* 消息通知常量
*
* @author wangHao
* @date 2026-02-05
*/
public interface NoticeConstants {
/**
* 通知范围类型 - 指定部门
*/
Integer NOTICE_SCOPE_TYPE_DEPART = 1;
/**
* 通知范围类型 - 指定人群
*/
Integer NOTICE_SCOPE_TYPE_PEOPLE = 2;
/**
* 通知范围类型 - 既指定部门又指定人群
*/
Integer NOTICE_SCOPE_TYPE_BOTH = 3;
/**
* 查询来源 - 后台管理
*/
Integer QUERY_SOURCE_ADMIN = 0;
/**
* 查询来源 - 移动端
*/
Integer QUERY_SOURCE_APP = 1;
/**
* 通知状态 - 不显示
*/
Integer NOTICE_STATUS_HIDE = 0;
/**
* 通知状态 - 显示
*/
Integer NOTICE_STATUS_SHOW = 1;
}
@@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.jeecg.common.system.bean.response.BaseUser;
@@ -13,6 +15,8 @@ import org.jeecg.modules.system.entity.SysUser;
import org.springframework.data.repository.query.Param;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* <p>
@@ -22,6 +26,7 @@ import java.util.List;
* @Author: Steve
* @Since 2019-01-22
*/
@Mapper
public interface SysDepartMapper extends BaseMapper<SysDepart> {
/**
@@ -167,4 +172,11 @@ public interface SysDepartMapper extends BaseMapper<SysDepart> {
List<SysDepart> selectDepartByDepartIds(String departId);
SysDepartGps selectDepartGpsByDepartId(String departId);
/**
* 根据部门编码集合查询部门名称映射
* @param orgCodes 部门编码集合
* @return Map<orgCode, departName>
*/
List<SysDepart> selectNameByCodes(@Param("orgCodes") Set<String> orgCodes);
}
@@ -417,4 +417,12 @@ public interface SysUserMapper extends BaseMapper<SysUser>, MPJBaseMapper<SysUse
List<UserBase> getUserInfoByTypeCode(@Param("findCode") String findCode,
@Param("findType") String findType);
/**
* 根据用户id集合查询用户昵称映射
* @param userIds 用户id集合
* @return Map<userId, realName>
*/
List<SysUser> selectNameByIds(@Param("userIds") Set<String> userIds);
}
@@ -177,4 +177,15 @@
depart_id as "departId"
from sys_depart_gps where depart_id = #{departId}
</select>
<select id="selectNameByCodes" resultType="org.jeecg.modules.system.entity.SysDepart">
SELECT org_code, depart_name
FROM sys_depart
WHERE del_flag = '0'
AND org_code IN
<foreach collection="orgCodes" item="code" open="(" separator="," close=")">
#{code}
</foreach>
</select>
</mapper>
@@ -1241,4 +1241,14 @@
ORDER BY processed_group;
</select>
<select id="selectNameByIds" resultType="org.jeecg.modules.system.entity.SysUser">
SELECT id, realname
FROM sys_user
WHERE del_flag = '0'
AND id IN
<foreach collection="userIds" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
</mapper>
@@ -0,0 +1,53 @@
package org.jeecg.modules.system.message.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.modules.system.message.model.po.SysMessageNotice;
import org.jeecg.modules.system.message.model.query.SysMessageNoticeQuery;
import org.jeecg.modules.system.message.model.vo.SysMessageNoticeVO;
import org.jeecg.modules.system.message.service.ISysMessageNoticeService;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 消息通知 移动端Controller
*
* @author wangHao
* @date 2026-02-05
*/
@Tag(name = "消息通知/移动端")
@RestController
@RequestMapping("/app/sys/message/notice")
@Slf4j
@RequiredArgsConstructor
public class SysMessageNoticeAppController extends JeecgController<SysMessageNotice, ISysMessageNoticeService> {
private final ISysMessageNoticeService sysMessageNoticeService;
/**
* 分页列表查询
*/
@Operation(summary = "消息通知-分页列表查询", description = "消息通知-分页列表查询")
@GetMapping(value = "/list")
public Result<List<SysMessageNoticeVO>> queryPageList(SysMessageNoticeQuery query,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
Page<SysMessageNotice> page = new Page<>(pageNo, pageSize);
return Result.OK(sysMessageNoticeService.queryPageAppList(query,page));
}
/**
* 通过id查询
*/
@Operation(summary = "消息通知-通过id查询", description = "消息通知-通过id查询")
@GetMapping(value = "/queryById")
public Result<SysMessageNotice> queryById(@RequestParam(name = "id", required = true) String id) {
return Result.OK(sysMessageNoticeService.getNoticeById(id));
}
}
@@ -0,0 +1,119 @@
package org.jeecg.modules.system.message.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.modules.system.message.model.dto.SysMessageNoticeDTO;
import org.jeecg.modules.system.message.model.query.SysMessageNoticeQuery;
import org.jeecg.modules.system.message.model.po.SysMessageNotice;
import org.jeecg.modules.system.message.model.vo.SysMessageNoticeVO;
import org.jeecg.modules.system.message.service.ISysMessageNoticeService;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
/**
* 消息通知 Controller
*
* @author wangHao
* @date 2026-02-05
*/
@Tag(name = "消息通知/后台")
@RestController
@RequestMapping("/sys/message/notice")
@Slf4j
@RequiredArgsConstructor
public class SysMessageNoticeController extends JeecgController<SysMessageNotice, ISysMessageNoticeService> {
private final ISysMessageNoticeService sysMessageNoticeService;
/**
* 分页列表查询
*/
@Operation(summary = "消息通知-分页列表查询", description = "消息通知-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<SysMessageNoticeVO>> queryPageList(SysMessageNoticeQuery query,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
Page<SysMessageNotice> page = new Page<>(pageNo, pageSize);
return Result.OK(sysMessageNoticeService.queryPageList(query,page));
}
/**
* 添加
*/
@AutoLog(value = "消息通知-添加")
@Operation(summary = "消息通知-添加", description = "消息通知-添加")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody SysMessageNoticeDTO dto) {
sysMessageNoticeService.add(dto);
return Result.OK("添加成功!");
}
/**
* 编辑
*/
@AutoLog(value = "消息通知-编辑")
@Operation(summary = "消息通知-编辑", description = "消息通知-编辑")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
public Result<String> edit(@RequestBody SysMessageNoticeDTO dto) {
sysMessageNoticeService.edit(dto);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*/
@AutoLog(value = "消息通知-通过id删除")
@Operation(summary = "消息通知-通过id删除", description = "消息通知-通过id删除")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
sysMessageNoticeService.deleteNotice(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*/
@AutoLog(value = "消息通知-批量删除")
@Operation(summary = "消息通知-批量删除", description = "消息通知-批量删除")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
sysMessageNoticeService.deleteBatchNotice(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*/
@Operation(summary = "消息通知-通过id查询", description = "消息通知-通过id查询")
@GetMapping(value = "/queryById")
public Result<SysMessageNotice> queryById(@RequestParam(name = "id", required = true) String id) {
return Result.OK(sysMessageNoticeService.getNoticeById(id));
}
/**
* 导出excel
*/
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, SysMessageNotice sysMessageNotice) {
return super.exportXls(request, sysMessageNotice, SysMessageNotice.class, "消息通知");
}
/**
* 导入excel
*/
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, SysMessageNotice.class);
}
}
@@ -0,0 +1,14 @@
package org.jeecg.modules.system.message.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.jeecg.modules.system.message.model.po.SysMessageNotice;
/**
* 消息通知 Mapper
*
* @author wangHao
* @date 2026-02-05
*/
public interface SysMessageNoticeMapper extends BaseMapper<SysMessageNotice> {
}
@@ -0,0 +1,16 @@
package org.jeecg.modules.system.message.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.jeecg.modules.system.message.model.po.SysMessageNoticeScope;
/**
* 消息通知范围 Mapper
*
* @author wangHao
* @date 2026-02-05
*/
@Mapper
public interface SysMessageNoticeScopeMapper extends BaseMapper<SysMessageNoticeScope> {
}
@@ -0,0 +1,29 @@
package org.jeecg.modules.system.message.model.dto;
import cn.hutool.core.bean.BeanUtil;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.jeecg.modules.system.message.model.po.SysMessageNotice;
import org.jeecg.modules.system.message.model.po.SysMessageNoticeScope;
import java.util.List;
/**
* 消息通知数据传输对象
* 用于接收前端传递的消息通知数据,包含范围信息
*
* @author wangHao
* @date 2026-02-05
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class SysMessageNoticeDTO extends SysMessageNotice {
/**
* 指定机构
*/
private List<SysMessageNoticeScope> departScopeList;
/**
* 指定人群
*/
private List<SysMessageNoticeScope> userScopeList;
}
@@ -0,0 +1,137 @@
package org.jeecg.modules.system.message.model.po;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 消息通知实体类
* <p>
* 用于系统消息通知的管理,支持在移动端首页顶部显示通知信息。
* 主要功能包括:
* <ul>
* <li>支持按部门、人群或两者组合的方式指定通知范围</li>
* <li>支持设置通知的有效时间范围(开始时间和结束时间)</li>
* <li>支持通知状态控制,决定是否在移动端首页显示</li>
* <li>支持逻辑删除,保留历史通知记录</li>
* </ul>
* </p>
*
* @author wangHao
* @date 2026-02-05
*/
@Data
@TableName("sys_message_notice")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(title = "sys_message_notice对象", description = "消息通知")
public class SysMessageNotice implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(title = "id")
private String id;
/**
* 标题
*/
@Excel(name = "标题", width = 15)
@Schema(title = "标题")
private String title;
/**
* 内容
*/
@Excel(name = "内容", width = 15)
@Schema(title = "内容")
private String content;
/**
* 范围(1-指定部门,2-指定人群,3-既指定部门又指定人群)
*/
@Schema(title = "范围")
private Integer scopeType;
/**
* 创建机构
*/
@Schema(title = "创建机构")
private String orgCode;
/**
* 公告状态(0:否 1:是)
* 冗余字段 用于移动端首页是否在顶部显示通知 目前说是创建的通知都显示 默认1
*/
@Schema(title = "通知状态")
private Integer noticeStatus;
/**
* 通知开始时间
* 用于移动端首页顶部通知显示时效范围的起始时间
* 目前说是都是凌晨发布通知 为了防止可能会存在任意时间发布 格式化为 yyyy-MM-dd HH:mm:ss
* HH:mm:ss 则填0
*/
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(title = "通知开始时间")
private Date startTime;
/**
* 通知结束时间
* 用于移动端首页顶部通知显示时效范围的停止时间
* 目前说是都是凌晨发布通知 为了防止可能会存在任意时间发布 格式化为 yyyy-MM-dd HH:mm:ss
* HH:mm:ss 则填0
*/
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(title = "通知结束时间")
private Date endTime;
/**
* 删除状态(0-正常,1-已删除)
*/
@Excel(name = "删除状态(0-正常,1-已删除)", width = 15)
@Schema(title = "删除状态(0-正常,1-已删除)")
@TableLogic
private String delFlag;
/**
* 创建人
*/
@Schema(title = "创建人")
private String createBy;
/**
* 创建时间
*/
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Schema(title = "创建时间")
private Date createTime;
/**
* 更新人
*/
@Schema(title = "更新人")
private String updateBy;
/**
* 更新时间
*/
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Schema(title = "更新时间")
private Date updateTime;
}
@@ -0,0 +1,66 @@
package org.jeecg.modules.system.message.model.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* 消息通知范围实体类
* <p>
* 用于记录消息通知的发送范围,与消息通知表(sys_message_notice)关联。
* 支持两种范围类型:
* <ul>
* <li>指定部门:记录部门Code和部门名称</li>
* <li>指定人群:记录人员ID和人员名称</li>
* </ul>
* 一条消息通知可以对应多条范围记录,实现灵活的消息推送控制。
* </p>
*
* @author wangHao
* @date 2026-02-05
*/
@Data
@TableName("sys_message_notice_scope")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(title = "sys_message_notice_scope对象", description = "消息范围")
public class SysMessageNoticeScope implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(title = "id")
private String id;
/**
* 消息id
*/
@Schema(title = "消息id")
private String noticeId;
/**
* 类别 1-指定部门,2-指定人群
*/
@Schema(title = "类别")
private Integer type;
/**
* 限制范围标识(如:部门Code,人员ID)
*/
@Schema(title = "限制范围标识")
private String scopeId;
/**
* 记录值(例:部门名称,人员名称)
*/
@Schema(title = "记录值")
private String record;
}
@@ -0,0 +1,45 @@
package org.jeecg.modules.system.message.model.query;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
* 消息通知查询参数对象
*
* @author wangHao
* @date 2026-02-05
*/
@Data
public class SysMessageNoticeQuery {
/**
* 通知标题
*/
@Schema(title = "通知标题")
private String title;
/**
* 通知范围(1:指定机构 2:指定人员)
*/
@Schema(title = "通知范围(1:指定机构 2:指定人员)")
private String scopeType;
/**
* 发布日期开始
*/
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Schema(title = "发布日期开始")
private Date startDate;
/**
* 发布日期结束
*/
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Schema(title = "发布日期结束")
private Date endDate;
}
@@ -0,0 +1,36 @@
package org.jeecg.modules.system.message.model.vo;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.jeecg.modules.system.message.model.po.SysMessageNotice;
import org.jeecg.modules.system.message.model.po.SysMessageNoticeScope;
import java.util.List;
/**
* 消息通知视图对象
* 用于返回给前端的消息通知数据,包含范围信息
*
* @author wangHao
* @date 2026-02-05
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class SysMessageNoticeVO extends SysMessageNotice {
/**
* 指定机构
*/
private List<SysMessageNoticeScope> departScopeList;
/**
* 指定人群
*/
private List<SysMessageNoticeScope> userScopeList;
public SysMessageNoticeVO() {}
public SysMessageNoticeVO(SysMessageNotice sysMessageNotice) {
BeanUtil.copyProperties(sysMessageNotice, this);
}
}
@@ -0,0 +1,14 @@
package org.jeecg.modules.system.message.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.system.message.model.po.SysMessageNoticeScope;
/**
* 消息通知范围 Service
*
* @author wangHao
* @date 2026-02-05
*/
public interface ISysMessageNoticeScopeService extends IService<SysMessageNoticeScope> {
}
@@ -0,0 +1,78 @@
package org.jeecg.modules.system.message.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.system.message.model.dto.SysMessageNoticeDTO;
import org.jeecg.modules.system.message.model.query.SysMessageNoticeQuery;
import org.jeecg.modules.system.message.model.po.SysMessageNotice;
import org.jeecg.modules.system.message.model.vo.SysMessageNoticeVO;
import java.util.List;
/**
* 消息通知 Service
*
* @author wangHao
* @date 2026-02-05
*/
public interface ISysMessageNoticeService extends IService<SysMessageNotice> {
/**
* 查询后台管理分页列表
* 根据用户角色权限过滤数据
*
* @param query 查询条件
* @param page 分页参数
* @return 分页结果
*/
IPage<SysMessageNoticeVO> queryPageList(SysMessageNoticeQuery query, Page<SysMessageNotice> page);
/**
* 查询移动端分页列表
* 根据用户所属部门和个人权限过滤数据
*
* @param query 查询条件
* @param page 分页参数
* @return 列表结果
*/
List<SysMessageNoticeVO> queryPageAppList(SysMessageNoticeQuery query, Page<SysMessageNotice> page);
/**
* 添加消息通知
*
* @param dto 消息通知数据
*/
void add(SysMessageNoticeDTO dto);
/**
* 编辑消息通知
*
* @param dto 消息通知数据
*/
void edit(SysMessageNoticeDTO dto);
/**
* 删除消息通知
* 同时删除关联的范围数据
*
* @param id 通知ID
*/
void deleteNotice(String id);
/**
* 批量删除消息通知
* 同时删除关联的范围数据
*
* @param ids 通知ID列表
*/
void deleteBatchNotice(List<String> ids);
/**
* 根据ID获取消息通知
*
* @param id 通知ID
* @return 消息通知
*/
SysMessageNotice getNoticeById(String id);
}
@@ -0,0 +1,18 @@
package org.jeecg.modules.system.message.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.modules.system.message.model.po.SysMessageNoticeScope;
import org.jeecg.modules.system.message.mapper.SysMessageNoticeScopeMapper;
import org.jeecg.modules.system.message.service.ISysMessageNoticeScopeService;
import org.springframework.stereotype.Service;
/**
* 消息通知范围 ServiceImpl
*
* @author wangHao
* @date 2026-02-05
*/
@Service
public class SysMessageNoticeScopeServiceImpl extends ServiceImpl<SysMessageNoticeScopeMapper, SysMessageNoticeScope> implements ISysMessageNoticeScopeService {
}
@@ -0,0 +1,393 @@
package org.jeecg.modules.system.message.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.jeecg.common.exception.ExceptionAssertsUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.constant.NoticeConstants;
import org.jeecg.enums.RoleEnum;
import org.jeecg.global.GlobalUtils;
import org.jeecg.modules.manager.ISysCache;
import org.jeecg.modules.system.entity.SysDepart;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.mapper.SysDepartMapper;
import org.jeecg.modules.system.mapper.SysUserMapper;
import org.jeecg.modules.system.message.mapper.SysMessageNoticeScopeMapper;
import org.jeecg.modules.system.message.model.dto.SysMessageNoticeDTO;
import org.jeecg.modules.system.message.model.query.SysMessageNoticeQuery;
import org.jeecg.modules.system.message.model.po.SysMessageNotice;
import org.jeecg.modules.system.message.mapper.SysMessageNoticeMapper;
import org.jeecg.modules.system.message.model.po.SysMessageNoticeScope;
import org.jeecg.modules.system.message.model.vo.SysMessageNoticeVO;
import org.jeecg.modules.system.message.service.ISysMessageNoticeScopeService;
import org.jeecg.modules.system.message.service.ISysMessageNoticeService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;
/**
* 消息通知 ServiceImpl
*
* @author wangHao
* @date 2026-02-05
*/
@Service
@RequiredArgsConstructor
public class SysMessageNoticeServiceImpl extends ServiceImpl<SysMessageNoticeMapper, SysMessageNotice> implements ISysMessageNoticeService {
private final ISysCache sysCache;
private final ISysMessageNoticeScopeService scopeService;
private final SysDepartMapper departMapper;
private final SysUserMapper userMapper;
@Override
public IPage<SysMessageNoticeVO> queryPageList(SysMessageNoticeQuery query, Page<SysMessageNotice> page) {
LambdaQueryWrapper<SysMessageNotice> wrapper = new LambdaQueryWrapper<>();
setQueryData(query, wrapper, NoticeConstants.QUERY_SOURCE_ADMIN);
Page<SysMessageNotice> resultPage = this.page(page, wrapper);
if (CollUtil.isEmpty(resultPage.getRecords())) {
return new Page<>();
}
IPage<SysMessageNoticeVO> resultPageVO = new Page<>();
BeanUtil.copyProperties(resultPage, resultPageVO);
return resultPageVO.setRecords(setResultData(resultPage.getRecords()));
}
@Override
public List<SysMessageNoticeVO> queryPageAppList(SysMessageNoticeQuery query, Page<SysMessageNotice> page) {
LambdaQueryWrapper<SysMessageNotice> wrapper = new LambdaQueryWrapper<>();
setQueryData(query, wrapper, NoticeConstants.QUERY_SOURCE_APP);
Page<SysMessageNotice> resultPage = this.page(page, wrapper);
if (CollUtil.isEmpty(resultPage.getRecords())) {
return new ArrayList<>();
}
return setResultData(resultPage.getRecords());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void add(SysMessageNoticeDTO dto) {
// 前置处理
beforeHandle(dto);
// 保存消息
dto.setOrgCode(GlobalUtils.getLoginUser().getOrgCode());
this.save(dto);
// 保存范围
List<SysMessageNoticeScope> scopes = new ArrayList<>();
if (CollUtil.isNotEmpty(dto.getDepartScopeList())) {
scopes.addAll(dto.getDepartScopeList());
}
if (CollUtil.isNotEmpty(dto.getUserScopeList())) {
scopes.addAll(dto.getUserScopeList());
}
if (CollUtil.isNotEmpty(scopes)) {
for (SysMessageNoticeScope scope : scopes) {
scope.setNoticeId(dto.getId());
}
scopeService.saveBatch(scopes);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void edit(SysMessageNoticeDTO dto) {
// 前置处理
beforeHandle(dto);
// 删除范围
scopeService.remove(new LambdaQueryWrapper<SysMessageNoticeScope>().eq(SysMessageNoticeScope::getNoticeId, dto.getId()));
// 保存范围
List<SysMessageNoticeScope> scopes = new ArrayList<>();
if (CollUtil.isNotEmpty(dto.getDepartScopeList())) {
scopes.addAll(dto.getDepartScopeList());
}
if (CollUtil.isNotEmpty(dto.getUserScopeList())) {
scopes.addAll(dto.getUserScopeList());
}
if (CollUtil.isNotEmpty(scopes)) {
for (SysMessageNoticeScope scope : scopes) {
scope.setNoticeId(dto.getId());
}
scopeService.saveBatch(scopes);
}
// 更新消息
this.updateById(dto);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteNotice(String id) {
// 先删除关联的范围表
scopeService.remove(new LambdaQueryWrapper<SysMessageNoticeScope>().eq(SysMessageNoticeScope::getNoticeId, id));
// 再删除主表
this.removeById(id);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteBatchNotice(List<String> ids) {
// 先删除关联的范围表
scopeService.remove(new LambdaQueryWrapper<SysMessageNoticeScope>().in(SysMessageNoticeScope::getNoticeId, ids));
// 再删除主表
this.removeByIds(ids);
}
@Override
public SysMessageNotice getNoticeById(String id) {
SysMessageNotice notice = this.getById(id);
if (notice == null) {
ExceptionAssertsUtil.fail("未找到对应数据");
}
return notice;
}
/**
* 设置查询条件
*
* @param query 查询参数
* @param wrapper 查询包装器
* @param source 查询来源(0-后台管理,1-移动端)
*/
private void setQueryData(SysMessageNoticeQuery query, LambdaQueryWrapper<SysMessageNotice> wrapper, Integer source) {
// 设置权限过滤
applyPermissionFilter(wrapper, source);
// 设置查询条件
applyQueryConditions(query, wrapper);
// 设置排序
wrapper.orderByDesc(SysMessageNotice::getCreateTime);
}
/**
* 应用权限过滤
*
* @param wrapper 查询包装器
* @param source 查询来源
*/
private void applyPermissionFilter(LambdaQueryWrapper<SysMessageNotice> wrapper, Integer source) {
if (NoticeConstants.QUERY_SOURCE_ADMIN.equals(source)) {
applyAdminPermissionFilter(wrapper);
} else if (NoticeConstants.QUERY_SOURCE_APP.equals(source)) {
applyAppPermissionFilter(wrapper);
}
}
/**
* 应用后台管理权限过滤
*
* @param wrapper 查询包装器
*/
private void applyAdminPermissionFilter(LambdaQueryWrapper<SysMessageNotice> wrapper) {
LoginUser loginUser = GlobalUtils.getLoginUser();
/*
* 1.后台接口的消息列表需要做一定数据权限控制需考虑的角色有admin,system,second_manager,third_manager
* 2.admin,system可查看所有的活动
* 3.second_manager,third_manager可查看管理单位下的消息列表
*/
boolean permissions = false;
List<String> roleCodes = Arrays.asList(Objects.toString(loginUser.getRoleCodes(), "").split(","));
if (roleCodes.contains(RoleEnum.ADMIN_MANAGER.roleName) || roleCodes.contains(RoleEnum.SYSTEM.roleName) || roleCodes.contains(RoleEnum.WEIGHT_PLAN_ADMIN.roleName)) {
permissions = true;
} else if (roleCodes.contains(RoleEnum.SECOND_MANAGER.roleName)) {
if (StrUtil.isNotEmpty(loginUser.getDepartCodes())) {
List<String> manageDepartCode = Arrays.stream(loginUser.getDepartCodes().split(",")).collect(Collectors.toList());
wrapper.in(SysMessageNotice::getOrgCode, manageDepartCode);
permissions = true;
}
}
// 没有权限,返回空结果
wrapper.eq(!permissions, SysMessageNotice::getId, "-1");
}
/**
* 应用移动端权限过滤
*
* @param wrapper 查询包装器
*/
private void applyAppPermissionFilter(LambdaQueryWrapper<SysMessageNotice> wrapper) {
LoginUser loginUser = GlobalUtils.getLoginUser();
// 默认条件只获取当前登录用户所能看到的消息范围
List<String> noticeIds = getAccessibleNoticeIds(loginUser);
if (CollUtil.isEmpty(noticeIds)) {
// 没有可以查看的通知
wrapper.eq(SysMessageNotice::getId, "-1");
} else {
wrapper.in(SysMessageNotice::getId, noticeIds);
}
}
/**
* 获取用户可访问的通知ID列表
*
* @param loginUser 登录用户
* @return 通知ID列表
*/
private List<String> getAccessibleNoticeIds(LoginUser loginUser) {
LambdaQueryWrapper<SysMessageNoticeScope> scopeWrapper = new LambdaQueryWrapper<>();
scopeWrapper.select(SysMessageNoticeScope::getNoticeId)
.eq(SysMessageNoticeScope::getScopeId, loginUser.getId())
.or()
.likeRight(SysMessageNoticeScope::getScopeId,
sysCache.getDepartByOrgCode(GlobalUtils.getFirstDepartOrgCode(loginUser.getOrgCode())).getOrgCode())
.or()
.likeRight(SysMessageNoticeScope::getScopeId,
sysCache.getDepartByOrgCode(GlobalUtils.getSecondDepartOrgCode(loginUser.getOrgCode())).getOrgCode())
.or()
.likeRight(SysMessageNoticeScope::getScopeId,
sysCache.getDepartByOrgCode(GlobalUtils.getThirdDepartOrgCode(loginUser.getOrgCode())).getOrgCode())
.or()
.likeRight(SysMessageNoticeScope::getScopeId,
sysCache.getDepartByOrgCode(loginUser.getOrgCode()).getOrgCode());
List<SysMessageNoticeScope> planScopes = scopeService.list(scopeWrapper);
return planScopes.stream().map(SysMessageNoticeScope::getNoticeId).collect(Collectors.toList());
}
/**
* 应用查询条件
*
* @param query 查询参数
* @param wrapper 查询包装器
*/
private void applyQueryConditions(SysMessageNoticeQuery query, LambdaQueryWrapper<SysMessageNotice> wrapper) {
if (query == null) {
return;
}
if (StrUtil.isNotEmpty(query.getTitle())) {
wrapper.like(SysMessageNotice::getTitle, query.getTitle());
}
if (StrUtil.isNotEmpty(query.getScopeType())) {
wrapper.eq(SysMessageNotice::getScopeType, query.getScopeType());
}
if (ObjUtil.isNotEmpty(query.getStartDate()) && ObjUtil.isNotEmpty(query.getEndDate())) {
wrapper.between(SysMessageNotice::getCreateTime, DateUtil.beginOfDay(query.getStartDate()), DateUtil.endOfDay(query.getEndDate()));
}
}
/**
* 设置结果数据
* 将消息通知列表转换为VO列表,并填充范围信息
*
* @param list 消息通知列表
* @return VO列表
*/
private List<SysMessageNoticeVO> setResultData(List<SysMessageNotice> list) {
if (CollUtil.isEmpty(list)) {
return new ArrayList<>();
}
// 获取范围数据并分组
List<String> noticeIds = list.stream().map(SysMessageNotice::getId).collect(Collectors.toList());
LambdaQueryWrapper<SysMessageNoticeScope> scopeWrapper = new LambdaQueryWrapper<>();
scopeWrapper.in(SysMessageNoticeScope::getNoticeId, noticeIds);
List<SysMessageNoticeScope> scopeList = scopeService.list(scopeWrapper);
Map<String, List<SysMessageNoticeScope>> scopeMap =
scopeList.stream().collect(Collectors.groupingBy(SysMessageNoticeScope::getNoticeId));
// 构建结果列表
List<SysMessageNoticeVO> resultList = new ArrayList<>();
for (SysMessageNotice notice : list) {
SysMessageNoticeVO noticeVO = new SysMessageNoticeVO(notice);
List<SysMessageNoticeScope> scopes = scopeMap.get(notice.getId());
if (CollUtil.isNotEmpty(scopes)) {
// 根据type分组
Map<Integer, List<SysMessageNoticeScope>> scopeTypeMap = scopes.stream().collect(Collectors.groupingBy(SysMessageNoticeScope::getType));
// 设置部门和人群范围(添加空值检查)
noticeVO.setDepartScopeList(scopeTypeMap.getOrDefault(NoticeConstants.NOTICE_SCOPE_TYPE_DEPART, new ArrayList<>()));
noticeVO.setUserScopeList(scopeTypeMap.getOrDefault(NoticeConstants.NOTICE_SCOPE_TYPE_PEOPLE, new ArrayList<>()));
}
resultList.add(noticeVO);
}
return resultList;
}
/**
* 前置处理
* 校验数据并填充范围信息
*
* @param data 消息通知数据
*/
private void beforeHandle(SysMessageNoticeDTO data) {
// 校验范围类型
if (data.getScopeType() == null) {
ExceptionAssertsUtil.fail("未知类型!");
}
// 校验范围数据
if (CollUtil.isEmpty(data.getDepartScopeList()) && CollUtil.isEmpty(data.getUserScopeList())) {
ExceptionAssertsUtil.fail("未选择部门或者员工!");
}
// 校验时间范围
if (data.getStartTime() != null && data.getEndTime() != null) {
if (data.getStartTime().after(data.getEndTime())) {
ExceptionAssertsUtil.fail("开始时间不能晚于结束时间!");
}
}
// 处理部门范围
if (NoticeConstants.NOTICE_SCOPE_TYPE_DEPART.equals(data.getScopeType())
|| NoticeConstants.NOTICE_SCOPE_TYPE_BOTH.equals(data.getScopeType())) {
if (CollUtil.isNotEmpty(data.getDepartScopeList())) {
processDepartScopes(data.getDepartScopeList());
}
}
// 处理人群范围
if (NoticeConstants.NOTICE_SCOPE_TYPE_PEOPLE.equals(data.getScopeType())
|| NoticeConstants.NOTICE_SCOPE_TYPE_BOTH.equals(data.getScopeType())) {
if (CollUtil.isNotEmpty(data.getUserScopeList())) {
processUserScopes(data.getUserScopeList());
}
}
}
/**
* 处理部门范围
* 填充部门名称
*
* @param departScopes 部门范围列表
*/
private void processDepartScopes(List<SysMessageNoticeScope> departScopes) {
Set<String> orgCodes = departScopes.stream()
.map(SysMessageNoticeScope::getScopeId)
.collect(Collectors.toSet());
List<SysDepart> departs = departMapper.selectNameByCodes(orgCodes);
Map<String, String> departNameMap = departs.stream()
.collect(Collectors.toMap(SysDepart::getOrgCode, SysDepart::getDepartName));
for (SysMessageNoticeScope scope : departScopes) {
scope.setType(NoticeConstants.NOTICE_SCOPE_TYPE_DEPART);
String departName = departNameMap.get(scope.getScopeId());
if (StrUtil.isNotEmpty(departName)) {
scope.setRecord(departName);
}
}
}
/**
* 处理人群范围
* 填充人员姓名
*
* @param userScopes 人群范围列表
*/
private void processUserScopes(List<SysMessageNoticeScope> userScopes) {
Set<String> userIds = userScopes.stream()
.map(SysMessageNoticeScope::getScopeId)
.collect(Collectors.toSet());
List<SysUser> users = userMapper.selectNameByIds(userIds);
Map<String, String> userNameMap = users.stream()
.collect(Collectors.toMap(SysUser::getId, SysUser::getRealname));
for (SysMessageNoticeScope scope : userScopes) {
scope.setType(NoticeConstants.NOTICE_SCOPE_TYPE_PEOPLE);
String userName = userNameMap.get(scope.getScopeId());
if (StrUtil.isNotEmpty(userName)) {
scope.setRecord(userName);
}
}
}
}