[add] 消息通知

This commit is contained in:
lcj
2025-08-05 19:15:37 +08:00
parent ce68cca915
commit fcf810ed7a
41 changed files with 2838 additions and 0 deletions

View File

@ -0,0 +1,95 @@
package org.dromara.message.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.dromara.common.core.domain.R;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.web.core.BaseController;
import org.dromara.message.domain.dto.notification.MsgNotificationQueryReq;
import org.dromara.message.domain.dto.notification.MsgNotificationSendMessageReq;
import org.dromara.message.domain.vo.notification.MsgNotificationVo;
import org.dromara.message.service.IMsgNotificationService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 消息通知
*
* @author lilemy
* @date 2025-08-05
*/
@Validated
@RestController
@RequestMapping("/message/notification")
public class MsgNotificationController extends BaseController {
@Resource
private IMsgNotificationService msgNotificationService;
/**
* 查询当前登录用户发送的消息通知列表
*/
@SaCheckPermission("message:notification:listLoginSend")
@GetMapping("/list/send/login")
public TableDataInfo<MsgNotificationVo> listByLoginSend(MsgNotificationQueryReq req, PageQuery pageQuery) {
Long userId = LoginHelper.getUserId();
req.setSenderId(userId);
return msgNotificationService.queryPageList(req, pageQuery);
}
/**
* 查询当前登录用户接收的消息通知列表
*/
@SaCheckPermission("message:notification:listLoginRecipient")
@GetMapping("/list/recipient/login")
public TableDataInfo<MsgNotificationVo> listByLoginRecipient(MsgNotificationQueryReq req, PageQuery pageQuery) {
Long userId = LoginHelper.getUserId();
req.setRecipientId(userId);
return msgNotificationService.queryPageList(req, pageQuery);
}
/**
* 获取消息通知详细信息
*
* @param id 主键
*/
@SaCheckPermission("message:notification:query")
@GetMapping("/{id}")
public R<MsgNotificationVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(msgNotificationService.queryById(id));
}
/**
* 新增消息通知
*/
@SaCheckPermission("message:notification:send")
@Log(title = "消息通知", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/send")
public R<Void> sendMessage(@Validated @RequestBody MsgNotificationSendMessageReq req) {
return toAjax(msgNotificationService.sendMessage(req));
}
/**
* 删除消息通知
*
* @param ids 主键串
*/
@SaCheckPermission("message:notification:remove")
@Log(title = "消息通知", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(msgNotificationService.deleteByIds(List.of(ids)));
}
}

View File

@ -0,0 +1,106 @@
package org.dromara.message.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.message.domain.dto.notifytarget.MsgNotifyTargetCreateReq;
import org.dromara.message.domain.dto.notifytarget.MsgNotifyTargetQueryReq;
import org.dromara.message.domain.dto.notifytarget.MsgNotifyTargetUpdateReq;
import org.dromara.message.domain.vo.notifytarget.MsgNotifyTargetVo;
import org.dromara.message.service.IMsgNotifyTargetService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 通知人员配置
*
* @author lilemy
* @date 2025-08-05
*/
@Validated
@RestController
@RequestMapping("/message/notifyTarget")
public class MsgNotifyTargetController extends BaseController {
@Resource
private IMsgNotifyTargetService msgNotifyTargetService;
/**
* 查询通知人员配置列表
*/
@SaCheckPermission("message:notifyTarget:list")
@GetMapping("/list")
public TableDataInfo<MsgNotifyTargetVo> list(MsgNotifyTargetQueryReq req, PageQuery pageQuery) {
return msgNotifyTargetService.queryPageList(req, pageQuery);
}
/**
* 导出通知人员配置列表
*/
@SaCheckPermission("message:notifyTarget:export")
@Log(title = "通知人员配置", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(MsgNotifyTargetQueryReq req, HttpServletResponse response) {
List<MsgNotifyTargetVo> list = msgNotifyTargetService.queryList(req);
ExcelUtil.exportExcel(list, "通知人员配置", MsgNotifyTargetVo.class, response);
}
/**
* 获取通知人员配置详细信息
*
* @param id 主键
*/
@SaCheckPermission("message:notifyTarget:query")
@GetMapping("/{id}")
public R<MsgNotifyTargetVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(msgNotifyTargetService.queryById(id));
}
/**
* 新增通知人员配置
*/
@SaCheckPermission("message:notifyTarget:add")
@Log(title = "通知人员配置", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated @RequestBody MsgNotifyTargetCreateReq req) {
return toAjax(msgNotifyTargetService.insertByBo(req));
}
/**
* 修改通知人员配置
*/
@SaCheckPermission("message:notifyTarget:edit")
@Log(title = "通知人员配置", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated @RequestBody MsgNotifyTargetUpdateReq req) {
return toAjax(msgNotifyTargetService.updateByBo(req));
}
/**
* 删除通知人员配置
*
* @param ids 主键串
*/
@SaCheckPermission("message:notifyTarget:remove")
@Log(title = "通知人员配置", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(msgNotifyTargetService.deleteByIds(List.of(ids)));
}
}

View File

@ -0,0 +1,105 @@
package org.dromara.message.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.web.core.BaseController;
import org.dromara.message.domain.dto.typeconfig.MsgTypeConfigCreateReq;
import org.dromara.message.domain.dto.typeconfig.MsgTypeConfigQueryReq;
import org.dromara.message.domain.dto.typeconfig.MsgTypeConfigUpdateReq;
import org.dromara.message.domain.vo.typeconfig.MsgTypeConfigVo;
import org.dromara.message.service.IMsgTypeConfigService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 消息类型配置
*
* @author lilemy
* @date 2025-08-05
*/
@Validated
@RestController
@RequestMapping("/message/typeConfig")
public class MsgTypeConfigController extends BaseController {
@Resource
private IMsgTypeConfigService msgTypeConfigService;
/**
* 查询消息类型配置列表
*/
@SaCheckPermission("message:typeConfig:list")
@GetMapping("/list")
public R<List<MsgTypeConfigVo>> list(MsgTypeConfigQueryReq req) {
List<MsgTypeConfigVo> list = msgTypeConfigService.queryList(req);
return R.ok(list);
}
/**
* 导出消息类型配置列表
*/
@SaCheckPermission("message:typeConfig:export")
@Log(title = "消息类型配置", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(MsgTypeConfigQueryReq req, HttpServletResponse response) {
List<MsgTypeConfigVo> list = msgTypeConfigService.queryList(req);
ExcelUtil.exportExcel(list, "消息类型配置", MsgTypeConfigVo.class, response);
}
/**
* 获取消息类型配置详细信息
*
* @param id 主键
*/
@SaCheckPermission("message:typeConfig:query")
@GetMapping("/{id}")
public R<MsgTypeConfigVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(msgTypeConfigService.queryById(id));
}
/**
* 新增消息类型配置
*/
@SaCheckPermission("message:typeConfig:add")
@Log(title = "消息类型配置", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Long> add(@Validated @RequestBody MsgTypeConfigCreateReq req) {
return R.ok(msgTypeConfigService.insertByBo(req));
}
/**
* 修改消息类型配置
*/
@SaCheckPermission("message:typeConfig:edit")
@Log(title = "消息类型配置", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated @RequestBody MsgTypeConfigUpdateReq req) {
return toAjax(msgTypeConfigService.updateByBo(req));
}
/**
* 删除消息类型配置
*
* @param ids 主键串
*/
@SaCheckPermission("message:typeConfig:remove")
@Log(title = "消息类型配置", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(msgTypeConfigService.deleteByIds(List.of(ids)));
}
}

View File

@ -0,0 +1,95 @@
package org.dromara.message.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 消息通知对象 msg_notification
*
* @author lilemy
* @date 2025-08-05
*/
@Data
@TableName("msg_notification")
public class MsgNotification implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 项目ID
*/
private Long projectId;
/**
* 接收通知的用户ID
*/
private Long recipientId;
/**
* 发送通知的用户ID系统通知 0
*/
private Long senderId;
/**
* 通知类型ID
*/
private Long typeId;
/**
* 通知标题
*/
private String title;
/**
* 通知的主要内容
*/
private String content;
/**
* 查看状态(0未读 1已读)
*/
private String viewStatus;
/**
* 查看时间
*/
private Date viewTime;
/**
* 点击通知后跳转的目标URL
*/
private String actionUrl;
/**
* 通知附件
*/
private String file;
/**
* 备注
*/
private String remark;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}

View File

@ -0,0 +1,51 @@
package org.dromara.message.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.io.Serial;
/**
* 通知人员配置对象 msg_notify_target
*
* @author lilemy
* @date 2025-08-05
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("msg_notify_target")
public class MsgNotifyTarget extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 项目ID
*/
private Long projectId;
/**
* 消息类型ID
*/
private Long typeId;
/**
* 是否启用(0正常 1禁用)
*/
private String status;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,44 @@
package org.dromara.message.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 通知人员配置详情对象 msg_notify_target_detail
*
* @author lilemy
* @date 2025-08-05
*/
@Data
@TableName("msg_notify_target_detail")
public class MsgNotifyTargetDetail implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 消息类型配置ID
*/
private Long configId;
/**
* 接收类型
*/
private String targetType;
/**
* 接收ID
*/
private Long targetId;
}

View File

@ -0,0 +1,66 @@
package org.dromara.message.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.io.Serial;
/**
* 消息类型配置对象 msg_type_config
*
* @author lilemy
* @date 2025-08-05
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("msg_type_config")
public class MsgTypeConfig extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 项目ID
*/
private Long projectId;
/**
* 父ID
*/
private Long parentId;
/**
* 消息类型编码
*/
private String typeCode;
/**
* 消息类型名称
*/
private String typeName;
/**
* 消息类型描述
*/
private String typeDesc;
/**
* 是否启用(0正常 1禁用)
*/
private String status;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,47 @@
package org.dromara.message.domain.dto.notification;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lilemy
* @date 2025-08-05 14:58
*/
@Data
public class MsgNotificationQueryReq implements Serializable {
@Serial
private static final long serialVersionUID = 6297082509342761841L;
/**
* 项目ID
*/
private Long projectId;
/**
* 接收通知的用户ID
*/
private Long recipientId;
/**
* 发送通知的用户ID系统通知 0
*/
private Long senderId;
/**
* 通知类型ID
*/
private Long typeId;
/**
* 通知标题
*/
private String title;
/**
* 查看状态(0未读 1已读)
*/
private String viewStatus;
}

View File

@ -0,0 +1,64 @@
package org.dromara.message.domain.dto.notification;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lilemy
* @date 2025-08-05 15:04
*/
@Data
public class MsgNotificationSendMessageReq implements Serializable {
@Serial
private static final long serialVersionUID = -3818662203023362981L;
/**
* 项目ID
*/
private Long projectId;
/**
* 接收通知的用户ID
*/
@NotNull(message = "接收通知的用户ID不能为空")
private Long recipientId;
/**
* 通知类型ID
*/
@NotNull(message = "通知类型ID不能为空")
private Long typeId;
/**
* 通知标题
*/
@NotBlank(message = "通知标题不能为空")
private String title;
/**
* 通知的主要内容
*/
@NotBlank(message = "通知主要内容不能为空")
private String content;
/**
* 点击通知后跳转的目标URL
*/
private String actionUrl;
/**
* 通知附件
*/
private String file;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,50 @@
package org.dromara.message.domain.dto.notifytarget;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.dromara.message.domain.dto.notifytargetdetail.MsgNotifyTargetDetailCreateDto;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* @author lilemy
* @date 2025-08-05 16:14
*/
@Data
public class MsgNotifyTargetCreateReq implements Serializable {
@Serial
private static final long serialVersionUID = 6726656627774354043L;
/**
* 项目ID
*/
@NotNull(message = "项目ID不能为空")
private Long projectId;
/**
* 消息类型ID
*/
@NotNull(message = "消息类型ID不能为空")
private Long typeId;
/**
* 通知人员配置详情列表
*/
@NotEmpty(message = "通知人员配置详情列表不能为空")
private List<MsgNotifyTargetDetailCreateDto> detailList;
/**
* 是否启用(0正常 1禁用)
*/
private String status;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,33 @@
package org.dromara.message.domain.dto.notifytarget;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lilemy
* @date 2025-08-05 16:14
*/
@Data
public class MsgNotifyTargetQueryReq implements Serializable {
@Serial
private static final long serialVersionUID = -1555802564920782912L;
/**
* 项目ID
*/
private Long projectId;
/**
* 消息类型ID
*/
private Long typeId;
/**
* 是否启用(0正常 1禁用)
*/
private String status;
}

View File

@ -0,0 +1,54 @@
package org.dromara.message.domain.dto.notifytarget;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.dromara.message.domain.dto.notifytargetdetail.MsgNotifyTargetDetailUpdateDto;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* @author lilemy
* @date 2025-08-05 16:15
*/
@Data
public class MsgNotifyTargetUpdateReq implements Serializable {
@Serial
private static final long serialVersionUID = -8315266866313669088L;
/**
* 主键ID
*/
@NotNull(message = "主键ID不能为空")
private Long id;
/**
* 项目ID
*/
private Long projectId;
/**
* 消息类型ID
*/
private Long typeId;
/**
* 通知人员配置详情列表
*/
@NotEmpty(message = "通知人员配置详情列表不能为空")
private List<MsgNotifyTargetDetailUpdateDto> detailList;
/**
* 是否启用(0正常 1禁用)
*/
private String status;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,26 @@
package org.dromara.message.domain.dto.notifytargetdetail;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author lilemy
* @date 2025-08-05 17:34
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MsgNotifyTargetDetailCreateDto {
/**
* 接收类型
*/
private String targetType;
/**
* 接收ID
*/
private Long targetId;
}

View File

@ -0,0 +1,32 @@
package org.dromara.message.domain.dto.notifytargetdetail;
import lombok.Data;
/**
* @author lilemy
* @date 2025-08-05 18:54
*/
@Data
public class MsgNotifyTargetDetailUpdateDto {
/**
* 主键ID
*/
private Long id;
/**
* 消息类型配置ID
*/
private Long configId;
/**
* 接收类型
*/
private String targetType;
/**
* 接收ID
*/
private Long targetId;
}

View File

@ -0,0 +1,59 @@
package org.dromara.message.domain.dto.typeconfig;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import org.dromara.message.domain.dto.notifytargetdetail.MsgNotifyTargetDetailCreateDto;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* @author lilemy
* @date 2025-08-05 11:55
*/
@Data
public class MsgTypeConfigCreateReq implements Serializable {
@Serial
private static final long serialVersionUID = 7200792696273923100L;
/**
* 项目ID
*/
private Long projectId;
/**
* 父ID
*/
private Long parentId;
/**
* 消息类型编码
*/
@NotBlank(message = "消息类型编码不能为空")
private String typeCode;
/**
* 消息类型名称
*/
@NotBlank(message = "消息类型名称不能为空")
private String typeName;
/**
* 消息类型描述
*/
private String typeDesc;
/**
* 是否启用(0正常 1禁用)
*/
private String status;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,42 @@
package org.dromara.message.domain.dto.typeconfig;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lilemy
* @date 2025-08-05 11:56
*/
@Data
public class MsgTypeConfigQueryReq implements Serializable {
@Serial
private static final long serialVersionUID = 4107391817233057971L;
/**
* 项目ID
*/
private Long projectId;
/**
* 父ID
*/
private Long parentId;
/**
* 消息类型编码
*/
private String typeCode;
/**
* 消息类型名称
*/
private String typeName;
/**
* 是否启用(0正常 1禁用)
*/
private String status;
}

View File

@ -0,0 +1,55 @@
package org.dromara.message.domain.dto.typeconfig;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lilemy
* @date 2025-08-05 11:55
*/
@Data
public class MsgTypeConfigUpdateReq implements Serializable {
@Serial
private static final long serialVersionUID = -9036270979980317352L;
/**
* 主键ID
*/
@NotNull(message = "主键ID不能为空")
private Long id;
/**
* 父ID
*/
private Long parentId;
/**
* 消息类型编码
*/
private String typeCode;
/**
* 消息类型名称
*/
private String typeName;
/**
* 消息类型描述
*/
private String typeDesc;
/**
* 是否启用(0正常 1禁用)
*/
private String status;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,25 @@
package org.dromara.message.domain.enums;
import lombok.Getter;
/**
* @author lilemy
* @date 2025-08-05 16:47
*/
@Getter
public enum MsgMessageTargetType {
USER("用户", "1"),
ROLE("角色", "2"),
DEPT("部门", "3");
private final String text;
private final String value;
MsgMessageTargetType(String text, String value) {
this.text = text;
this.value = value;
}
}

View File

@ -0,0 +1,24 @@
package org.dromara.message.domain.enums;
import lombok.Getter;
/**
* @author lilemy
* @date 2025-08-05 15:39
*/
@Getter
public enum MsgMessageViewStatus {
NO("未读", "0"),
YSE("已读", "1");
private final String text;
private final String value;
MsgMessageViewStatus(String text, String value) {
this.text = text;
this.value = value;
}
}

View File

@ -0,0 +1,108 @@
package org.dromara.message.domain.vo.notification;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import org.dromara.message.domain.MsgNotification;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 消息通知视图对象 msg_notification
*
* @author lilemy
* @date 2025-08-05
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = MsgNotification.class)
public class MsgNotificationVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 项目ID
*/
@ExcelProperty(value = "项目ID")
private Long projectId;
/**
* 接收通知的用户ID
*/
@ExcelProperty(value = "接收通知的用户ID")
private Long recipientId;
/**
* 发送通知的用户ID系统通知 0
*/
@ExcelProperty(value = "发送通知的用户ID")
private Long senderId;
/**
* 发送通知的用户名称
*/
private String senderName;
/**
* 通知类型ID
*/
@ExcelProperty(value = "通知类型ID")
private Long typeId;
/**
* 通知标题
*/
@ExcelProperty(value = "通知标题")
private String title;
/**
* 通知的主要内容
*/
@ExcelProperty(value = "通知的主要内容")
private String content;
/**
* 查看状态(0未读 1已读)
*/
@ExcelProperty(value = "查看状态(0未读 1已读)", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "view_status")
private String viewStatus;
/**
* 查看时间
*/
@ExcelProperty(value = "查看时间")
private Date viewTime;
/**
* 点击通知后跳转的目标URL
*/
@ExcelProperty(value = "点击通知后跳转的目标URL")
private String actionUrl;
/**
* 通知附件
*/
@ExcelProperty(value = "通知附件")
private String file;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
}

View File

@ -0,0 +1,67 @@
package org.dromara.message.domain.vo.notifytarget;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import org.dromara.message.domain.MsgNotifyTarget;
import org.dromara.message.domain.vo.notifytargetdetail.MsgNotifyTargetDetailVo;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 通知人员配置视图对象 msg_notify_target
*
* @author lilemy
* @date 2025-08-05
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = MsgNotifyTarget.class)
public class MsgNotifyTargetVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 项目ID
*/
@ExcelProperty(value = "项目ID")
private Long projectId;
/**
* 消息类型ID
*/
@ExcelProperty(value = "消息类型ID")
private Long typeId;
/**
* 通知人员配置详情列表
*/
private List<MsgNotifyTargetDetailVo> detailList;
/**
* 是否启用(0正常 1禁用)
*/
@ExcelProperty(value = "是否启用(0正常 1禁用)", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "sys_notice_status")
private String status;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
}

View File

@ -0,0 +1,49 @@
package org.dromara.message.domain.vo.notifytargetdetail;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.message.domain.MsgNotifyTargetDetail;
import java.io.Serial;
import java.io.Serializable;
/**
* 通知人员配置详情视图对象 msg_notify_target_detail
*
* @author lilemy
* @date 2025-08-05
*/
@Data
@AutoMapper(target = MsgNotifyTargetDetail.class)
public class MsgNotifyTargetDetailVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
private Long id;
/**
* 消息类型配置ID
*/
private Long configId;
/**
* 接收类型
*/
private String targetType;
/**
* 接收ID
*/
private Long targetId;
/**
* 接收名称
*/
private String targetName;
}

View File

@ -0,0 +1,78 @@
package org.dromara.message.domain.vo.typeconfig;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import org.dromara.message.domain.MsgTypeConfig;
import java.io.Serial;
import java.io.Serializable;
/**
* 消息类型配置视图对象 msg_type_config
*
* @author lilemy
* @date 2025-08-05
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = MsgTypeConfig.class)
public class MsgTypeConfigVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 项目ID
*/
@ExcelProperty(value = "项目ID")
private Long projectId;
/**
* 父ID
*/
@ExcelProperty(value = "父ID")
private Long parentId;
/**
* 消息类型编码
*/
@ExcelProperty(value = "消息类型编码")
private String typeCode;
/**
* 消息类型名称
*/
@ExcelProperty(value = "消息类型名称")
private String typeName;
/**
* 消息类型描述
*/
@ExcelProperty(value = "消息类型描述")
private String typeDesc;
/**
* 是否启用(0正常 1禁用)
*/
@ExcelProperty(value = "是否启用(0正常 1禁用)", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "sys_notice_status")
private String status;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
}

View File

@ -0,0 +1,15 @@
package org.dromara.message.mapper;
import org.dromara.message.domain.MsgNotification;
import org.dromara.message.domain.vo.notification.MsgNotificationVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* 消息通知Mapper接口
*
* @author lilemy
* @date 2025-08-05
*/
public interface MsgNotificationMapper extends BaseMapperPlus<MsgNotification, MsgNotificationVo> {
}

View File

@ -0,0 +1,15 @@
package org.dromara.message.mapper;
import org.dromara.message.domain.MsgNotifyTargetDetail;
import org.dromara.message.domain.vo.notifytargetdetail.MsgNotifyTargetDetailVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* 通知人员配置详情Mapper接口
*
* @author lilemy
* @date 2025-08-05
*/
public interface MsgNotifyTargetDetailMapper extends BaseMapperPlus<MsgNotifyTargetDetail, MsgNotifyTargetDetailVo> {
}

View File

@ -0,0 +1,15 @@
package org.dromara.message.mapper;
import org.dromara.message.domain.MsgNotifyTarget;
import org.dromara.message.domain.vo.notifytarget.MsgNotifyTargetVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* 通知人员配置Mapper接口
*
* @author lilemy
* @date 2025-08-05
*/
public interface MsgNotifyTargetMapper extends BaseMapperPlus<MsgNotifyTarget, MsgNotifyTargetVo> {
}

View File

@ -0,0 +1,15 @@
package org.dromara.message.mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.message.domain.MsgTypeConfig;
import org.dromara.message.domain.vo.typeconfig.MsgTypeConfigVo;
/**
* 消息类型配置Mapper接口
*
* @author lilemy
* @date 2025-08-05
*/
public interface MsgTypeConfigMapper extends BaseMapperPlus<MsgTypeConfig, MsgTypeConfigVo> {
}

View File

@ -0,0 +1,96 @@
package org.dromara.message.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.message.domain.MsgNotification;
import org.dromara.message.domain.dto.notification.MsgNotificationQueryReq;
import org.dromara.message.domain.dto.notification.MsgNotificationSendMessageReq;
import org.dromara.message.domain.vo.notification.MsgNotificationVo;
import java.util.Collection;
import java.util.List;
/**
* 消息通知Service接口
*
* @author lilemy
* @date 2025-08-05
*/
public interface IMsgNotificationService extends IService<MsgNotification> {
/**
* 校验消息通知权限
*
* @param userId 用户ID
* @param notification 消息通知
*/
void validHasPermission(Long userId, MsgNotification notification);
/**
* 查询消息通知
*
* @param id 主键
* @return 消息通知
*/
MsgNotificationVo queryById(Long id);
/**
* 分页查询消息通知列表
*
* @param req 查询条件
* @param pageQuery 分页参数
* @return 消息通知分页列表
*/
TableDataInfo<MsgNotificationVo> queryPageList(MsgNotificationQueryReq req, PageQuery pageQuery);
/**
* 查询符合条件的消息通知列表
*
* @param req 查询条件
* @return 消息通知列表
*/
List<MsgNotificationVo> queryList(MsgNotificationQueryReq req);
/**
* 发送消息通知
*
* @param req 消息通知
* @return 是否发送成功
*/
Boolean sendMessage(MsgNotificationSendMessageReq req);
/**
* 批量删除消息通知信息
*
* @param ids 待删除的主键集合
* @return 是否删除成功
*/
Boolean deleteByIds(Collection<Long> ids);
/**
* 获取消息通知视图对象
*
* @param notification 消息通知对象
* @return 消息通知视图对象
*/
MsgNotificationVo getVo(MsgNotification notification);
/**
* 获取消息通知查询条件封装
*
* @param req 查询条件
* @return 查询条件封装
*/
LambdaQueryWrapper<MsgNotification> buildQueryWrapper(MsgNotificationQueryReq req);
/**
* 获取消息通知分页对象视图
*
* @param notificationPage 消息通知分页对象
* @return 消息通知分页详情对象视图
*/
Page<MsgNotificationVo> getVoPage(Page<MsgNotification> notificationPage);
}

View File

@ -0,0 +1,49 @@
package org.dromara.message.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.message.domain.MsgNotifyTargetDetail;
import org.dromara.message.domain.vo.notifytargetdetail.MsgNotifyTargetDetailVo;
import java.util.List;
/**
* 通知人员配置详情Service接口
*
* @author lilemy
* @date 2025-08-05
*/
public interface IMsgNotifyTargetDetailService extends IService<MsgNotifyTargetDetail> {
/**
* 根据通知目标ID查询用户ID列表
*
* @param targetId 通知目标ID
* @return 用户ID列表
*/
List<Long> queryUserIdsByTargetId(Long targetId);
/**
* 根据通知目标ID查询通知目标详情列表
*
* @param targetId 通知目标ID
* @return 通知目标详情列表
*/
List<MsgNotifyTargetDetailVo> queryVoListByTargetId(Long targetId);
/**
* 获取通知人员配置详情封装
*
* @param detail 通知人员配置详情
* @return 通知人员配置详情封装
*/
MsgNotifyTargetDetailVo getVo(MsgNotifyTargetDetail detail);
/**
* 批量获取通知人员配置详情封装
*
* @param detailList 通知人员配置详情列表
* @return 通知人员配置详情封装列表
*/
List<MsgNotifyTargetDetailVo> getVoList(List<MsgNotifyTargetDetail> detailList);
}

View File

@ -0,0 +1,97 @@
package org.dromara.message.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.message.domain.MsgNotifyTarget;
import org.dromara.message.domain.dto.notifytarget.MsgNotifyTargetCreateReq;
import org.dromara.message.domain.dto.notifytarget.MsgNotifyTargetQueryReq;
import org.dromara.message.domain.dto.notifytarget.MsgNotifyTargetUpdateReq;
import org.dromara.message.domain.vo.notifytarget.MsgNotifyTargetVo;
import java.util.Collection;
import java.util.List;
/**
* 通知人员配置Service接口
*
* @author lilemy
* @date 2025-08-05
*/
public interface IMsgNotifyTargetService extends IService<MsgNotifyTarget> {
/**
* 查询通知人员配置
*
* @param id 主键
* @return 通知人员配置
*/
MsgNotifyTargetVo queryById(Long id);
/**
* 分页查询通知人员配置列表
*
* @param req 查询条件
* @param pageQuery 分页参数
* @return 通知人员配置分页列表
*/
TableDataInfo<MsgNotifyTargetVo> queryPageList(MsgNotifyTargetQueryReq req, PageQuery pageQuery);
/**
* 查询符合条件的通知人员配置列表
*
* @param req 查询条件
* @return 通知人员配置列表
*/
List<MsgNotifyTargetVo> queryList(MsgNotifyTargetQueryReq req);
/**
* 新增通知人员配置
*
* @param req 通知人员配置
* @return 是否新增成功
*/
Boolean insertByBo(MsgNotifyTargetCreateReq req);
/**
* 修改通知人员配置
*
* @param req 通知人员配置
* @return 是否修改成功
*/
Boolean updateByBo(MsgNotifyTargetUpdateReq req);
/**
* 批量删除通知人员配置信息
*
* @param ids 待删除的主键集合
* @return 是否删除成功
*/
Boolean deleteByIds(Collection<Long> ids);
/**
* 获取通知人员配置视图对象
*
* @param notifyTarget 通知人员配置对象
* @return 通知人员配置视图对象
*/
MsgNotifyTargetVo getVo(MsgNotifyTarget notifyTarget);
/**
* 获取通知人员配置查询条件封装
*
* @param req 查询条件
* @return 查询条件封装
*/
LambdaQueryWrapper<MsgNotifyTarget> buildQueryWrapper(MsgNotifyTargetQueryReq req);
/**
* 获取通知人员配置分页对象视图
*
* @param notifyTargetPage 通知人员配置分页对象
* @return 通知人员配置分页详情对象视图
*/
Page<MsgNotifyTargetVo> getVoPage(Page<MsgNotifyTarget> notifyTargetPage);
}

View File

@ -0,0 +1,86 @@
package org.dromara.message.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.message.domain.MsgTypeConfig;
import org.dromara.message.domain.dto.typeconfig.MsgTypeConfigCreateReq;
import org.dromara.message.domain.dto.typeconfig.MsgTypeConfigQueryReq;
import org.dromara.message.domain.dto.typeconfig.MsgTypeConfigUpdateReq;
import org.dromara.message.domain.vo.typeconfig.MsgTypeConfigVo;
import java.util.Collection;
import java.util.List;
/**
* 消息类型配置Service接口
*
* @author lilemy
* @date 2025-08-05
*/
public interface IMsgTypeConfigService extends IService<MsgTypeConfig> {
/**
* 查询消息类型配置
*
* @param id 主键
* @return 消息类型配置
*/
MsgTypeConfigVo queryById(Long id);
/**
* 查询符合条件的消息类型配置列表
*
* @param req 查询条件
* @return 消息类型配置列表
*/
List<MsgTypeConfigVo> queryList(MsgTypeConfigQueryReq req);
/**
* 新增消息类型配置
*
* @param req 消息类型配置
* @return 新增成功消息类型配置id
*/
Long insertByBo(MsgTypeConfigCreateReq req);
/**
* 修改消息类型配置
*
* @param req 消息类型配置
* @return 是否修改成功
*/
Boolean updateByBo(MsgTypeConfigUpdateReq req);
/**
* 批量删除消息类型配置信息
*
* @param ids 待删除的主键集合
* @return 是否删除成功
*/
Boolean deleteByIds(Collection<Long> ids);
/**
* 获取消息类型配置视图对象
*
* @param typeConfig 消息类型配置对象
* @return 消息类型配置视图对象
*/
MsgTypeConfigVo getVo(MsgTypeConfig typeConfig);
/**
* 获取消息类型配置查询条件封装
*
* @param req 查询条件
* @return 查询条件封装
*/
LambdaQueryWrapper<MsgTypeConfig> buildQueryWrapper(MsgTypeConfigQueryReq req);
/**
* 获取消息类型配置分页对象视图
*
* @param typeConfigList 消息类型配置分页对象
* @return 消息类型配置分页对象视图
*/
List<MsgTypeConfigVo> getVoList(List<MsgTypeConfig> typeConfigList);
}

View File

@ -0,0 +1,280 @@
package org.dromara.message.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.sse.dto.SseMessageDto;
import org.dromara.common.sse.utils.SseMessageUtils;
import org.dromara.message.domain.MsgNotification;
import org.dromara.message.domain.MsgTypeConfig;
import org.dromara.message.domain.dto.notification.MsgNotificationQueryReq;
import org.dromara.message.domain.dto.notification.MsgNotificationSendMessageReq;
import org.dromara.message.domain.enums.MsgMessageViewStatus;
import org.dromara.message.domain.vo.notification.MsgNotificationVo;
import org.dromara.message.mapper.MsgNotificationMapper;
import org.dromara.message.service.IMsgNotificationService;
import org.dromara.message.service.IMsgTypeConfigService;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.service.ISysUserService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 消息通知Service业务层处理
*
* @author lilemy
* @date 2025-08-05
*/
@Service
public class MsgNotificationServiceImpl extends ServiceImpl<MsgNotificationMapper, MsgNotification>
implements IMsgNotificationService {
@Resource
private ISysUserService userService;
@Resource
private IMsgTypeConfigService typeConfigService;
/**
* 校验消息通知权限
*
* @param userId 用户ID
* @param notification 消息通知
*/
@Override
public void validHasPermission(Long userId, MsgNotification notification) {
Long senderId = notification.getSenderId();
Long recipientId = notification.getRecipientId();
if ((senderId == 0 && !userId.equals(recipientId))
|| (senderId != 0 && !userId.equals(senderId) && !userId.equals(recipientId))) {
throw new ServiceException("无权限处理此消息通知", HttpStatus.FORBIDDEN);
}
}
/**
* 查询消息通知
*
* @param id 主键
* @return 消息通知
*/
@Override
public MsgNotificationVo queryById(Long id) {
MsgNotification notification = this.getById(id);
if (notification == null) {
throw new ServiceException("消息通知信息不存在", HttpStatus.NOT_FOUND);
}
Long userId = LoginHelper.getUserId();
if (userId == null) {
throw new ServiceException("未获取到用户信息", HttpStatus.UNAUTHORIZED);
}
// 校验权限
validHasPermission(userId, notification);
// 如果为消息接收方,则修改状态为已阅读
if (userId.equals(notification.getRecipientId()) && notification.getViewStatus().equals(MsgMessageViewStatus.NO.getValue())) {
notification.setViewStatus(MsgMessageViewStatus.YSE.getValue());
notification.setViewTime(new Date());
this.updateById(notification);
}
return this.getVo(notification);
}
/**
* 分页查询消息通知列表
*
* @param req 查询条件
* @param pageQuery 分页参数
* @return 消息通知分页列表
*/
@Override
public TableDataInfo<MsgNotificationVo> queryPageList(MsgNotificationQueryReq req, PageQuery pageQuery) {
Page<MsgNotification> result = this.page(pageQuery.build(), this.buildQueryWrapper(req));
return TableDataInfo.build(this.getVoPage(result));
}
/**
* 查询符合条件的消息通知列表
*
* @param req 查询条件
* @return 消息通知列表
*/
@Override
public List<MsgNotificationVo> queryList(MsgNotificationQueryReq req) {
List<MsgNotification> list = this.list(this.buildQueryWrapper(req));
// 获取发送用户信息
List<Long> senderUserIds = list.stream()
.map(MsgNotification::getSenderId).distinct().filter(id -> id != null && id != 0).toList();
List<SysUserVo> senderUserVoList = userService.selectUserByIds(senderUserIds, null);
Map<Long, String> senderUserMap = senderUserVoList.stream()
.collect(Collectors.toMap(SysUserVo::getUserId, SysUserVo::getNickName));
return list.stream().map(notification -> {
MsgNotificationVo notificationVo = new MsgNotificationVo();
BeanUtils.copyProperties(notification, notificationVo);
// 关联发送用户信息
Long senderId = notification.getSenderId();
if (senderId != null && senderId != 0 && senderUserMap.containsKey(senderId)) {
notificationVo.setSenderName(senderUserMap.get(senderId));
}
return notificationVo;
}
).toList();
}
/**
* 发送消息通知
*
* @param req 消息通知
* @return 是否发送成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean sendMessage(MsgNotificationSendMessageReq req) {
// 记录发送消息
MsgNotification notification = new MsgNotification();
BeanUtils.copyProperties(req, notification);
Long recipientId = req.getRecipientId();
if (userService.selectUserById(recipientId) == null) {
throw new ServiceException("接收用户不存在", HttpStatus.NOT_FOUND);
}
Long typeId = notification.getTypeId();
MsgTypeConfig typeConfig = typeConfigService.getById(typeId);
if (typeConfig == null) {
throw new ServiceException("消息类型不存在", HttpStatus.NOT_FOUND);
}
// 填充默认值
Long userId = LoginHelper.getUserId();
if (userId == null) {
throw new ServiceException("未获取到用户信息", HttpStatus.UNAUTHORIZED);
}
notification.setSenderId(userId);
boolean save = this.save(notification);
if (!save) {
throw new ServiceException("发送消息失败", HttpStatus.ERROR);
}
// 使用 sse 发送消息
SseMessageDto sseMessageDto = new SseMessageDto();
sseMessageDto.setUserIds(List.of(recipientId));
String message = StringUtils.format("您有一条{}消息,请及时查看", typeConfig.getTypeName());
sseMessageDto.setMessage(message);
SseMessageUtils.publishMessage(sseMessageDto);
return true;
}
/**
* 批量删除消息通知信息
*
* @param ids 待删除的主键集合
* @return 是否删除成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteByIds(Collection<Long> ids) {
List<MsgNotification> notifications = this.listByIds(ids);
if (CollUtil.isEmpty(notifications)) {
return false;
}
Long userId = LoginHelper.getUserId();
notifications.forEach(notification -> validHasPermission(userId, notification));
return removeBatchByIds(ids);
}
/**
* 获取消息通知视图对象
*
* @param notification 消息通知对象
* @return 消息通知视图对象
*/
@Override
public MsgNotificationVo getVo(MsgNotification notification) {
MsgNotificationVo vo = new MsgNotificationVo();
if (notification == null) {
return vo;
}
BeanUtils.copyProperties(notification, vo);
// 获取发送用户信息
Long senderId = notification.getSenderId();
if (senderId != null && senderId != 0) {
SysUserVo userVo = userService.selectUserById(senderId);
vo.setSenderName(userVo.getNickName());
}
return vo;
}
/**
* 获取消息通知查询条件封装
*
* @param req 查询条件
* @return 查询条件封装
*/
@Override
public LambdaQueryWrapper<MsgNotification> buildQueryWrapper(MsgNotificationQueryReq req) {
LambdaQueryWrapper<MsgNotification> lqw = new LambdaQueryWrapper<>();
if (req == null) {
return lqw;
}
Long projectId = req.getProjectId();
Long recipientId = req.getRecipientId();
Long senderId = req.getSenderId();
Long typeId = req.getTypeId();
String title = req.getTitle();
String viewStatus = req.getViewStatus();
lqw.eq(ObjectUtils.isNotEmpty(projectId), MsgNotification::getProjectId, projectId);
lqw.eq(ObjectUtils.isNotEmpty(recipientId), MsgNotification::getRecipientId, recipientId);
lqw.eq(ObjectUtils.isNotEmpty(senderId), MsgNotification::getSenderId, senderId);
lqw.eq(ObjectUtils.isNotEmpty(typeId), MsgNotification::getTypeId, typeId);
lqw.like(StringUtils.isNotBlank(title), MsgNotification::getTitle, title);
lqw.eq(StringUtils.isNotBlank(viewStatus), MsgNotification::getViewStatus, viewStatus);
return lqw;
}
/**
* 获取消息通知分页对象视图
*
* @param notificationPage 消息通知分页对象
* @return 消息通知分页详情对象视图
*/
@Override
public Page<MsgNotificationVo> getVoPage(Page<MsgNotification> notificationPage) {
List<MsgNotification> notificationList = notificationPage.getRecords();
Page<MsgNotificationVo> notificationVoPage = new Page<>(
notificationPage.getCurrent(),
notificationPage.getSize(),
notificationPage.getTotal());
if (CollUtil.isEmpty(notificationList)) {
return notificationVoPage;
}
// 获取发送用户信息
List<Long> senderUserIds = notificationList.stream()
.map(MsgNotification::getSenderId).distinct().filter(id -> id != null && id != 0).toList();
List<SysUserVo> senderUserVoList = userService.selectUserByIds(senderUserIds, null);
Map<Long, String> senderUserMap = senderUserVoList.stream()
.collect(Collectors.toMap(SysUserVo::getUserId, SysUserVo::getNickName));
List<MsgNotificationVo> notificationVoList = notificationList.stream().map(notification -> {
MsgNotificationVo notificationVo = new MsgNotificationVo();
BeanUtils.copyProperties(notification, notificationVo);
// 关联发送用户信息
Long senderId = notification.getSenderId();
if (senderId != null && senderId != 0 && senderUserMap.containsKey(senderId)) {
notificationVo.setSenderName(senderUserMap.get(senderId));
}
return notificationVo;
}).toList();
notificationVoPage.setRecords(notificationVoList);
return notificationVoPage;
}
}

View File

@ -0,0 +1,166 @@
package org.dromara.message.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.message.domain.MsgNotifyTargetDetail;
import org.dromara.message.domain.enums.MsgMessageTargetType;
import org.dromara.message.domain.vo.notifytargetdetail.MsgNotifyTargetDetailVo;
import org.dromara.message.mapper.MsgNotifyTargetDetailMapper;
import org.dromara.message.service.IMsgNotifyTargetDetailService;
import org.dromara.system.domain.bo.SysUserBo;
import org.dromara.system.domain.vo.SysDeptVo;
import org.dromara.system.domain.vo.SysRoleVo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.service.ISysDeptService;
import org.dromara.system.service.ISysRoleService;
import org.dromara.system.service.ISysUserService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* 通知人员配置详情Service业务层处理
*
* @author lilemy
* @date 2025-08-05
*/
@Service
public class MsgNotifyTargetDetailServiceImpl extends ServiceImpl<MsgNotifyTargetDetailMapper, MsgNotifyTargetDetail>
implements IMsgNotifyTargetDetailService {
@Resource
private ISysUserService userService;
@Resource
private ISysRoleService roleService;
@Resource
private ISysDeptService deptService;
/**
* 根据通知目标ID查询用户ID列表
*
* @param targetId 通知目标ID
* @return 用户ID列表
*/
@Override
public List<Long> queryUserIdsByTargetId(Long targetId) {
List<MsgNotifyTargetDetail> details = this.lambdaQuery()
.eq(MsgNotifyTargetDetail::getTargetId, targetId)
.list();
List<Long> userIds = new ArrayList<>();
details.forEach(detail -> {
String targetType = detail.getTargetType();
if (targetType.equals(MsgMessageTargetType.USER.getValue())) {
userIds.add(detail.getTargetId());
} else if (targetType.equals(MsgMessageTargetType.ROLE.getValue())) {
SysUserBo userBo = new SysUserBo();
userBo.setRoleId(detail.getTargetId());
TableDataInfo<SysUserVo> dataInfo = userService.selectAllocatedList(userBo, new PageQuery(PageQuery.DEFAULT_PAGE_NUM, PageQuery.DEFAULT_PAGE_SIZE));
List<SysUserVo> rows = dataInfo.getRows();
List<Long> userIdList = rows.stream().map(SysUserVo::getUserId).distinct().filter(Objects::nonNull).toList();
userIds.addAll(userIdList);
} else if (targetType.equals(MsgMessageTargetType.DEPT.getValue())) {
List<SysUserVo> userVos = userService.selectUserListByDept(detail.getTargetId());
List<Long> userIdList = userVos.stream().map(SysUserVo::getUserId).distinct().filter(Objects::nonNull).toList();
userIds.addAll(userIdList);
}
});
return userIds;
}
/**
* 根据通知目标ID查询通知目标详情列表
*
* @param targetId 通知目标ID
* @return 通知目标详情列表
*/
@Override
public List<MsgNotifyTargetDetailVo> queryVoListByTargetId(Long targetId) {
List<MsgNotifyTargetDetail> details = this.lambdaQuery()
.eq(MsgNotifyTargetDetail::getTargetId, targetId)
.list();
return this.getVoList(details);
}
/**
* 获取通知人员配置详情封装
*
* @param detail 通知人员配置详情
* @return 通知人员配置详情封装
*/
@Override
public MsgNotifyTargetDetailVo getVo(MsgNotifyTargetDetail detail) {
MsgNotifyTargetDetailVo vo = new MsgNotifyTargetDetailVo();
if (detail == null) {
return vo;
}
BeanUtils.copyProperties(detail, vo);
Long targetId = detail.getTargetId();
if (detail.getTargetType().equals(MsgMessageTargetType.USER.getValue())) {
vo.setTargetName(userService.selectUserById(targetId).getUserName());
} else if (detail.getTargetType().equals(MsgMessageTargetType.ROLE.getValue())) {
vo.setTargetName(roleService.selectRoleById(targetId).getRoleName());
} else if (detail.getTargetType().equals(MsgMessageTargetType.DEPT.getValue())) {
vo.setTargetName(deptService.selectDeptById(targetId).getDeptName());
}
return vo;
}
/**
* 批量获取通知人员配置详情封装
*
* @param detailList 通知人员配置详情列表
* @return 通知人员配置详情封装列表
*/
@Override
public List<MsgNotifyTargetDetailVo> getVoList(List<MsgNotifyTargetDetail> detailList) {
if (CollUtil.isEmpty(detailList)) {
return List.of();
}
// 获取用户列表
List<Long> userIds = new ArrayList<>();
List<Long> roleIds = new ArrayList<>();
List<Long> deptIds = new ArrayList<>();
detailList.forEach(detail -> {
String targetType = detail.getTargetType();
Long targetId = detail.getTargetId();
if (targetType.equals(MsgMessageTargetType.USER.getValue())) {
userIds.add(targetId);
} else if (targetType.equals(MsgMessageTargetType.ROLE.getValue())) {
roleIds.add(targetId);
} else if (targetType.equals(MsgMessageTargetType.DEPT.getValue())) {
deptIds.add(targetId);
}
});
List<Long> newUserIds = userIds.stream().distinct().filter(Objects::nonNull).toList();
List<Long> newRoleIds = roleIds.stream().distinct().filter(Objects::nonNull).toList();
List<Long> newDeptIds = deptIds.stream().distinct().filter(Objects::nonNull).toList();
List<SysUserVo> userVos = userService.selectUserByIds(newUserIds, null);
List<SysRoleVo> roleVos = roleService.selectRoleByIds(newRoleIds);
List<SysDeptVo> deptVos = deptService.selectDeptByIds(newDeptIds);
Map<Long, String> userMap = userVos.stream().collect(Collectors.toMap(SysUserVo::getUserId, SysUserVo::getNickName));
Map<Long, String> roleMap = roleVos.stream().collect(Collectors.toMap(SysRoleVo::getRoleId, SysRoleVo::getRoleName));
Map<Long, String> deptMap = deptVos.stream().collect(Collectors.toMap(SysDeptVo::getDeptId, SysDeptVo::getDeptName));
return detailList.stream().map(detail -> {
MsgNotifyTargetDetailVo vo = new MsgNotifyTargetDetailVo();
BeanUtils.copyProperties(detail, vo);
if (detail.getTargetType().equals(MsgMessageTargetType.USER.getValue())) {
vo.setTargetName(userMap.get(detail.getTargetId()));
} else if (detail.getTargetType().equals(MsgMessageTargetType.ROLE.getValue())) {
vo.setTargetName(roleMap.get(detail.getTargetId()));
} else if (detail.getTargetType().equals(MsgMessageTargetType.DEPT.getValue())) {
vo.setTargetName(deptMap.get(detail.getTargetId()));
}
return vo;
}).toList();
}
}

View File

@ -0,0 +1,268 @@
package org.dromara.message.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.message.domain.MsgNotifyTarget;
import org.dromara.message.domain.MsgNotifyTargetDetail;
import org.dromara.message.domain.dto.notifytarget.MsgNotifyTargetCreateReq;
import org.dromara.message.domain.dto.notifytarget.MsgNotifyTargetQueryReq;
import org.dromara.message.domain.dto.notifytarget.MsgNotifyTargetUpdateReq;
import org.dromara.message.domain.dto.notifytargetdetail.MsgNotifyTargetDetailCreateDto;
import org.dromara.message.domain.dto.notifytargetdetail.MsgNotifyTargetDetailUpdateDto;
import org.dromara.message.domain.vo.notifytarget.MsgNotifyTargetVo;
import org.dromara.message.domain.vo.notifytargetdetail.MsgNotifyTargetDetailVo;
import org.dromara.message.mapper.MsgNotifyTargetMapper;
import org.dromara.message.service.IMsgNotifyTargetDetailService;
import org.dromara.message.service.IMsgNotifyTargetService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 通知人员配置Service业务层处理
*
* @author lilemy
* @date 2025-08-05
*/
@Service
public class MsgNotifyTargetServiceImpl extends ServiceImpl<MsgNotifyTargetMapper, MsgNotifyTarget>
implements IMsgNotifyTargetService {
@Resource
private IMsgNotifyTargetDetailService notifyTargetDetailService;
/**
* 查询通知人员配置
*
* @param id 主键
* @return 通知人员配置
*/
@Override
public MsgNotifyTargetVo queryById(Long id) {
MsgNotifyTarget notifyTarget = this.getById(id);
if (notifyTarget == null) {
throw new ServiceException("通知人员配置信息不存在", HttpStatus.NOT_FOUND);
}
return this.getVo(notifyTarget);
}
/**
* 分页查询通知人员配置列表
*
* @param req 查询条件
* @param pageQuery 分页参数
* @return 通知人员配置分页列表
*/
@Override
public TableDataInfo<MsgNotifyTargetVo> queryPageList(MsgNotifyTargetQueryReq req, PageQuery pageQuery) {
Page<MsgNotifyTarget> result = this.page(pageQuery.build(), this.buildQueryWrapper(req));
return TableDataInfo.build(this.getVoPage(result));
}
/**
* 查询符合条件的通知人员配置列表
*
* @param req 查询条件
* @return 通知人员配置列表
*/
@Override
public List<MsgNotifyTargetVo> queryList(MsgNotifyTargetQueryReq req) {
return this.list(this.buildQueryWrapper(req)).stream().map(this::getVo).toList();
}
/**
* 新增通知人员配置
*
* @param req 通知人员配置
* @return 是否新增成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(MsgNotifyTargetCreateReq req) {
MsgNotifyTarget target = new MsgNotifyTarget();
BeanUtils.copyProperties(req, target);
boolean save = this.save(target);
if (!save) {
throw new ServiceException("新增通知人员配置信息异常", HttpStatus.ERROR);
}
List<MsgNotifyTargetDetailCreateDto> detailList = req.getDetailList();
List<MsgNotifyTargetDetail> saveDetail = detailList.stream().map(detail -> {
MsgNotifyTargetDetail entity = new MsgNotifyTargetDetail();
entity.setConfigId(target.getId());
entity.setTargetId(detail.getTargetId());
entity.setTargetType(detail.getTargetType());
return entity;
}).toList();
boolean b = notifyTargetDetailService.saveBatch(saveDetail);
if (!b) {
throw new ServiceException("新增通知人员配置信息异常", HttpStatus.ERROR);
}
return true;
}
/**
* 修改通知人员配置
*
* @param req 通知人员配置
* @return 是否修改成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(MsgNotifyTargetUpdateReq req) {
Long id = req.getId();
MsgNotifyTarget notifyTarget = this.getById(id);
if (notifyTarget == null) {
throw new ServiceException("修改通知人员配置信息不存在", HttpStatus.ERROR);
}
MsgNotifyTarget target = new MsgNotifyTarget();
BeanUtils.copyProperties(req, target);
List<MsgNotifyTargetDetailUpdateDto> detailList = req.getDetailList();
// 判断是否需要新增或删除配置详情
if (CollUtil.isNotEmpty(detailList)) {
// 1. 从数据库中获取当前配置下的所有旧详情
List<MsgNotifyTargetDetail> oldDetails = notifyTargetDetailService.lambdaQuery()
.eq(MsgNotifyTargetDetail::getTargetId, id)
.list();
// 2. 计算需要删除的详情ID
// 提取前端提交的详情中所有已存在的记录ID (ID不为null)
Set<Long> incomingIds = detailList.stream()
.map(MsgNotifyTargetDetailUpdateDto::getId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
// 筛选出 oldDetails 中 ID 不在 incomingIds 里的记录,这些就是要删除的
List<Long> idsToDelete = oldDetails.stream()
.map(MsgNotifyTargetDetail::getId)
.filter(detailId -> !incomingIds.contains(detailId))
.toList();
// 3. 执行删除操作
if (CollUtil.isNotEmpty(idsToDelete)) {
notifyTargetDetailService.removeBatchByIds(idsToDelete);
}
// 4. 准备新增和修改的数据,并执行批量保存或更新
if (CollUtil.isNotEmpty(detailList)) {
// 将 DTO 列表转换为实体列表
List<MsgNotifyTargetDetail> entitiesToSaveOrUpdate = detailList.stream().map(dto -> {
MsgNotifyTargetDetail entity = new MsgNotifyTargetDetail();
entity.setId(dto.getId()); // ID为null是新增不为null是更新
entity.setConfigId(id); // 设置关联的父ID
entity.setTargetType(dto.getTargetType());
entity.setTargetId(dto.getTargetId());
return entity;
}).collect(Collectors.toList());
// 5. 执行批量新增或更新
notifyTargetDetailService.saveOrUpdateBatch(entitiesToSaveOrUpdate);
}
}
boolean b = this.updateById(target);
if (!b) {
throw new ServiceException("修改通知人员配置失败", HttpStatus.ERROR);
}
return true;
}
/**
* 批量删除通知人员配置信息
*
* @param ids 待删除的主键集合
* @return 是否删除成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteByIds(Collection<Long> ids) {
List<MsgNotifyTargetDetail> details = notifyTargetDetailService.lambdaQuery()
.in(MsgNotifyTargetDetail::getTargetId, ids)
.list();
boolean result = notifyTargetDetailService.removeBatchByIds(details);
if (!result) {
throw new ServiceException("删除通知人员配置详情信息失败");
}
result = this.removeBatchByIds(ids);
if (!result) {
throw new ServiceException("删除通知人员配置信息失败");
}
return true;
}
/**
* 获取通知人员配置视图对象
*
* @param notifyTarget 通知人员配置对象
* @return 通知人员配置视图对象
*/
@Override
public MsgNotifyTargetVo getVo(MsgNotifyTarget notifyTarget) {
MsgNotifyTargetVo vo = new MsgNotifyTargetVo();
if (notifyTarget == null) {
return vo;
}
BeanUtils.copyProperties(notifyTarget, vo);
Long id = notifyTarget.getId();
List<MsgNotifyTargetDetailVo> detailVos = notifyTargetDetailService.queryVoListByTargetId(id);
vo.setDetailList(detailVos);
return vo;
}
/**
* 获取通知人员配置查询条件封装
*
* @param req 查询条件
* @return 查询条件封装
*/
@Override
public LambdaQueryWrapper<MsgNotifyTarget> buildQueryWrapper(MsgNotifyTargetQueryReq req) {
LambdaQueryWrapper<MsgNotifyTarget> lqw = new LambdaQueryWrapper<>();
if (req == null) {
return lqw;
}
Long projectId = req.getProjectId();
Long typeId = req.getTypeId();
String status = req.getStatus();
lqw.eq(ObjectUtils.isNotEmpty(projectId), MsgNotifyTarget::getProjectId, projectId);
lqw.eq(ObjectUtils.isNotEmpty(typeId), MsgNotifyTarget::getTypeId, typeId);
lqw.eq(StringUtils.isNotBlank(status), MsgNotifyTarget::getStatus, status);
return lqw;
}
/**
* 获取通知人员配置分页对象视图
*
* @param notifyTargetPage 通知人员配置分页对象
* @return 通知人员配置分页详情对象视图
*/
@Override
public Page<MsgNotifyTargetVo> getVoPage(Page<MsgNotifyTarget> notifyTargetPage) {
List<MsgNotifyTarget> notifyTargetList = notifyTargetPage.getRecords();
Page<MsgNotifyTargetVo> voPage = new Page<>(
notifyTargetPage.getCurrent(),
notifyTargetPage.getSize(),
notifyTargetPage.getTotal()
);
List<MsgNotifyTargetVo> targetVoList = notifyTargetList.stream().map(notifyTarget -> {
MsgNotifyTargetVo vo = new MsgNotifyTargetVo();
BeanUtils.copyProperties(notifyTarget, vo);
vo.setDetailList(notifyTargetDetailService.queryVoListByTargetId(notifyTarget.getId()));
return vo;
}).toList();
voPage.setRecords(targetVoList);
return voPage;
}
}

View File

@ -0,0 +1,180 @@
package org.dromara.message.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.message.domain.MsgTypeConfig;
import org.dromara.message.domain.dto.typeconfig.MsgTypeConfigCreateReq;
import org.dromara.message.domain.dto.typeconfig.MsgTypeConfigQueryReq;
import org.dromara.message.domain.dto.typeconfig.MsgTypeConfigUpdateReq;
import org.dromara.message.domain.vo.typeconfig.MsgTypeConfigVo;
import org.dromara.message.mapper.MsgTypeConfigMapper;
import org.dromara.message.service.IMsgTypeConfigService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
/**
* 消息类型配置Service业务层处理
*
* @author lilemy
* @date 2025-08-05
*/
@Service
public class MsgTypeConfigServiceImpl extends ServiceImpl<MsgTypeConfigMapper, MsgTypeConfig>
implements IMsgTypeConfigService {
/**
* 查询消息类型配置
*
* @param id 主键
* @return 消息类型配置
*/
@Override
public MsgTypeConfigVo queryById(Long id) {
MsgTypeConfig typeConfig = this.getById(id);
if (typeConfig == null) {
throw new ServiceException("消息类型配置信息不存在", HttpStatus.NOT_FOUND);
}
return this.getVo(typeConfig);
}
/**
* 查询符合条件的消息类型配置列表
*
* @param req 查询条件
* @return 消息类型配置列表
*/
@Override
public List<MsgTypeConfigVo> queryList(MsgTypeConfigQueryReq req) {
List<MsgTypeConfig> result = this.list(this.buildQueryWrapper(req));
return this.getVoList(result);
}
/**
* 新增消息类型配置
*
* @param req 消息类型配置
* @return 新增成功消息类型配置id
*/
@Override
public Long insertByBo(MsgTypeConfigCreateReq req) {
MsgTypeConfig typeConfig = new MsgTypeConfig();
BeanUtils.copyProperties(req, typeConfig);
// 校验编号是否重复
boolean b = this.lambdaQuery()
.eq(MsgTypeConfig::getTypeCode, typeConfig.getTypeCode())
.in(MsgTypeConfig::getProjectId, typeConfig.getProjectId(), 0L)
.count() > 0;
if (b) {
throw new ServiceException("当前消息编号已存在", HttpStatus.CONFLICT);
}
boolean save = this.save(typeConfig);
if (!save) {
throw new ServiceException("新增消息类型配置信息异常", HttpStatus.ERROR);
}
return typeConfig.getId();
}
/**
* 修改消息类型配置
*
* @param req 消息类型配置
* @return 是否修改成功
*/
@Override
public Boolean updateByBo(MsgTypeConfigUpdateReq req) {
Long id = req.getId();
MsgTypeConfig oldTypeConfig = this.getById(id);
if (oldTypeConfig == null) {
throw new ServiceException("消息类型配置信息不存在", HttpStatus.NOT_FOUND);
}
MsgTypeConfig typeConfig = new MsgTypeConfig();
BeanUtils.copyProperties(req, typeConfig);
// 校验编号是否重复
if (!typeConfig.getTypeCode().equals(oldTypeConfig.getTypeCode())){
boolean b = this.lambdaQuery()
.eq(MsgTypeConfig::getTypeCode, typeConfig.getTypeCode())
.in(MsgTypeConfig::getProjectId, typeConfig.getProjectId(), 0L)
.count() > 0;
if (b) {
throw new ServiceException("当前消息编号已存在", HttpStatus.CONFLICT);
}
}
boolean result = this.updateById(typeConfig);
if (!result) {
throw new ServiceException("修改消息类型配置信息异常", HttpStatus.ERROR);
}
return true;
}
/**
* 批量删除消息类型配置信息
*
* @param ids 待删除的主键集合
* @return 是否删除成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteByIds(Collection<Long> ids) {
return this.removeBatchByIds(ids);
}
/**
* 获取消息类型配置视图对象
*
* @param typeConfig 消息类型配置对象
* @return 消息类型配置视图对象
*/
@Override
public MsgTypeConfigVo getVo(MsgTypeConfig typeConfig) {
MsgTypeConfigVo vo = new MsgTypeConfigVo();
if (typeConfig == null) {
return vo;
}
BeanUtils.copyProperties(typeConfig, vo);
return vo;
}
/**
* 获取消息类型配置查询条件封装
*
* @param req 查询条件
* @return 查询条件封装
*/
@Override
public LambdaQueryWrapper<MsgTypeConfig> buildQueryWrapper(MsgTypeConfigQueryReq req) {
LambdaQueryWrapper<MsgTypeConfig> lqw = new LambdaQueryWrapper<>();
if (req == null) {
return lqw;
}
Long projectId = req.getProjectId();
Long parentId = req.getParentId();
String typeCode = req.getTypeCode();
String typeName = req.getTypeName();
String status = req.getStatus();
lqw.eq(ObjectUtils.isNotEmpty(projectId), MsgTypeConfig::getProjectId, projectId);
lqw.eq(ObjectUtils.isNotEmpty(parentId), MsgTypeConfig::getParentId, parentId);
lqw.eq(StringUtils.isNotBlank(typeCode), MsgTypeConfig::getTypeCode, typeCode);
lqw.like(StringUtils.isNotBlank(typeName), MsgTypeConfig::getTypeName, typeName);
lqw.eq(StringUtils.isNotBlank(status), MsgTypeConfig::getStatus, status);
return lqw;
}
/**
* 获取消息类型配置分页对象视图
*
* @param typeConfigList 消息类型配置分页对象
* @return 消息类型配置分页对象视图
*/
@Override
public List<MsgTypeConfigVo> getVoList(List<MsgTypeConfig> typeConfigList) {
return typeConfigList.stream().map(this::getVo).toList();
}
}

View File

@ -0,0 +1,7 @@
<?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="org.dromara.message.mapper.MsgNotificationMapper">
</mapper>

View File

@ -0,0 +1,7 @@
<?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="org.dromara.message.mapper.MsgNotifyTargetDetailMapper">
</mapper>

View File

@ -0,0 +1,7 @@
<?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="org.dromara.message.mapper.MsgNotifyTargetMapper">
</mapper>

View File

@ -0,0 +1,7 @@
<?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="org.dromara.message.mapper.MsgTypeConfigMapper">
</mapper>

View File

@ -1914,3 +1914,83 @@ values(1951230902137835526, '物资-材料设备删除', 1951230902137835522, '4
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1951230902137835527, '物资-材料设备导出', 1951230902137835522, '5', '#', '', 1, 0, 'F', '0', '0', 'cailiaoshebei:cailiaoshebei:export', '#', 103, 1, sysdate(), null, null, '');
-- 菜单 SQL
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952577325643083777, '消息通知', '1952576561528975362', '1', 'notification', 'message/notification/index', 1, 0, 'C', '0', '0', 'message:notification:list', '#', 103, 1, sysdate(), null, null, '消息通知菜单');
-- 按钮 SQL
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952577325643083778, '消息通知查询', 1952577325643083777, '1', '#', '', 1, 0, 'F', '0', '0', 'message:notification:query', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952577325643083779, '消息通知新增', 1952577325643083777, '2', '#', '', 1, 0, 'F', '0', '0', 'message:notification:add', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952577325643083780, '消息通知修改', 1952577325643083777, '3', '#', '', 1, 0, 'F', '0', '0', 'message:notification:edit', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952577325643083781, '消息通知删除', 1952577325643083777, '4', '#', '', 1, 0, 'F', '0', '0', 'message:notification:remove', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952577325643083782, '消息通知导出', 1952577325643083777, '5', '#', '', 1, 0, 'F', '0', '0', 'message:notification:export', '#', 103, 1, sysdate(), null, null, '');
-- 菜单 SQL
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952577327337582594, '通知人员配置', '1952576561528975362', '1', 'notifyTarget', 'message/notifyTarget/index', 1, 0, 'C', '0', '0', 'message:notifyTarget:list', '#', 103, 1, sysdate(), null, null, '通知人员配置菜单');
-- 按钮 SQL
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952577327337582595, '通知人员配置查询', 1952577327337582594, '1', '#', '', 1, 0, 'F', '0', '0', 'message:notifyTarget:query', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952577327337582596, '通知人员配置新增', 1952577327337582594, '2', '#', '', 1, 0, 'F', '0', '0', 'message:notifyTarget:add', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952577327337582597, '通知人员配置修改', 1952577327337582594, '3', '#', '', 1, 0, 'F', '0', '0', 'message:notifyTarget:edit', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952577327337582598, '通知人员配置删除', 1952577327337582594, '4', '#', '', 1, 0, 'F', '0', '0', 'message:notifyTarget:remove', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952577327337582599, '通知人员配置导出', 1952577327337582594, '5', '#', '', 1, 0, 'F', '0', '0', 'message:notifyTarget:export', '#', 103, 1, sysdate(), null, null, '');
-- 菜单 SQL
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952577327597629441, '消息类型配置', '1952576561528975362', '1', 'typeConfig', 'message/typeConfig/index', 1, 0, 'C', '0', '0', 'message:typeConfig:list', '#', 103, 1, sysdate(), null, null, '消息类型配置菜单');
-- 按钮 SQL
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952577327597629442, '消息类型配置查询', 1952577327597629441, '1', '#', '', 1, 0, 'F', '0', '0', 'message:typeConfig:query', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952577327597629443, '消息类型配置新增', 1952577327597629441, '2', '#', '', 1, 0, 'F', '0', '0', 'message:typeConfig:add', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952577327597629444, '消息类型配置修改', 1952577327597629441, '3', '#', '', 1, 0, 'F', '0', '0', 'message:typeConfig:edit', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952577327597629445, '消息类型配置删除', 1952577327597629441, '4', '#', '', 1, 0, 'F', '0', '0', 'message:typeConfig:remove', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952577327597629446, '消息类型配置导出', 1952577327597629441, '5', '#', '', 1, 0, 'F', '0', '0', 'message:typeConfig:export', '#', 103, 1, sysdate(), null, null, '');
-- 菜单 SQL
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952650149079576578, '通知人员配置详情', '1952577327337582594', '1', 'notifyTargetDetail', 'message/notifyTargetDetail/index', 1, 0, 'C', '0', '0', 'message:notifyTargetDetail:list', '#', 103, 1, sysdate(), null, null, '通知人员配置详情菜单');
-- 按钮 SQL
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952650149079576579, '通知人员配置详情查询', 1952650149079576578, '1', '#', '', 1, 0, 'F', '0', '0', 'message:notifyTargetDetail:query', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952650149079576580, '通知人员配置详情新增', 1952650149079576578, '2', '#', '', 1, 0, 'F', '0', '0', 'message:notifyTargetDetail:add', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952650149079576581, '通知人员配置详情修改', 1952650149079576578, '3', '#', '', 1, 0, 'F', '0', '0', 'message:notifyTargetDetail:edit', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952650149079576582, '通知人员配置详情删除', 1952650149079576578, '4', '#', '', 1, 0, 'F', '0', '0', 'message:notifyTargetDetail:remove', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
values(1952650149079576583, '通知人员配置详情导出', 1952650149079576578, '5', '#', '', 1, 0, 'F', '0', '0', 'message:notifyTargetDetail:export', '#', 103, 1, sysdate(), null, null, '');

View File

@ -1718,3 +1718,76 @@ create table pgs_construction_schedule_plan
primary key (`id`) using btree,
index `idx_project_id` (`project_id` asc) using btree comment '项目ID'
) comment '施工进度计划' collate = utf8mb4_unicode_ci;
drop table if exists msg_type_config;
create table msg_type_config
(
`id` bigint not null auto_increment comment '主键ID',
`project_id` bigint default 0 not null comment '项目ID',
`parent_id` bigint default 0 not null comment '父ID',
`type_code` varchar(64) not null comment '消息类型编码',
`type_name` varchar(128) not null comment '消息类型名称',
`type_desc` varchar(512) null comment '消息类型描述',
`status` char(1) default '0' null comment '是否启用(0正常 1禁用)',
`remark` varchar(255) null comment '备注',
`create_by` bigint null comment '创建者',
`update_by` bigint null comment '更新者',
`create_dept` bigint null comment '创建部门',
`create_time` datetime default CURRENT_TIMESTAMP null comment '创建时间',
`update_time` datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新时间',
primary key (`id`) using btree,
index `idx_project_id` (`project_id` asc) using btree comment '项目ID'
) comment '消息类型配置' collate = utf8mb4_unicode_ci;
drop table if exists msg_notify_target;
create table msg_notify_target
(
`id` bigint not null auto_increment comment '主键ID',
`project_id` bigint default 0 not null comment '项目ID',
`type_id` bigint not null comment '消息类型ID',
`status` char(1) default '0' null comment '是否启用(0正常 1禁用)',
`remark` varchar(255) null comment '备注',
`create_by` bigint null comment '创建者',
`update_by` bigint null comment '更新者',
`create_dept` bigint null comment '创建部门',
`create_time` datetime default CURRENT_TIMESTAMP null comment '创建时间',
`update_time` datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新时间',
primary key (`id`) using btree,
index `idx_project_id` (`project_id` asc) using btree comment '项目ID',
index `idx_type_id` (`type_id` asc) using btree comment '消息类型ID'
) comment '通知人员配置' collate = utf8mb4_unicode_ci;
drop table if exists msg_notification;
create table msg_notification
(
`id` bigint not null auto_increment comment '主键ID',
`project_id` bigint default 0 not null comment '项目ID',
`recipient_id` bigint not null comment '接收通知的用户ID',
`sender_id` bigint default 0 not null comment '发送通知的用户ID系统通知 0',
`type_id` bigint not null comment '通知类型ID',
`title` varchar(255) default '' not null comment '通知标题',
`content` text not null comment '通知的主要内容',
`view_status` char(1) default '0' not null comment '查看状态(0未读 1已读)',
`view_time` datetime null comment '查看时间',
`action_url` varchar(1024) default '' not null comment '点击通知后跳转的目标URL',
`file` varchar(1024) null comment '通知附件',
`remark` varchar(255) null comment '备注',
`create_time` datetime default CURRENT_TIMESTAMP null comment '创建时间',
`update_time` datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新时间',
primary key (`id`) using btree,
index `idx_project_id` (`project_id` asc) using btree comment '项目ID',
index `idx_type_id` (`type_id` asc) using btree comment '消息类型ID',
index `idx_sender_id` (`sender_id` asc) using btree comment '发送通知的用户ID',
index `idx_recipient_view` (`recipient_id` asc, `view_status` asc) comment '接收通知的用户ID,查看状态'
) comment '消息通知' collate = utf8mb4_unicode_ci;
drop table if exists msg_notify_target_detail;
create table msg_notify_target_detail
(
`id` bigint not null auto_increment comment '主键ID',
`config_id` bigint not null comment '消息类型配置ID',
`target_type` char(1) not null comment '接收类型',
`target_id` bigint not null comment '接收ID',
primary key (`id`) using btree,
unique index `un_idx_config_target` (`config_id`, `target_type`, `target_id`)
) comment '通知人员配置详情' collate = utf8mb4_unicode_ci;