站内信模块:整体功能实现

This commit is contained in:
YunaiV
2023-01-28 20:10:19 +08:00
parent 143035d798
commit ae3ee95cdd
57 changed files with 1400 additions and 1051 deletions

View File

@@ -1,54 +0,0 @@
package cn.iocoder.yudao.module.system.controller.admin.notify;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.log.NotifyLogBaseVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.log.NotifyLogPageReqVO;
import cn.iocoder.yudao.module.system.convert.notify.NotifyLogConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO;
import cn.iocoder.yudao.module.system.service.notify.NotifyLogService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.Valid;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
/**
* <p>
*
* </p>
*
* @author LuoWenFeng
*/
@Api(tags = "管理后台 - 站内信发送日志")
@RestController
@RequestMapping("/system/notify-log")
@Validated
public class NotifyLogController {
@Resource
private NotifyLogService notifyLogService;
@Resource
private AdminUserService userService;
@GetMapping("/page")
@ApiOperation("获得发送站内信日志分页")
public CommonResult<PageResult<NotifyLogBaseVO>> getNotifyLogPage(@Valid NotifyLogPageReqVO pageVO) {
PageResult<NotifyMessageDO> pageResult = notifyLogService.getNotifyMessageSendPage(pageVO);
PageResult<NotifyLogBaseVO> result = NotifyLogConvert.INSTANCE.convertPage(pageResult);
result.getList().forEach(v -> {
v.setReceiveUserName(userService.getUser(v.getUserId()).getNickname());
});
return success(result);
}
}

View File

@@ -1,9 +1,9 @@
package cn.iocoder.yudao.module.system.controller.admin.notify;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageRespVO;
import cn.iocoder.yudao.module.system.convert.notify.NotifyMessageConvert;
@@ -18,7 +18,6 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Collections;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -33,6 +32,8 @@ public class NotifyMessageController {
@Resource
private NotifyMessageService notifyMessageService;
// ========== 管理所有的站内信 ==========
@GetMapping("/get")
@ApiOperation("获得站内信")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
@@ -50,38 +51,45 @@ public class NotifyMessageController {
return success(NotifyMessageConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/get-recent-list")
@ApiOperation("获取当前用户最新站内信默认10条")
@ApiImplicitParam(name = "size", value = "10", defaultValue = "10", dataTypeClass = Integer.class)
public CommonResult<List<NotifyMessageRespVO>> getRecentList(@RequestParam(name = "size", defaultValue = "10") Integer size) {
NotifyMessagePageReqVO reqVO = new NotifyMessagePageReqVO();
List<NotifyMessageDO> pageResult = notifyMessageService.getNotifyMessageList(reqVO, size);
if (CollUtil.isNotEmpty(pageResult)) {
return success(NotifyMessageConvert.INSTANCE.convertList(pageResult));
}
return success(Collections.emptyList());
// ========== 查看自己的站内信 ==========
@GetMapping("/my-page")
@ApiOperation("获得我的站内信分页")
public CommonResult<PageResult<NotifyMessageRespVO>> getMyMyNotifyMessagePage(@Valid NotifyMessageMyPageReqVO pageVO) {
PageResult<NotifyMessageDO> pageResult = notifyMessageService.getMyMyNotifyMessagePage(pageVO,
getLoginUserId(), UserTypeEnum.ADMIN.getValue());
return success(NotifyMessageConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/get-unread-count")
@ApiOperation("获得未读站内信数量")
public CommonResult<Long> getUnreadCount() {
return success(notifyMessageService.getUnreadNotifyMessageCount(getLoginUserId(), UserTypeEnum.ADMIN.getValue()));
}
@PutMapping("/update-list-read")
@ApiOperation("批量标记已读")
@PutMapping("/update-read")
@ApiOperation("标记站内信为已读")
@ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class)
public CommonResult<Boolean> batchUpdateNotifyMessageReadStatus(@RequestBody List<Long> ids) {
notifyMessageService.batchUpdateNotifyMessageReadStatus(ids, getLoginUserId());
public CommonResult<Boolean> updateNotifyMessageRead(@RequestParam("ids") List<Long> ids) {
notifyMessageService.updateNotifyMessageRead(ids, getLoginUserId(), UserTypeEnum.ADMIN.getValue());
return success(Boolean.TRUE);
}
@PutMapping("/update-all-read")
@ApiOperation("所有未读消息标记已读")
public CommonResult<Boolean> batchUpdateAllNotifyMessageReadStatus() {
notifyMessageService.batchUpdateAllNotifyMessageReadStatus(getLoginUserId(), UserTypeEnum.ADMIN.getValue());
@ApiOperation("标记所有站内信为已读")
public CommonResult<Boolean> updateAllNotifyMessageRead() {
notifyMessageService.updateAllNotifyMessageRead(getLoginUserId(), UserTypeEnum.ADMIN.getValue());
return success(Boolean.TRUE);
}
@GetMapping("/get-unread-list")
@ApiOperation("获取当前用户的最新站内信列表,默认 10 条")
@ApiImplicitParam(name = "size", value = "10", defaultValue = "10", dataTypeClass = Integer.class)
public CommonResult<List<NotifyMessageRespVO>> getUnreadNotifyMessageList(
@RequestParam(name = "size", defaultValue = "10") Integer size) {
List<NotifyMessageDO> list = notifyMessageService.getUnreadNotifyMessageList(
getLoginUserId(), UserTypeEnum.ADMIN.getValue(), size);
return success(NotifyMessageConvert.INSTANCE.convertList(list));
}
@GetMapping("/get-unread-count")
@ApiOperation("获得当前用户的未读站内信数量")
public CommonResult<Long> getUnreadNotifyMessageCount() {
return success(notifyMessageService.getUnreadNotifyMessageCount(getLoginUserId(), UserTypeEnum.ADMIN.getValue()));
}
}

View File

@@ -2,8 +2,6 @@ package cn.iocoder.yudao.module.system.controller.admin.notify;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.*;
import cn.iocoder.yudao.module.system.convert.notify.NotifyTemplateConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
@@ -17,13 +15,9 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@Api(tags = "管理后台 - 站内信模版")
@RestController
@@ -37,7 +31,6 @@ public class NotifyTemplateController {
@Resource
private NotifySendService notifySendService;
@PostMapping("/create")
@ApiOperation("创建站内信模版")
@PreAuthorize("@ss.hasPermission('system:notify-template:create')")
@@ -79,22 +72,12 @@ public class NotifyTemplateController {
return success(NotifyTemplateConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/export-excel")
@ApiOperation("导出站内信模版 Excel")
@PreAuthorize("@ss.hasPermission('system:notify-template:export')")
@OperateLog(type = EXPORT)
public void exportNotifyTemplateExcel(@Valid NotifyTemplateExportReqVO exportReqVO,
HttpServletResponse response) throws IOException {
List<NotifyTemplateDO> list = notifyTemplateService.getNotifyTemplateList(exportReqVO);
// 导出 Excel
List<NotifyTemplateExcelVO> datas = NotifyTemplateConvert.INSTANCE.convertList02(list);
ExcelUtils.write(response, "站内信模版.xls", "数据", NotifyTemplateExcelVO.class, datas);
}
@PostMapping("/send-notify")
@ApiOperation("发送站内信")
@PreAuthorize("@ss.hasPermission('system:notify-template:send-notify')")
public CommonResult<Long> sendNotify(@Valid @RequestBody NotifyTemplateSendReqVO sendReqVO) {
return success(notifySendService.sendSingleNotifyToAdmin(sendReqVO.getUserId(),
sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams()));
}
}

View File

@@ -1,44 +0,0 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.log;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* 站内信 Base VO提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class NotifyLogBaseVO {
@ApiModelProperty(value = "模版编码")
private String templateCode;
@ApiModelProperty(value = "标题")
private String title;
@ApiModelProperty(value = "内容", required = true)
private String content;
@ApiModelProperty(value = "发送时间", required = true)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date sendTime;
@ApiModelProperty(value = "芋艿", required = true)
private String receiveUserName;
@ApiModelProperty(value = "1", required = true)
private Long userId;
@ApiModelProperty(value = "是否已读 false-未读 true-已读")
private Boolean readStatus;
@ApiModelProperty(value = "阅读时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date readTime;
}

View File

@@ -4,35 +4,58 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* 站内信 Base VO提供给添加、修改、详细的子 VO 使用
* 站内信消息 Base VO提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class NotifyMessageBaseVO {
@ApiModelProperty(value = "标题")
private String title;
@ApiModelProperty(value = "用户编号", required = true, example = "25025")
@NotNull(message = "用户编号不能为空")
private Long userId;
@ApiModelProperty(value = "内容")
private String content;
@ApiModelProperty(value = "用户类型", required = true, example = "1", notes = "参见 UserTypeEnum 枚举")
@NotNull(message = "用户类型不能为空")
private Byte userType;
@ApiModelProperty(value = "发送时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date sendTime;
@ApiModelProperty(value = "模版编号", required = true, example = "13013")
@NotNull(message = "模版编号不能为空")
private Long templateId;
@ApiModelProperty(value = "芋艿")
private String sendUserName;
@ApiModelProperty(value = "模板编码", required = true, example = "test_01")
@NotNull(message = "模板编码不能为空")
private String templateCode;
@ApiModelProperty(value = "是否已读 false-未读 true-已读")
@ApiModelProperty(value = "模版发送人名称", required = true, example = "芋艿")
@NotNull(message = "模版发送人名称不能为空")
private String templateNickname;
@ApiModelProperty(value = "模版内容", required = true, example = "测试内容")
@NotNull(message = "模版内容不能为空")
private String templateContent;
@ApiModelProperty(value = "模版类型", required = true, example = "2")
@NotNull(message = "模版类型不能为空")
private Integer templateType;
@ApiModelProperty(value = "模版参数", required = true)
@NotNull(message = "模版参数不能为空")
private Map<String, Object> templateParams;
@ApiModelProperty(value = "是否已读", required = true, example = "true")
@NotNull(message = "是否已读不能为空")
private Boolean readStatus;
@ApiModelProperty(value = "阅读时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date readTime;
private LocalDateTime readTime;
}

View File

@@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.log;
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.message;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.annotations.ApiModel;
@@ -8,23 +8,21 @@ import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ApiModel("管理后台 - 站内信日志分页 Request VO")
@ApiModel("管理后台 - 站内信分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class NotifyLogPageReqVO extends PageParam {
public class NotifyMessageMyPageReqVO extends PageParam {
@ApiModelProperty(value = "模版编码")
private String templateCode;
@ApiModelProperty(value = "标题")
private String title;
@ApiModelProperty(value = "是否已读", example = "true")
private Boolean readStatus;
@ApiModelProperty(value = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date[] sendTime;
private LocalDateTime[] createTime;
}

View File

@@ -8,6 +8,7 @@ import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import java.util.Date;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@@ -18,14 +19,20 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
@ToString(callSuper = true)
public class NotifyMessagePageReqVO extends PageParam {
@ApiModelProperty(value = "标题")
private String title;
@ApiModelProperty(value = "用户编号", example = "25025")
private Long userId;
@ApiModelProperty(value = "是否已读 0-未读 1-已读")
private Boolean readStatus;
@ApiModelProperty(value = "用户类型", example = "1")
private Integer userType;
@ApiModelProperty(value = "模板编码", example = "test_01")
private String templateCode;
@ApiModelProperty(value = "模版类型", example = "2")
private Integer templateType;
@ApiModelProperty(value = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date[] createTime;
private LocalDateTime[] createTime;
}

View File

@@ -10,7 +10,10 @@ import io.swagger.annotations.*;
@ToString(callSuper = true)
public class NotifyMessageRespVO extends NotifyMessageBaseVO {
@ApiModelProperty(value = "ID", required = true)
@ApiModelProperty(value = "ID", required = true, example = "1024")
private Long id;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
}

View File

@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
@@ -14,24 +15,32 @@ import javax.validation.constraints.NotNull;
@Data
public class NotifyTemplateBaseVO {
@ApiModelProperty(value = "模版编码", required = true)
@ApiModelProperty(value = "模版名称", required = true, example = "测试模版")
@NotEmpty(message = "模版名称不能为空")
private String name;
@ApiModelProperty(value = "模版编码", required = true, example = "SEND_TEST")
@NotNull(message = "模版编码不能为空")
private String code;
@ApiModelProperty(value = "模版标题", required = true)
@NotNull(message = "模版标题不能为空")
private String title;
@ApiModelProperty(value = "模版类型", required = true, example = "1", notes = "对应 system_notify_template_type 字典")
@NotNull(message = "模版类型不能为空")
private Integer type;
@ApiModelProperty(value = "模版内容", required = true)
@NotNull(message = "模版内容不能为空")
@ApiModelProperty(value = "发送人名称", required = true, example = "土豆")
@NotEmpty(message = "发送人名称不能为空")
private String nickname;
@ApiModelProperty(value = "模版内容", required = true, example = "我是模版内容")
@NotEmpty(message = "模版内容不能为空")
private String content;
@ApiModelProperty(value = "状态1-启用 0-禁用", required = true)
@NotNull(message = "状态1-启用 0-禁用不能为空")
@ApiModelProperty(value = "状态", required = true, example = "1", notes = "参见 CommonStatusEnum 枚举")
@NotNull(message = "状态不能为空")
@InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}")
private Integer status;
@ApiModelProperty(value = "备注")
private String remarks;
@ApiModelProperty(value = "备注", example = "我是备注")
private String remark;
}

View File

@@ -8,5 +8,4 @@ import io.swagger.annotations.*;
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class NotifyTemplateCreateReqVO extends NotifyTemplateBaseVO {
}

View File

@@ -1,43 +0,0 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template;
import cn.iocoder.yudao.module.system.enums.DictTypeConstants;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import com.alibaba.excel.annotation.ExcelProperty;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
/**
* 站内信模版 Excel VO
*
* @author 芋道源码
*/
@Data
public class NotifyTemplateExcelVO {
@ExcelProperty("ID")
private Long id;
@ExcelProperty("模版编码")
private String code;
@ExcelProperty("模版标题")
private String title;
@ExcelProperty("模版内容")
private String content;
@ExcelProperty(value = "状态1-启用 0-禁用", converter = DictConvert.class)
@DictFormat(DictTypeConstants.COMMON_STATUS)
private Integer status;
@ExcelProperty("备注")
private String remarks;
@ExcelProperty("创建时间")
private Date createTime;
}

View File

@@ -1,28 +0,0 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ApiModel(value = "管理后台 - 站内信模版 Excel 导出 Request VO", description = "参数和 NotifyTemplatePageReqVO 是一致的")
@Data
public class NotifyTemplateExportReqVO {
@ApiModelProperty(value = "模版编码")
private String code;
@ApiModelProperty(value = "模版标题")
private String title;
@ApiModelProperty(value = "状态1-启用 0-禁用")
private Integer status;
@ApiModelProperty(value = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date[] createTime;
}

View File

@@ -1,6 +1,9 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import lombok.*;
import java.time.LocalDateTime;
import java.util.*;
import io.swagger.annotations.*;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
@@ -14,17 +17,17 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
@ToString(callSuper = true)
public class NotifyTemplatePageReqVO extends PageParam {
@ApiModelProperty(value = "模版编码")
@ApiModelProperty(value = "模版编码", example = "test_01")
private String code;
@ApiModelProperty(value = "模版标题")
private String title;
@ApiModelProperty(value = "模版名称", example = "我是名称")
private String name;
@ApiModelProperty(value = "状态1-启用 0-禁用")
private String status;
@ApiModelProperty(value = "状态", example = "1", notes = "参见 CommonStatusEnum 枚举类")
private Integer status;
@ApiModelProperty(value = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date[] createTime;
private LocalDateTime[] createTime;
}

View File

@@ -10,7 +10,7 @@ import io.swagger.annotations.*;
@ToString(callSuper = true)
public class NotifyTemplateRespVO extends NotifyTemplateBaseVO {
@ApiModelProperty(value = "ID", required = true)
@ApiModelProperty(value = "ID", required = true, example = "1024")
private Long id;
@ApiModelProperty(value = "参数数组", example = "name,code")

View File

@@ -4,6 +4,7 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Map;
@@ -16,7 +17,7 @@ public class NotifyTemplateSendReqVO {
private Long userId;
@ApiModelProperty(value = "模板编码", required = true, example = "01")
@NotNull(message = "模板编码不能为空")
@NotEmpty(message = "模板编码不能为空")
private String templateCode;
@ApiModelProperty(value = "模板参数")

View File

@@ -10,8 +10,8 @@ import javax.validation.constraints.*;
@ToString(callSuper = true)
public class NotifyTemplateUpdateReqVO extends NotifyTemplateBaseVO {
@ApiModelProperty(value = "ID", required = true)
@NotNull(message = "ID不能为空")
@ApiModelProperty(value = "ID", required = true, example = "1024")
@NotNull(message = "ID 不能为空")
private Long id;
}

View File

@@ -1,23 +0,0 @@
package cn.iocoder.yudao.module.system.convert.notify;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.log.NotifyLogBaseVO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* <p>
*
* </p>
*
* @author LuoWenFeng
*/
@Mapper
public interface NotifyLogConvert {
NotifyLogConvert INSTANCE = Mappers.getMapper(NotifyLogConvert.class);
PageResult<NotifyLogBaseVO> convertPage(PageResult<NotifyMessageDO> page);
}

View File

@@ -5,7 +5,6 @@ import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateExcelVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateRespVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateUpdateReqVO;
import org.mapstruct.Mapper;
@@ -32,6 +31,4 @@ public interface NotifyTemplateConvert {
PageResult<NotifyTemplateRespVO> convertPage(PageResult<NotifyTemplateDO> page);
List<NotifyTemplateExcelVO> convertList02(List<NotifyTemplateDO> list);
}

View File

@@ -2,19 +2,24 @@ package cn.iocoder.yudao.module.system.dal.dataobject.notify;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.*;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Map;
/**
* 站内信 DO
*
* @author xrcoder
*/
@TableName("system_notify_message")
@TableName(value = "system_notify_message", autoResultMap = true)
@KeySequence("system_notify_message_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@@ -25,22 +30,10 @@ import java.util.Date;
public class NotifyMessageDO extends BaseDO {
/**
* ID
* 站内信编号,自增
*/
@TableId
private Long id;
/**
* 站内信模版编号
*
* 关联 {@link NotifyTemplateDO#getId()}
*/
private Long templateId;
/**
* 站内信模版编码
*
* 关联 {@link NotifyTemplateDO#getCode()}
*/
private String templateCode;
/**
* 用户编号
*
@@ -53,28 +46,49 @@ public class NotifyMessageDO extends BaseDO {
* 枚举 {@link UserTypeEnum}
*/
private Integer userType;
// ========= 模板相关字段 =========
/**
* 标题
* 模版编号
*
* 关联 {@link NotifyTemplateDO#getId()}
*/
private String title;
private Long templateId;
/**
* 内容
* 模版编码
*
* 关联 {@link NotifyTemplateDO#getCode()}
*/
private String content;
// TODO @luowenfeng是不是创建时间直接作为发送时间
private String templateCode;
/**
* 发送时间
* 模版类型
*
* 冗余 {@link NotifyTemplateDO#getType()}
*/
private Date sendTime;
// TODO @luowenfeng是不是不用发送 id 和名字😑?
private Integer templateType;
/**
* 发送用户id
* 模版发送人名称
*
* 冗余 {@link NotifyTemplateDO#getNickname()}
*/
private Long sendUserId;
private String templateNickname;
/**
* 发送用户名
* 模版内容
*
* 基于 {@link NotifyTemplateDO#getContent()} 格式化后的内容
*/
private String sendUserName;
private String templateContent;
/**
* 模版参数
*
* 基于 {@link NotifyTemplateDO#getParams()} 输入后的参数
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private Map<String, Object> templateParams;
// ========= 读取相关字段 =========
/**
* 是否已读
*/
@@ -82,6 +96,6 @@ public class NotifyMessageDO extends BaseDO {
/**
* 阅读时间
*/
private Date readTime;
private LocalDateTime readTime;
}

View File

@@ -31,14 +31,24 @@ public class NotifyTemplateDO extends BaseDO {
*/
@TableId
private Long id;
/**
* 模版名称
*/
private String name;
/**
* 模版编码
*/
private String code;
/**
* 模版标题
* 模版类型
*
* 对应 system_notify_template_type 字典
*/
private String title;
private Integer type;
/**
* 发送人名称
*/
private String nickname;
/**
* 模版内容
*/

View File

@@ -3,49 +3,61 @@ package cn.iocoder.yudao.module.system.dal.mysql.notify;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.log.NotifyLogPageReqVO;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO;
import org.apache.ibatis.annotations.Mapper;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
/**
* 站内信 Mapper
*
* @author xrcoder
*/
@Mapper
public interface NotifyMessageMapper extends BaseMapperX<NotifyMessageDO> {
default PageResult<NotifyMessageDO> selectPage(NotifyMessagePageReqVO reqVO, Long userId, Integer userType) {
default PageResult<NotifyMessageDO> selectPage(NotifyMessagePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<NotifyMessageDO>()
.likeIfPresent(NotifyMessageDO::getTitle, reqVO.getTitle())
.eqIfPresent(NotifyMessageDO::getReadStatus, reqVO.getReadStatus())
.eqIfPresent(NotifyMessageDO::getUserId, reqVO.getUserId())
.eqIfPresent(NotifyMessageDO::getUserType, reqVO.getUserType())
.likeIfPresent(NotifyMessageDO::getTemplateCode, reqVO.getTemplateCode())
.eqIfPresent(NotifyMessageDO::getTemplateType, reqVO.getTemplateType())
.betweenIfPresent(NotifyMessageDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(NotifyMessageDO::getId));
}
default PageResult<NotifyMessageDO> selectPage(NotifyMessageMyPageReqVO reqVO, Long userId, Integer userType) {
return selectPage(reqVO, new LambdaQueryWrapperX<NotifyMessageDO>()
.eq(NotifyMessageDO::getReadStatus, reqVO.getReadStatus())
.betweenIfPresent(NotifyMessageDO::getCreateTime, reqVO.getCreateTime())
.eq(NotifyMessageDO::getUserId, userId)
.eq(NotifyMessageDO::getUserType, userType)
.orderByDesc(NotifyMessageDO::getId));
}
default PageResult<NotifyMessageDO> selectSendPage(NotifyLogPageReqVO reqVO, Long userId) {
return selectPage(reqVO, new LambdaQueryWrapperX<NotifyMessageDO>()
.likeIfPresent(NotifyMessageDO::getTitle, reqVO.getTitle())
.betweenIfPresent(NotifyMessageDO::getSendTime, reqVO.getSendTime())
.eqIfPresent(NotifyMessageDO::getTemplateCode, reqVO.getTemplateCode())
.eq(NotifyMessageDO::getSendUserId, userId)
.orderByDesc(NotifyMessageDO::getId));
default int updateListRead(Collection<Long> ids, Long userId, Integer userType) {
return update(new NotifyMessageDO().setReadStatus(true).setReadTime(LocalDateTime.now()),
new LambdaQueryWrapperX<NotifyMessageDO>()
.in(NotifyMessageDO::getId, ids)
.eq(NotifyMessageDO::getUserId, userId)
.eq(NotifyMessageDO::getUserType, userType)
.eq(NotifyMessageDO::getReadStatus, false));
}
default List<NotifyMessageDO> selectList(NotifyMessagePageReqVO reqVO, Integer size, Long userId, Integer userType) {
return selectList(new LambdaQueryWrapperX<NotifyMessageDO>()
.likeIfPresent(NotifyMessageDO::getTitle, reqVO.getTitle())
.eqIfPresent(NotifyMessageDO::getReadStatus, reqVO.getReadStatus())
.betweenIfPresent(NotifyMessageDO::getCreateTime, reqVO.getCreateTime())
.eqIfPresent(NotifyMessageDO::getUserId, userId)
.eqIfPresent(NotifyMessageDO::getUserType, userType)
.orderByDesc(NotifyMessageDO::getId)
.last("limit " + size));
default int updateListRead(Long userId, Integer userType) {
return update(new NotifyMessageDO().setReadStatus(true).setReadTime(LocalDateTime.now()),
new LambdaQueryWrapperX<NotifyMessageDO>()
.eq(NotifyMessageDO::getUserId, userId)
.eq(NotifyMessageDO::getUserType, userType)
.eq(NotifyMessageDO::getReadStatus, false));
}
default List<NotifyMessageDO> selectUnreadListByUserIdAndUserType(Long userId, Integer userType, Integer size) {
return selectList(new QueryWrapperX<NotifyMessageDO>() // 由于要使用 limitN 语句,所以只能用 QueryWrapperX
.eq("user_id", userId)
.eq("user_type", userType)
.eq("read_status", false)
.orderByDesc("id").limitN(size));
}
default Long selectUnreadCountByUserIdAndUserType(Long userId, Integer userType) {
@@ -55,11 +67,4 @@ public interface NotifyMessageMapper extends BaseMapperX<NotifyMessageDO> {
.eq(NotifyMessageDO::getUserType, userType));
}
default List<NotifyMessageDO> selectUnreadListByUserIdAndUserType(Long userId, Integer userType) {
return selectList(new LambdaQueryWrapperX<NotifyMessageDO>()
.eq(NotifyMessageDO::getReadStatus, false)
.eq(NotifyMessageDO::getUserId, userId)
.eq(NotifyMessageDO::getUserType, userType));
}
}

View File

@@ -1,45 +1,23 @@
package cn.iocoder.yudao.module.system.dal.mysql.notify;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateExportReqVO;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
/**
* 站内信模版 Mapper
*
* @author xrcoder
*/
@Mapper
public interface NotifyTemplateMapper extends BaseMapperX<NotifyTemplateDO> {
@Select("SELECT COUNT(*) FROM system_notify_template WHERE update_time > #{maxUpdateTime}")
Long selectCountByUpdateTimeGt(Date maxUpdateTime);
default NotifyTemplateDO selectByCode(String code) {
return selectOne(NotifyTemplateDO::getCode, code);
}
default PageResult<NotifyTemplateDO> selectPage(NotifyTemplatePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<NotifyTemplateDO>()
.eqIfPresent(NotifyTemplateDO::getCode, reqVO.getCode())
.eqIfPresent(NotifyTemplateDO::getTitle, reqVO.getTitle())
.eqIfPresent(NotifyTemplateDO::getStatus, reqVO.getStatus())
.betweenIfPresent(NotifyTemplateDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(NotifyTemplateDO::getId));
}
default List<NotifyTemplateDO> selectList(NotifyTemplateExportReqVO reqVO) {
return selectList(new LambdaQueryWrapperX<NotifyTemplateDO>()
.eqIfPresent(NotifyTemplateDO::getCode, reqVO.getCode())
.eqIfPresent(NotifyTemplateDO::getTitle, reqVO.getTitle())
.likeIfPresent(NotifyTemplateDO::getCode, reqVO.getCode())
.likeIfPresent(NotifyTemplateDO::getName, reqVO.getName())
.eqIfPresent(NotifyTemplateDO::getStatus, reqVO.getStatus())
.betweenIfPresent(NotifyTemplateDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(NotifyTemplateDO::getId));

View File

@@ -22,7 +22,7 @@ public interface SmsCodeMapper extends BaseMapperX<SmsCodeDO> {
.eqIfPresent("scene", scene)
.eqIfPresent("code", code)
.orderByDesc("id")
.limit1());
.limitN(1));
}
}

View File

@@ -45,7 +45,7 @@ public interface MailLogService {
* @param isSend 是否发送成功
* @return 日志编号
*/
Long createMailLog(Long userId,Integer userType, String toMail,
Long createMailLog(Long userId, Integer userType, String toMail,
MailAccountDO account, MailTemplateDO template ,
String templateContent, Map<String, Object> templateParams, Boolean isSend);

View File

@@ -88,8 +88,7 @@ public class MailSendServiceImpl implements MailSendService {
// 校验邮箱是否存在
mail = checkMail(mail);
// 构建有序的模板参数。为什么放在这个位置,是提前保证模板参数的正确性,而不是到了插入发送日志
List<KeyValue<String, Object>> newTemplateParams = buildTemplateParams(template, templateParams);
checkTemplateParams(template, templateParams);
// 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus());
@@ -152,21 +151,19 @@ public class MailSendServiceImpl implements MailSendService {
}
/**
* 将参数模板,处理成有序的 KeyValue 数组
* 校验邮件参数是否确实
*
* @param template 邮箱模板
* @param templateParams 原始参数
* @return 处理后的参数
* @param templateParams 参数列表
*/
@VisibleForTesting
public List<KeyValue<String, Object>> buildTemplateParams(MailTemplateDO template, Map<String, Object> templateParams) {
return template.getParams().stream().map(key -> {
public void checkTemplateParams(MailTemplateDO template, Map<String, Object> templateParams) {
template.getParams().forEach(key -> {
Object value = templateParams.get(key);
if (value == null) {
throw exception(MAIL_SEND_TEMPLATE_PARAM_MISS, key);
}
return new KeyValue<>(key, value);
}).collect(Collectors.toList());
});
}
}

View File

@@ -1,24 +0,0 @@
package cn.iocoder.yudao.module.system.service.notify;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.log.NotifyLogPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO;
/**
* 站内信日志 Service 接口
*
* @author LuoWenFeng
*/
public interface NotifyLogService {
// TODO @LuoWenFengNotifyLogService=》NotifyMessageService
/**
* 获得站内信发送分页
*
* @param pageReqVO 分页查询
* @return 站内信分页
*/
PageResult<NotifyMessageDO> getNotifyMessageSendPage(NotifyLogPageReqVO pageReqVO);
}

View File

@@ -1,35 +0,0 @@
package cn.iocoder.yudao.module.system.service.notify;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.log.NotifyLogPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO;
import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyMessageMapper;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
/**
* <p>
* 站内信日志 Service 实现类
*
* </p>
*
* @author LuoWenFeng
*/
@Service
@Validated
public class NotifyLogServiceImpl implements NotifyLogService {
@Resource
private NotifyMessageMapper notifyMessageMapper;
@Override
public PageResult<NotifyMessageDO> getNotifyMessageSendPage(NotifyLogPageReqVO pageReqVO) {
return notifyMessageMapper.selectSendPage(pageReqVO, getLoginUserId());
}
}

View File

@@ -1,11 +1,14 @@
package cn.iocoder.yudao.module.system.service.notify;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* 站内信 Service 接口
@@ -15,28 +18,17 @@ import java.util.List;
public interface NotifyMessageService {
/**
* 获得站内信
* 创建站内信
*
* @param id 编号
* @return 站内信
* @param userId 用户编号
* @param userType 用户类型
* @param template 模版信息
* @param templateContent 模版内容
* @param templateParams 模版参数
* @return 站内信编号
*/
NotifyMessageDO getNotifyMessage(Long id);
/**
* 获得站内信列表
*
* @param ids 编号
* @return 站内信列表
*/
List<NotifyMessageDO> getNotifyMessageList(Collection<Long> ids);
/**
* 获得站内信集合
*
* @param pageReqVO 分页查询
* @return 站内信分页
*/
List<NotifyMessageDO> getNotifyMessageList(NotifyMessagePageReqVO pageReqVO, Integer size);
Long createNotifyMessage(Long userId, Integer userType,
NotifyTemplateDO template, String templateContent, Map<String, Object> templateParams);
/**
* 获得站内信分页
@@ -46,37 +38,60 @@ public interface NotifyMessageService {
*/
PageResult<NotifyMessageDO> getNotifyMessagePage(NotifyMessagePageReqVO pageReqVO);
/**
* 获得【我的】站内信分页
*
* @param pageReqVO 分页查询
* @param userId 用户编号
* @param userType 用户类型
* @return 站内信分页
*/
PageResult<NotifyMessageDO> getMyMyNotifyMessagePage(NotifyMessageMyPageReqVO pageReqVO, Long userId, Integer userType);
/**
* 获得站内信
*
* @param id 编号
* @return 站内信
*/
NotifyMessageDO getNotifyMessage(Long id);
/**
* 获得【我的】未读站内信列表
*
* @param userId 用户编号
* @param userType 用户类型
* @param size 数量
* @return 站内信列表
*/
List<NotifyMessageDO> getUnreadNotifyMessageList(Long userId, Integer userType, Integer size);
/**
* 统计用户未读站内信条数
*
* @param userId 用户ID
* @param userId 用户编号
* @param userType 用户类型
* @return 返回未读站内信条数
*/
Long getUnreadNotifyMessageCount(Long userId, Integer userType);
/**
* 修改站内信阅读状态
*
* @param id 站内信编号
* @param status 状态
*/
void updateNotifyMessageReadStatus(Long id, Boolean status);
/**
* 批量修改站内信阅读状态
* 标记站内信为已读
*
* @param ids 站内信编号集合
* @param userId 用户ID
* @param userId 用户编号
* @param userType 用户类型
* @return 更新到的条数
*/
void batchUpdateNotifyMessageReadStatus(Collection<Long> ids, Long userId);
int updateNotifyMessageRead(Collection<Long> ids, Long userId, Integer userType);
/**
* 批量修改用户所有未读消息标记已读
* 标记所有站内信为已读
*
* @param userId 用户ID
* @param userId 用户编号
* @param userType 用户类型
* @return 更新到的条数
*/
void batchUpdateAllNotifyMessageReadStatus(Long userId, Integer userType);
int updateAllNotifyMessageRead(Long userId, Integer userType);
}

View File

@@ -1,30 +1,20 @@
package cn.iocoder.yudao.module.system.service.notify;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyMessageMapper;
import com.google.common.annotations.VisibleForTesting;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
/**
* 站内信 Service 实现类
@@ -38,43 +28,25 @@ public class NotifyMessageServiceImpl implements NotifyMessageService {
@Resource
private NotifyMessageMapper notifyMessageMapper;
@Resource
private NotifyTemplateService notifyTemplateService;
@VisibleForTesting
public NotifyTemplateDO checkNotifyTemplateValid(String templateCode) {
// 获得站内信模板。考虑到效率,从缓存中获取
NotifyTemplateDO template = notifyTemplateService.getNotifyTemplateByCodeFromCache(templateCode);
// 站内信模板不存在
if (template == null) {
throw exception(NOTIFY_TEMPLATE_NOT_EXISTS);
}
return template;
@Override
public Long createNotifyMessage(Long userId, Integer userType,
NotifyTemplateDO template, String templateContent, Map<String, Object> templateParams) {
NotifyMessageDO message = new NotifyMessageDO().setUserId(userId).setUserType(userType)
.setTemplateId(template.getId()).setTemplateCode(template.getCode())
.setTemplateType(template.getType()).setTemplateNickname(template.getNickname())
.setTemplateContent(templateContent).setTemplateParams(templateParams).setReadStatus(false);
notifyMessageMapper.insert(message);
return message.getId();
}
/**
* 将参数模板,处理成有序的 KeyValue 数组
*
* @param template 站内信模板
* @param templateParams 原始参数
* @return 处理后的参数
*/
@VisibleForTesting
public List<KeyValue<String, Object>> buildTemplateParams(NotifyTemplateDO template, Map<String, Object> templateParams) {
return template.getParams().stream().map(key -> {
Object value = templateParams.get(key);
if (value == null) {
throw exception(NOTIFY_TEMPLATE_PARAM_MISS, key);
}
return new KeyValue<>(key, value);
}).collect(Collectors.toList());
@Override
public PageResult<NotifyMessageDO> getNotifyMessagePage(NotifyMessagePageReqVO pageReqVO) {
return notifyMessageMapper.selectPage(pageReqVO);
}
private void validateNotifyMessageExists(Long id) {
if (notifyMessageMapper.selectById(id) == null) {
throw exception(NOTIFY_MESSAGE_NOT_EXISTS);
}
@Override
public PageResult<NotifyMessageDO> getMyMyNotifyMessagePage(NotifyMessageMyPageReqVO pageReqVO, Long userId, Integer userType) {
return notifyMessageMapper.selectPage(pageReqVO, userId, userType);
}
@Override
@@ -83,97 +55,23 @@ public class NotifyMessageServiceImpl implements NotifyMessageService {
}
@Override
public List<NotifyMessageDO> getNotifyMessageList(Collection<Long> ids) {
return notifyMessageMapper.selectBatchIds(ids);
public List<NotifyMessageDO> getUnreadNotifyMessageList(Long userId, Integer userType, Integer size) {
return notifyMessageMapper.selectUnreadListByUserIdAndUserType(userId, userType, size);
}
@Override
public List<NotifyMessageDO> getNotifyMessageList(NotifyMessagePageReqVO pageReqVO, Integer size) {
return notifyMessageMapper.selectList(pageReqVO, size, getLoginUserId(), UserTypeEnum.ADMIN.getValue());
}
@Override
public PageResult<NotifyMessageDO> getNotifyMessagePage(NotifyMessagePageReqVO pageReqVO) {
return notifyMessageMapper.selectPage(pageReqVO, getLoginUserId(), UserTypeEnum.ADMIN.getValue());
}
/**
* 统计用户未读站内信条数
*
* @param userId 用户ID
* @param userType 用户类型
* @return 返回未读站内信条数
*/
@Override
public Long getUnreadNotifyMessageCount(Long userId, Integer userType) {
return notifyMessageMapper.selectUnreadCountByUserIdAndUserType(userId, userType);
}
/**
* 修改站内信阅读状态
*
* @param id 站内信编号
* @param status 状态
*/
@Override
public void updateNotifyMessageReadStatus(Long id, Boolean status) {
// 校验消息是否存在
this.validateNotifyMessageExists(id);
// 更新状态
batchUpdateReadStatus(CollectionUtils.singleton(id));
public int updateNotifyMessageRead(Collection<Long> ids, Long userId, Integer userType) {
return notifyMessageMapper.updateListRead(ids, userId, userType);
}
/**
* 批量修改站内信阅读状态
*
* @param ids 站内信编号集合
* @param userId 用户ID
*/
@Override
public void batchUpdateNotifyMessageReadStatus(Collection<Long> ids, Long userId) {
List<NotifyMessageDO> list = getNotifyMessageList(ids);
if (CollUtil.isEmpty(list)) {
throw exception(NOTIFY_MESSAGE_NOT_EXISTS);
}
// 验证站内信是否是属于用户
for (NotifyMessageDO messageDO : list) {
checkNotifyMessageIdValid(messageDO, userId);
}
batchUpdateReadStatus(ids);
}
@VisibleForTesting
public void checkNotifyMessageIdValid(NotifyMessageDO notifyMessageDO, Long userId) {
if (!NumberUtil.equals(notifyMessageDO.getUserId(), userId)) {
throw exception(NOTIFY_MESSAGE_ID_PARAM_ERROR);
}
}
/**
* 批量修改用户所有未读消息标记已读
*
* @param userId 用户ID
* @param userType 用户类型
*/
@Override
public void batchUpdateAllNotifyMessageReadStatus(Long userId, Integer userType) {
List<NotifyMessageDO> list = notifyMessageMapper.selectUnreadListByUserIdAndUserType(userId, userType);
if (CollUtil.isNotEmpty(list)) {
batchUpdateReadStatus(CollectionUtils.convertList(list, NotifyMessageDO::getId));
}
}
/**
* 批量修改阅读状态为已读
*
* @param ids 站内变编号数组
*/
private void batchUpdateReadStatus(Collection<Long> ids) {
NotifyMessageDO updateObj = new NotifyMessageDO();
updateObj.setReadStatus(true);
updateObj.setReadTime(new Date());
// TODO @luowenfeng涉及到 mybatis 的操作,都要隐藏到 mapper 中;
notifyMessageMapper.update(updateObj, new LambdaQueryWrapperX<NotifyMessageDO>().in(NotifyMessageDO::getId, ids));
public int updateAllNotifyMessageRead(Long userId, Integer userType) {
return notifyMessageMapper.updateListRead(userId, userType);
}
}

View File

@@ -3,9 +3,13 @@ package cn.iocoder.yudao.module.system.service.notify;
import java.util.List;
import java.util.Map;
/**
* 站内信发送 Service 接口
*
* @author xrcoder
*/
public interface NotifySendService {
/**
* 发送单条站内信给管理后台的用户
*

View File

@@ -1,25 +1,20 @@
package cn.iocoder.yudao.module.system.service.notify;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyMessageMapper;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTICE_NOT_FOUND;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
// TODO @luowenfeng可以直接合并到 NotifyMessageService 中;之前 sms 台复杂,所以没合并。
/**
* 站内信发送 Service 实现类
*
@@ -34,10 +29,7 @@ public class NotifySendServiceImpl implements NotifySendService {
private NotifyTemplateService notifyTemplateService;
@Resource
private NotifyMessageMapper notifyMessageMapper;
@Resource
private AdminUserService userService;
private NotifyMessageService notifyMessageService;
@Override
public Long sendSingleNotifyToAdmin(Long userId, String templateCode, Map<String, Object> templateParams) {
@@ -51,38 +43,44 @@ public class NotifySendServiceImpl implements NotifySendService {
@Override
public Long sendSingleNotify(Long userId, Integer userType, String templateCode, Map<String, Object> templateParams) {
// 校验短信模板是否合法
NotifyTemplateDO template = this.checkNotifyTemplateValid(templateCode);
String content = notifyTemplateService.formatNotifyTemplateContent(template.getContent(), templateParams);
// 获得用户
AdminUserDO sendUser = userService.getUser(getLoginUserId());
// 校验模版
NotifyTemplateDO template = checkNotifyTemplateValid(templateCode);
if (Objects.equals(template.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
log.info("[sendSingleNotify][模版({})已经关闭,无法给用户({}/{})发送]", templateCode, userId, userType);
return null;
}
// 校验参数
checkTemplateParams(template, templateParams);
// todo 模板状态未开启时的业务;如果未开启,就直接 return 好了;
NotifyMessageDO notifyMessageDO = new NotifyMessageDO();
notifyMessageDO.setContent(content);
notifyMessageDO.setTitle(template.getTitle());
notifyMessageDO.setReadStatus(false);
notifyMessageDO.setTemplateId(template.getId());
notifyMessageDO.setTemplateCode(templateCode);
notifyMessageDO.setUserId(userId);
notifyMessageDO.setUserType(userType);
notifyMessageDO.setSendTime(new Date());
notifyMessageDO.setSendUserId(sendUser.getId());
notifyMessageDO.setSendUserName(sendUser.getUsername());
notifyMessageMapper.insert(notifyMessageDO);
return notifyMessageDO.getId();
// 发送站内信
String content = notifyTemplateService.formatNotifyTemplateContent(template.getContent(), templateParams);
return notifyMessageService.createNotifyMessage(userId, userType, template, content, templateParams);
}
// 此注解的含义
@VisibleForTesting
public NotifyTemplateDO checkNotifyTemplateValid(String templateCode) {
// 获得信模板。考虑到效率,从缓存中获取
// 获得站内信模板。考虑到效率,从缓存中获取
NotifyTemplateDO template = notifyTemplateService.getNotifyTemplateByCodeFromCache(templateCode);
// 信模板不存在
// 站内信模板不存在
if (template == null) {
throw exception(NOTICE_NOT_FOUND);
}
return template;
}
/**
* 校验站内信模版参数是否确实
*
* @param template 邮箱模板
* @param templateParams 参数列表
*/
@VisibleForTesting
public void checkTemplateParams(NotifyTemplateDO template, Map<String, Object> templateParams) {
template.getParams().forEach(key -> {
Object value = templateParams.get(key);
if (value == null) {
throw exception(NOTIFY_SEND_TEMPLATE_PARAM_MISS, key);
}
});
}
}

View File

@@ -2,14 +2,11 @@ package cn.iocoder.yudao.module.system.service.notify;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateUpdateReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import javax.validation.Valid;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
@@ -17,7 +14,6 @@ import java.util.Map;
*
* @author xrcoder
*/
// TODO 芋艿:缺少单测,可以参考 SmsTemplateServiceTest 写下
public interface NotifyTemplateService {
/**
@@ -33,16 +29,6 @@ public interface NotifyTemplateService {
*/
NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code);
/**
* 格式化站内信内容
*
* @param content 站内信模板的内容
* @param params 站内信内容的参数
* @return 格式化后的内容
*/
String formatNotifyTemplateContent(String content, Map<String, Object> params);
/**
* 创建站内信模版
*
@@ -73,14 +59,6 @@ public interface NotifyTemplateService {
*/
NotifyTemplateDO getNotifyTemplate(Long id);
/**
* 获得站内信模版列表
*
* @param ids 编号
* @return 站内信模版列表
*/
List<NotifyTemplateDO> getNotifyTemplateList(Collection<Long> ids);
/**
* 获得站内信模版分页
*
@@ -90,11 +68,12 @@ public interface NotifyTemplateService {
PageResult<NotifyTemplateDO> getNotifyTemplatePage(NotifyTemplatePageReqVO pageReqVO);
/**
* 获得站内信模版列表, 用于 Excel 导出
* 格式化站内信内容
*
* @param exportReqVO 查询条件
* @return 站内信模版列表
* @param content 站内信模板的内容
* @param params 站内信内容的参数
* @return 格式化后的内容
*/
List<NotifyTemplateDO> getNotifyTemplateList(NotifyTemplateExportReqVO exportReqVO);
String formatNotifyTemplateContent(String content, Map<String, Object> params);
}

View File

@@ -1,12 +1,10 @@
package cn.iocoder.yudao.module.system.service.notify;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateUpdateReqVO;
import cn.iocoder.yudao.module.system.convert.notify.NotifyTemplateConvert;
@@ -15,20 +13,17 @@ import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyTemplateMapper;
import cn.iocoder.yudao.module.system.mq.producer.notify.NotifyProducer;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_CODE_DUPLICATE;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_NOT_EXISTS;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
/**
* 站内信模版 Service 实现类
@@ -40,12 +35,6 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEM
@Slf4j
public class NotifyTemplateServiceImpl implements NotifyTemplateService {
/**
* 定时执行 {@link #schedulePeriodicRefresh()} 的周期
* 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
*/
private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
/**
* 正则表达式,匹配 {} 中的变量
*/
@@ -65,82 +54,25 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService {
*/
private volatile Map<String, NotifyTemplateDO> notifyTemplateCache;
/**
* 缓存站内信模板的最大更新时间,用于后续的增量轮询,判断是否有更新
*/
private volatile Date maxUpdateTime;
/**
* 初始化站内信模板的本地缓存
*/
@Override
@PostConstruct
public void initLocalCache() {
// 获取站内信模板列表,如果有更新
List<NotifyTemplateDO> notifyTemplateList = this.loadNotifyTemplateIfUpdate(maxUpdateTime);
if (CollUtil.isEmpty(notifyTemplateList)) {
return;
}
// 第一步:查询数据
List<NotifyTemplateDO> templates = notifyTemplateMapper.selectList();
log.info("[initLocalCache][缓存站内信模版,数量为:{}]", templates.size());
// 写入缓存
notifyTemplateCache = CollectionUtils.convertMap(notifyTemplateList, NotifyTemplateDO::getCode);
maxUpdateTime = CollectionUtils.getMaxValue(notifyTemplateList, NotifyTemplateDO::getUpdateTime);
log.info("[initLocalCache][初始化 NotifyTemplate 数量为 {}]", notifyTemplateList.size());
// 第二步:构建缓存
notifyTemplateCache = CollectionUtils.convertMap(templates, NotifyTemplateDO::getCode);
}
/**
* 如果站内信模板发生变化,从数据库中获取最新的全量站内信模板。
* 如果未发生变化,则返回空
*
* @param maxUpdateTime 当前站内信模板的最大更新时间
* @return 站内信模板列表
*/
private List<NotifyTemplateDO> loadNotifyTemplateIfUpdate(Date maxUpdateTime) {
// 第一步,判断是否要更新。
if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
log.info("[loadNotifyTemplateIfUpdate][首次加载全量站内信模板]");
} else { // 判断数据库中是否有更新的站内信模板
if (notifyTemplateMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
return null;
}
log.info("[loadNotifyTemplateIfUpdate][增量加载全量站内信模板]");
}
// 第二步,如果有更新,则从数据库加载所有站内信模板
return notifyTemplateMapper.selectList();
}
@Scheduled(fixedDelay = SCHEDULER_PERIOD)
public void schedulePeriodicRefresh() {
initLocalCache();
}
/**
* 获得站内信模板,从缓存中
*
* @param code 模板编码
* @return 站内信模板
*/
@Override
public NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code) {
return notifyTemplateCache.get(code);
}
/**
* 格式化站内信内容
*
* @param content 站内信模板的内容
* @param params 站内信内容的参数
* @return 格式化后的内容
*/
@Override
public String formatNotifyTemplateContent(String content, Map<String, Object> params) {
return StrUtil.format(content, params);
}
@VisibleForTesting
public List<String> parseTemplateContentParams(String content) {
return ReUtil.findAllGroup1(PATTERN_PARAMS, content);
}
@Override
public Long createNotifyTemplate(NotifyTemplateCreateReqVO createReqVO) {
// 校验站内信编码是否重复
@@ -150,32 +82,37 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService {
NotifyTemplateDO notifyTemplate = NotifyTemplateConvert.INSTANCE.convert(createReqVO);
notifyTemplate.setParams(parseTemplateContentParams(notifyTemplate.getContent()));
notifyTemplateMapper.insert(notifyTemplate);
// 发送刷新消息
notifyProducer.sendNotifyTemplateRefreshMessage();
// 返回
return notifyTemplate.getId();
}
@Override
public void updateNotifyTemplate(NotifyTemplateUpdateReqVO updateReqVO) {
// 校验存在
this.validateNotifyTemplateExists(updateReqVO.getId());
validateNotifyTemplateExists(updateReqVO.getId());
// 校验站内信编码是否重复
checkNotifyTemplateCodeDuplicate(updateReqVO.getId(), updateReqVO.getCode());
// 更新
NotifyTemplateDO updateObj = NotifyTemplateConvert.INSTANCE.convert(updateReqVO);
updateObj.setParams(parseTemplateContentParams(updateObj.getContent()));
notifyTemplateMapper.updateById(updateObj);
// 发送刷新消息
notifyProducer.sendNotifyTemplateRefreshMessage();
}
@VisibleForTesting
public List<String> parseTemplateContentParams(String content) {
return ReUtil.findAllGroup1(PATTERN_PARAMS, content);
}
@Override
public void deleteNotifyTemplate(Long id) {
// 校验存在
this.validateNotifyTemplateExists(id);
validateNotifyTemplateExists(id);
// 删除
notifyTemplateMapper.deleteById(id);
// 发送刷新消息
@@ -193,21 +130,11 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService {
return notifyTemplateMapper.selectById(id);
}
@Override
public List<NotifyTemplateDO> getNotifyTemplateList(Collection<Long> ids) {
return notifyTemplateMapper.selectBatchIds(ids);
}
@Override
public PageResult<NotifyTemplateDO> getNotifyTemplatePage(NotifyTemplatePageReqVO pageReqVO) {
return notifyTemplateMapper.selectPage(pageReqVO);
}
@Override
public List<NotifyTemplateDO> getNotifyTemplateList(NotifyTemplateExportReqVO exportReqVO) {
return notifyTemplateMapper.selectList(exportReqVO);
}
@VisibleForTesting
public void checkNotifyTemplateCodeDuplicate(Long id, String code) {
NotifyTemplateDO template = notifyTemplateMapper.selectByCode(code);
@@ -222,4 +149,16 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService {
throw exception(NOTIFY_TEMPLATE_CODE_DUPLICATE, code);
}
}
/**
* 格式化站内信内容
*
* @param content 站内信模板的内容
* @param params 站内信内容的参数
* @return 格式化后的内容
*/
@Override
public String formatNotifyTemplateContent(String content, Map<String, Object> params) {
return StrUtil.format(content, params);
}
}