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

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,7 +1,9 @@
package cn.iocoder.yudao.framework.mybatis.core.query;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.mybatis.core.enums.SqlConstants;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;

View File

@ -146,19 +146,19 @@ public class QueryWrapperX<T> extends QueryWrapper<T> {
*
* @return this
*/
public QueryWrapperX<T> limit1() {
public QueryWrapperX<T> limitN(int n) {
Assert.notNull(SqlConstants.DB_TYPE, "获取不到数据库的类型");
switch (SqlConstants.DB_TYPE) {
case ORACLE:
case ORACLE_12C:
super.eq("ROWNUM", 1);
super.eq("ROWNUM", n);
break;
case SQL_SERVER:
case SQL_SERVER2005:
super.select("TOP 1 *"); // 由于 SQL Server 是通过 SELECT TOP 1 实现限制一条所以只好使用 * 查询剩余字段
super.select("TOP " + n + " *"); // 由于 SQL Server 是通过 SELECT TOP 1 实现限制一条所以只好使用 * 查询剩余字段
break;
default:
super.last("LIMIT 1");
super.last("LIMIT " + n);
}
return this;
}

View File

@ -153,13 +153,13 @@ public interface ErrorCodeConstants {
ErrorCode MAIL_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1002025000, "模板参数({})缺失");
ErrorCode MAIL_SEND_MAIL_NOT_EXISTS = new ErrorCode(1002025000, "邮箱不存在");
// ========== 站内信模版 1002023000 ==========
ErrorCode NOTIFY_TEMPLATE_NOT_EXISTS = new ErrorCode(1002023000, "站内信模版不存在");
ErrorCode NOTIFY_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1002023001, "已经存在编码为【{}】的站内信模板");
ErrorCode NOTIFY_TEMPLATE_PARAM_MISS = new ErrorCode(1002023002, "模板参数({})缺失");
// ========== 站内信模版 1002026000 ==========
ErrorCode NOTIFY_TEMPLATE_NOT_EXISTS = new ErrorCode(1002026000, "站内信模版不存在");
ErrorCode NOTIFY_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1002026001, "已经存在编码为【{}】的站内信模板");
// ========== 站内信 1002024000 ==========
ErrorCode NOTIFY_MESSAGE_NOT_EXISTS = new ErrorCode(1002024000, "站内信不存在");
ErrorCode NOTIFY_MESSAGE_ID_PARAM_ERROR = new ErrorCode(1002024001, "站内信ID错误");
// ========== 站内信模版 1002027000 ==========
// ========== 站内信发送 1002028000 ==========
ErrorCode NOTIFY_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1002025000, "模板参数({})缺失");
}

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") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 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);
}
}

View File

@ -145,7 +145,7 @@ class MailSendServiceImplTest extends BaseMockitoUnitTest {
}
@Test
public void testBuildTemplateParams_paramMiss() {
public void testCheckTemplateParams_paramMiss() {
// 准备参数
MailTemplateDO template = randomPojo(MailTemplateDO.class,
o -> o.setParams(Lists.newArrayList("code")));
@ -153,7 +153,7 @@ class MailSendServiceImplTest extends BaseMockitoUnitTest {
// mock 方法
// 调用并断言异常
assertServiceException(() -> mailSendService.buildTemplateParams(template, templateParams),
assertServiceException(() -> mailSendService.checkTemplateParams(template, templateParams),
MAIL_SEND_TEMPLATE_PARAM_MISS, "code");
}

View File

@ -0,0 +1,266 @@
package cn.iocoder.yudao.module.system.service.notify;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.enums.SqlConstants;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
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.baomidou.mybatisplus.annotation.DbType;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link NotifyMessageServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import(NotifyMessageServiceImpl.class)
public class NotifyMessageServiceImplTest extends BaseDbUnitTest {
@Resource
private NotifyMessageServiceImpl notifyMessageService;
@Resource
private NotifyMessageMapper notifyMessageMapper;
@Test
public void testCreateNotifyMessage_success() {
// 准备参数
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class);
String templateContent = randomString();
Map<String, Object> templateParams = randomTemplateParams();
// mock 方法
// 调用
Long messageId = notifyMessageService.createNotifyMessage(userId, userType,
template, templateContent, templateParams);
// 断言
NotifyMessageDO message = notifyMessageMapper.selectById(messageId);
assertNotNull(message);
assertEquals(userId, message.getUserId());
assertEquals(userType, message.getUserType());
assertEquals(template.getId(), message.getTemplateId());
assertEquals(template.getCode(), message.getTemplateCode());
assertEquals(template.getType(), message.getTemplateType());
assertEquals(template.getNickname(), message.getTemplateNickname());
assertEquals(templateContent, message.getTemplateContent());
assertEquals(templateParams, message.getTemplateParams());
assertEquals(false, message.getReadStatus());
assertNull(message.getReadTime());
}
@Test
public void testGetNotifyMessagePage() {
// mock 数据
NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到
o.setUserId(1L);
o.setUserType(UserTypeEnum.ADMIN.getValue());
o.setTemplateCode("test_01");
o.setTemplateType(10);
o.setCreateTime(buildTime(2022, 1, 2));
o.setTemplateParams(randomTemplateParams());
});
notifyMessageMapper.insert(dbNotifyMessage);
// 测试 userId 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L)));
// 测试 userType 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
// 测试 templateCode 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setTemplateCode("test_11")));
// 测试 templateType 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setTemplateType(20)));
// 测试 createTime 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setCreateTime(buildTime(2022, 2, 1))));
// 准备参数
NotifyMessagePageReqVO reqVO = new NotifyMessagePageReqVO();
reqVO.setUserId(1L);
reqVO.setUserType(UserTypeEnum.ADMIN.getValue());
reqVO.setTemplateCode("est_01");
reqVO.setTemplateType(10);
reqVO.setCreateTime(buildBetweenTime(2022, 1, 1, 2022, 1, 10));
// 调用
PageResult<NotifyMessageDO> pageResult = notifyMessageService.getNotifyMessagePage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbNotifyMessage, pageResult.getList().get(0));
}
@Test
public void testGetMyNotifyMessagePage() {
// mock 数据
NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到
o.setUserId(1L);
o.setUserType(UserTypeEnum.ADMIN.getValue());
o.setReadStatus(true);
o.setCreateTime(buildTime(2022, 1, 2));
o.setTemplateParams(randomTemplateParams());
});
notifyMessageMapper.insert(dbNotifyMessage);
// 测试 userId 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L)));
// 测试 userType 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
// 测试 readStatus 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(false)));
// 测试 createTime 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setCreateTime(buildTime(2022, 2, 1))));
// 准备参数
Long userId = 1L;
Integer userType = UserTypeEnum.ADMIN.getValue();
NotifyMessageMyPageReqVO reqVO = new NotifyMessageMyPageReqVO();
reqVO.setReadStatus(true);
reqVO.setCreateTime(buildBetweenTime(2022, 1, 1, 2022, 1, 10));
// 调用
PageResult<NotifyMessageDO> pageResult = notifyMessageService.getMyMyNotifyMessagePage(reqVO, userId, userType);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbNotifyMessage, pageResult.getList().get(0));
}
@Test
public void testGetUnreadNotifyMessageList() {
SqlConstants.init(DbType.MYSQL);
// mock 数据
NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到
o.setUserId(1L);
o.setUserType(UserTypeEnum.ADMIN.getValue());
o.setReadStatus(false);
o.setTemplateParams(randomTemplateParams());
});
notifyMessageMapper.insert(dbNotifyMessage);
// 测试 userId 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L)));
// 测试 userType 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
// 测试 readStatus 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true)));
// 准备参数
Long userId = 1L;
Integer userType = UserTypeEnum.ADMIN.getValue();
Integer size = 10;
// 调用
List<NotifyMessageDO> list = notifyMessageService.getUnreadNotifyMessageList(userId, userType, size);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbNotifyMessage, list.get(0));
}
@Test
public void testGetUnreadNotifyMessageCount() {
SqlConstants.init(DbType.MYSQL);
// mock 数据
NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到
o.setUserId(1L);
o.setUserType(UserTypeEnum.ADMIN.getValue());
o.setReadStatus(false);
o.setTemplateParams(randomTemplateParams());
});
notifyMessageMapper.insert(dbNotifyMessage);
// 测试 userId 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L)));
// 测试 userType 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
// 测试 readStatus 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true)));
// 准备参数
Long userId = 1L;
Integer userType = UserTypeEnum.ADMIN.getValue();
// 调用并断言
assertEquals(1, notifyMessageService.getUnreadNotifyMessageCount(userId, userType));
}
@Test
public void testUpdateNotifyMessageRead() {
// mock 数据
NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到
o.setUserId(1L);
o.setUserType(UserTypeEnum.ADMIN.getValue());
o.setReadStatus(false);
o.setReadTime(null);
o.setTemplateParams(randomTemplateParams());
});
notifyMessageMapper.insert(dbNotifyMessage);
// 测试 userId 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L)));
// 测试 userType 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
// 测试 readStatus 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true)));
// 准备参数
Collection<Long> ids = Arrays.asList(dbNotifyMessage.getId(), dbNotifyMessage.getId() + 1,
dbNotifyMessage.getId() + 2, dbNotifyMessage.getId() + 3);
Long userId = 1L;
Integer userType = UserTypeEnum.ADMIN.getValue();
// 调用
int updateCount = notifyMessageService.updateNotifyMessageRead(ids, userId, userType);
// 断言
assertEquals(1, updateCount);
NotifyMessageDO notifyMessage = notifyMessageMapper.selectById(dbNotifyMessage.getId());
assertTrue(notifyMessage.getReadStatus());
assertNotNull(notifyMessage.getReadTime());
}
@Test
public void testUpdateAllNotifyMessageRead() {
// mock 数据
NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到
o.setUserId(1L);
o.setUserType(UserTypeEnum.ADMIN.getValue());
o.setReadStatus(false);
o.setReadTime(null);
o.setTemplateParams(randomTemplateParams());
});
notifyMessageMapper.insert(dbNotifyMessage);
// 测试 userId 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L)));
// 测试 userType 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
// 测试 readStatus 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true)));
// 准备参数
Long userId = 1L;
Integer userType = UserTypeEnum.ADMIN.getValue();
// 调用
int updateCount = notifyMessageService.updateAllNotifyMessageRead(userId, userType);
// 断言
assertEquals(1, updateCount);
NotifyMessageDO notifyMessage = notifyMessageMapper.selectById(dbNotifyMessage.getId());
assertTrue(notifyMessage.getReadStatus());
assertNotNull(notifyMessage.getReadTime());
}
private static Map<String, Object> randomTemplateParams() {
return MapUtil.<String, Object>builder().put(randomString(), randomString())
.put(randomString(), randomString()).build();
}
}

View File

@ -0,0 +1,121 @@
package cn.iocoder.yudao.module.system.service.notify;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.HashMap;
import java.util.Map;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
class NotifySendServiceImplTest extends BaseMockitoUnitTest {
@InjectMocks
private NotifySendServiceImpl notifySendService;
@Mock
private NotifyTemplateService notifyTemplateService;
@Mock
private NotifyMessageService notifyMessageService;
/**
* 发送成功当短信模板开启时
*/
@Test
public void testSendSingleNotify_successWhenMailTemplateEnable() {
// 准备参数
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String templateCode = randomString();
Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
.put("op", "login").build();
// mock NotifyTemplateService 的方法
NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, o -> {
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setContent("验证码为{code}, 操作为{op}");
o.setParams(Lists.newArrayList("code", "op"));
});
when(notifyTemplateService.getNotifyTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
String content = randomString();
when(notifyTemplateService.formatNotifyTemplateContent(eq(template.getContent()), eq(templateParams)))
.thenReturn(content);
// mock NotifyMessageService 的方法
Long messageId = randomLongId();
when(notifyMessageService.createNotifyMessage(eq(userId), eq(userType),
eq(template), eq(content), eq(templateParams))).thenReturn(messageId);
// 调用
Long resultMessageId = notifySendService.sendSingleNotify(userId, userType, templateCode, templateParams);
// 断言
assertEquals(messageId, resultMessageId);
}
/**
* 发送成功当短信模板关闭时
*/
@Test
public void testSendSingleMail_successWhenSmsTemplateDisable() {
// 准备参数
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String templateCode = randomString();
Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
.put("op", "login").build();
// mock NotifyTemplateService 的方法
NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, o -> {
o.setStatus(CommonStatusEnum.DISABLE.getStatus());
o.setContent("验证码为{code}, 操作为{op}");
o.setParams(Lists.newArrayList("code", "op"));
});
when(notifyTemplateService.getNotifyTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
// 调用
Long resultMessageId = notifySendService.sendSingleNotify(userId, userType, templateCode, templateParams);
// 断言
assertNull(resultMessageId);
verify(notifyTemplateService, never()).formatNotifyTemplateContent(anyString(), anyMap());
verify(notifyMessageService, never()).createNotifyMessage(anyLong(), anyInt(), any(), anyString(), anyMap());
}
@Test
public void testCheckMailTemplateValid_notExists() {
// 准备参数
String templateCode = randomString();
// mock 方法
// 调用并断言异常
assertServiceException(() -> notifySendService.checkNotifyTemplateValid(templateCode),
NOTICE_NOT_FOUND);
}
@Test
public void testCheckTemplateParams_paramMiss() {
// 准备参数
NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class,
o -> o.setParams(Lists.newArrayList("code")));
Map<String, Object> templateParams = new HashMap<>();
// mock 方法
// 调用并断言异常
assertServiceException(() -> notifySendService.checkTemplateParams(template, templateParams),
NOTIFY_SEND_TEMPLATE_PARAM_MISS, "code");
}
}

View File

@ -0,0 +1,146 @@
package cn.iocoder.yudao.module.system.service.notify;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateCreateReqVO;
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 cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyTemplateMapper;
import cn.iocoder.yudao.module.system.mq.producer.notify.NotifyProducer;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_NOT_EXISTS;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.verify;
/**
* {@link NotifyTemplateServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import(NotifyTemplateServiceImpl.class)
public class NotifyTemplateServiceImplTest extends BaseDbUnitTest {
@Resource
private NotifyTemplateServiceImpl notifyTemplateService;
@Resource
private NotifyTemplateMapper notifyTemplateMapper;
@MockBean
private NotifyProducer notifyProducer;
@Test
public void testCreateNotifyTemplate_success() {
// 准备参数
NotifyTemplateCreateReqVO reqVO = randomPojo(NotifyTemplateCreateReqVO.class,
o -> o.setStatus(randomCommonStatus()));
// 调用
Long notifyTemplateId = notifyTemplateService.createNotifyTemplate(reqVO);
// 断言
assertNotNull(notifyTemplateId);
// 校验记录的属性是否正确
NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(notifyTemplateId);
assertPojoEquals(reqVO, notifyTemplate);
verify(notifyProducer).sendNotifyTemplateRefreshMessage();
}
@Test
public void testUpdateNotifyTemplate_success() {
// mock 数据
NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class);
notifyTemplateMapper.insert(dbNotifyTemplate);// @Sql: 先插入出一条存在的数据
// 准备参数
NotifyTemplateUpdateReqVO reqVO = randomPojo(NotifyTemplateUpdateReqVO.class, o -> {
o.setId(dbNotifyTemplate.getId()); // 设置更新的 ID
o.setStatus(randomCommonStatus());
});
// 调用
notifyTemplateService.updateNotifyTemplate(reqVO);
// 校验是否更新正确
NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, notifyTemplate);
verify(notifyProducer).sendNotifyTemplateRefreshMessage();
}
@Test
public void testUpdateNotifyTemplate_notExists() {
// 准备参数
NotifyTemplateUpdateReqVO reqVO = randomPojo(NotifyTemplateUpdateReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> notifyTemplateService.updateNotifyTemplate(reqVO), NOTIFY_TEMPLATE_NOT_EXISTS);
}
@Test
public void testDeleteNotifyTemplate_success() {
// mock 数据
NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class);
notifyTemplateMapper.insert(dbNotifyTemplate);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbNotifyTemplate.getId();
// 调用
notifyTemplateService.deleteNotifyTemplate(id);
// 校验数据不存在了
assertNull(notifyTemplateMapper.selectById(id));
verify(notifyProducer).sendNotifyTemplateRefreshMessage();
}
@Test
public void testDeleteNotifyTemplate_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> notifyTemplateService.deleteNotifyTemplate(id), NOTIFY_TEMPLATE_NOT_EXISTS);
}
@Test
public void testGetNotifyTemplatePage() {
// mock 数据
NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class, o -> { // 等会查询到
o.setName("芋头");
o.setCode("test_01");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(buildTime(2022, 2, 3));
});
notifyTemplateMapper.insert(dbNotifyTemplate);
// 测试 name 不匹配
notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setName("")));
// 测试 code 不匹配
notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCode("test_02")));
// 测试 status 不匹配
notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 createTime 不匹配
notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCreateTime(buildTime(2022, 1, 5))));
// 准备参数
NotifyTemplatePageReqVO reqVO = new NotifyTemplatePageReqVO();
reqVO.setName("");
reqVO.setCode("est_01");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setCreateTime(buildBetweenTime(2022, 2, 1, 2022, 2, 5));
// 调用
PageResult<NotifyTemplateDO> pageResult = notifyTemplateService.getNotifyTemplatePage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbNotifyTemplate, pageResult.getList().get(0));
}
}

View File

@ -28,3 +28,5 @@ DELETE FROM "system_oauth2_code";
DELETE FROM "system_mail_account";
DELETE FROM "system_mail_template";
DELETE FROM "system_mail_log";
DELETE FROM "system_notify_template";
DELETE FROM "system_notify_message";

View File

@ -626,3 +626,43 @@ CREATE TABLE IF NOT EXISTS "system_mail_log" (
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '邮件日志表';
-- 将该建表 SQL 语句添加到 yudao-module-system-biz 模块的 test/resources/sql/create_tables.sql 文件里
CREATE TABLE IF NOT EXISTS "system_notify_template" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar NOT NULL,
"code" varchar NOT NULL,
"nickname" varchar NOT NULL,
"content" varchar NOT NULL,
"type" varchar NOT NULL,
"params" varchar,
"status" varchar NOT NULL,
"remark" varchar,
"creator" varchar DEFAULT '',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar DEFAULT '',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '站内信模板表';
CREATE TABLE IF NOT EXISTS "system_notify_message" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"user_id" bigint NOT NULL,
"user_type" varchar NOT NULL,
"template_id" bigint NOT NULL,
"template_code" varchar NOT NULL,
"template_nickname" varchar NOT NULL,
"template_content" varchar NOT NULL,
"template_type" int NOT NULL,
"template_params" varchar NOT NULL,
"read_status" bit NOT NULL,
"read_time" varchar,
"creator" varchar DEFAULT '',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar DEFAULT '',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint not null default '0',
PRIMARY KEY ("id")
) COMMENT '站内信消息表';

View File

@ -115,6 +115,7 @@ yudao:
- cn.iocoder.yudao.module.member.enums.ErrorCodeConstants
- cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants
- cn.iocoder.yudao.module.system.enums.ErrorCodeConstants
- cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants
tenant: # 多租户相关配置项
enable: true
ignore-urls:
@ -141,6 +142,7 @@ yudao:
- system_mail_account
- system_mail_template
- system_mail_log
- system_notify_template
- infra_codegen_column
- infra_codegen_table
- infra_test_demo

View File

@ -0,0 +1,51 @@
import request from '@/utils/request'
import qs from 'qs'
// 获得我的站内信分页
export function getNotifyMessagePage(query) {
return request({
url: '/system/notify-message/page',
method: 'get',
params: query
})
}
// 获得我的站内信分页
export function getMyNotifyMessagePage(query) {
return request({
url: '/system/notify-message/my-page',
method: 'get',
params: query
})
}
// 批量标记已读
export function updateNotifyMessageRead(ids) {
return request({
url: '/system/notify-message/update-read?' + qs.stringify({ids: ids}, { indices: false }),
method: 'put'
})
}
// 标记所有站内信为已读
export function updateAllNotifyMessageRead() {
return request({
url: '/system/notify-message/update-all-read',
method: 'put'
})
}
// 获取当前用户的最新站内信列表
export function getUnreadNotifyMessageList() {
return request({
url: '/system/notify-message/get-unread-list',
method: 'get'
})
}
export function getUnreadNotifyMessageCount() {
return request({
url: '/system/notify-message/get-unread-count',
method: 'get'
})
}

View File

@ -1,38 +0,0 @@
import request from '@/utils/request'
// 获得我的站内信分页
export function getNotifyMessagePage(query) {
return request({
url: '/system/notify-message/page',
method: 'get',
params: query
})
}
// 获得单条我的站内信
export function getNotifyMessage(query) {
return request({
url: '/system/notify-message/get',
method: 'get',
params: query
})
}
// 批量标记已读
export function updateNotifyMessageListRead(data) {
return request({
url: '/system/notify-message/update-list-read',
method: 'put',
data: data
})
}
// 所有未读消息标记已读
export function updateNotifyMessageAllRead(data) {
return request({
url: '/system/notify-message/update-all-read',
method: 'put',
data: data
})
}

View File

@ -1,11 +0,0 @@
import request from '@/utils/request'
// 获得我的站内信分页
export function getNotifyLogPage(query) {
return request({
url: '/system/notify-log/page',
method: 'get',
params: query
})
}

View File

@ -9,7 +9,7 @@ export default {
name: 'YudaoDoc',
data() {
return {
url: 'http://www.iocoder.cn/Yudao/build-debugger-environment/?yudao'
url: 'https://doc.iocoder.cn/'
}
},
methods: {

View File

@ -0,0 +1,83 @@
<template>
<div>
<el-popover placement="bottom" width="600" trigger="click">
<!-- icon 展示 -->
<el-badge slot="reference" :is-dot="unreadCount > 0" type="danger">
<svg-icon icon-class="message" @click="getList"/>
</el-badge>
<!-- 弹出列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column width="120" property="templateNickname" label="日期" />
<el-table-column width="180" property="title" label="发送时间">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="类型" align="center" prop="templateType" width="100">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE" :value="scope.row.templateType" />
</template>
</el-table-column>
<el-table-column property="templateContent" label="内容" />
</el-table>
<!-- 更多 -->
<div style="text-align: right; margin-top: 10px">
<el-button type="primary" size="mini" @click="goMyList">查看全部</el-button>
</div>
</el-popover>
</div>
</template>
<script>
import {getUnreadNotifyMessageCount, getUnreadNotifyMessageList} from "@/api/system/notify/message";
export default {
name: 'NotifyMessage',
data() {
return {
//
loading: false,
//
list: [],
// ,
unreadCount: 0,
}
},
created() {
//
this.getUnreadCount()
//
window.timer = setInterval(()=>{
this.getUnreadCount()
},1000 * 60 * 2)
},
methods: {
getList: function() {
this.loading = true;
getUnreadNotifyMessageList().then(response => {
this.list = response.data;
this.loading = false;
// unreadCount 0
this.unreadCount = 0
});
},
getUnreadCount: function() {
getUnreadNotifyMessageCount().then(response => {
this.unreadCount = response.data;
})
},
goMyList: function() {
this.$router.push({
name: 'MyNotifyMessage'
});
}
}
}
</script>
<style>
.el-badge__content.is-fixed {
top: 10px; /* 保证徽章的位置 */
}
</style>

View File

@ -9,6 +9,9 @@
<template v-if="device!=='mobile'">
<search id="header-search" class="right-menu-item" />
<!-- 站内信 -->
<notify-message class="right-menu-item hover-effect" />
<el-tooltip content="源码地址" effect="dark" placement="bottom">
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
</el-tooltip>
@ -57,6 +60,7 @@ import SizeSelect from '@/components/SizeSelect'
import Search from '@/components/HeaderSearch'
import RuoYiGit from '@/components/RuoYi/Git'
import RuoYiDoc from '@/components/RuoYi/Doc'
import NotifyMessage from '@/layout/components/Message'
import {getPath} from "@/utils/ruoyi";
export default {
@ -68,7 +72,8 @@ export default {
SizeSelect,
Search,
RuoYiGit,
RuoYiDoc
RuoYiDoc,
NotifyMessage
},
computed: {
...mapGetters([

View File

@ -75,7 +75,8 @@ export const constantRoutes = [
meta: {title: '首页', icon: 'dashboard', affix: true}
}
]
}, {
},
{
path: '/user',
component: Layout,
hidden: true,
@ -85,9 +86,14 @@ export const constantRoutes = [
component: (resolve) => require(['@/views/system/user/profile/index'], resolve),
name: 'Profile',
meta: {title: '个人中心', icon: 'user'}
}
]
}, {
}, {
path: 'notify-message',
component: (resolve) => require(['@/views/system/notify/my/index'], resolve),
name: 'MyNotifyMessage',
meta: { title: '我的站内信', icon: 'message' },
}]
},
{
path: '/dict',
component: Layout,
hidden: true,
@ -98,18 +104,8 @@ export const constantRoutes = [
meta: {title: '字典数据', icon: '', activeMenu: '/system/dict'}
}
]
}, {
path: '/property',
component: Layout,
hidden: true,
children: [{
path: 'value/:propertyId(\\d+)',
component: (resolve) => require(['@/views/mall/product/property/value'], resolve),
name: 'PropertyValue',
meta: {title: '商品属性值', icon: '', activeMenu: '/product/property'}
}
]
}, {
},
{
path: '/job',
component: Layout,
hidden: true,
@ -131,24 +127,8 @@ export const constantRoutes = [
meta: {title: '修改生成配置', activeMenu: '/infra/codegen'}
}
]
}, {
path: '/spu',
component: Layout,
hidden: true,
children: [{
path: 'edit/:spuId(\\d+)',
component: (resolve) => require(['@/views/mall/product/spu/save'], resolve),
name: 'SpuEdit',
meta: {title: '修改商品', activeMenu: '/product/spu'}
},
{
path: 'add',
component: (resolve) => require(['@/views/mall/product/spu/save'], resolve),
name: 'SpuAdd',
meta: {title: '添加商品', activeMenu: '/product/spu'}
}
]
}, {
},
{
path: '/bpm',
component: Layout,
hidden: true,
@ -165,7 +145,8 @@ export const constantRoutes = [
meta: {title: '查看 OA 请假', icon: 'view', activeMenu: '/bpm/oa/leave'}
}
]
}, {
},
{
path: '/bpm',
component: Layout,
hidden: true,
@ -197,6 +178,36 @@ export const constantRoutes = [
}
]
},
{
path: '/property',
component: Layout,
hidden: true,
children: [{
path: 'value/:propertyId(\\d+)',
component: (resolve) => require(['@/views/mall/product/property/value'], resolve),
name: 'PropertyValue',
meta: {title: '商品属性值', icon: '', activeMenu: '/product/property'}
}
]
},
{
path: '/spu',
component: Layout,
hidden: true,
children: [{
path: 'edit/:spuId(\\d+)',
component: (resolve) => require(['@/views/mall/product/spu/save'], resolve),
name: 'SpuEdit',
meta: {title: '修改商品', activeMenu: '/product/spu'}
},
{
path: 'add',
component: (resolve) => require(['@/views/mall/product/spu/save'], resolve),
name: 'SpuAdd',
meta: {title: '添加商品', activeMenu: '/product/spu'}
}
]
},
{
path: '/trade/order',
component: Layout,

View File

@ -25,8 +25,8 @@ export const DICT_TYPE = {
SYSTEM_SMS_RECEIVE_STATUS: 'system_sms_receive_status',
SYSTEM_ERROR_CODE_TYPE: 'system_error_code_type',
SYSTEM_OAUTH2_GRANT_TYPE: 'system_oauth2_grant_type',
SYSTEM_NOTIFY_READ_STATUS: "system_notify_read_status",
SYSTEM_MAIL_SEND_STATUS: 'system_mail_send_status',
SYSTEM_NOTIFY_TEMPLATE_TYPE: 'system_notify_template_type',
// ========== INFRA 模块 ==========
INFRA_BOOLEAN_STRING: 'infra_boolean_string',

View File

@ -140,7 +140,7 @@ export default {
//
list: [],
//
title: "",
title: "邮件发送日志详细",
//
open: false,
//

View File

@ -0,0 +1,179 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="用户编号" prop="userId">
<el-input v-model="queryParams.userId" placeholder="请输入用户编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="用户类型" prop="userType">
<el-select v-model="queryParams.userType" placeholder="请选择用户类型" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.USER_TYPE)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="模板编码" prop="templateCode">
<el-input v-model="queryParams.templateCode" placeholder="请输入模板编码" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="模版类型" prop="templateType">
<el-select v-model="queryParams.templateType" placeholder="请选择模版类型" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="用户编号" align="center" prop="userId" />
<el-table-column label="用户类型" align="center" prop="userType">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.USER_TYPE" :value="scope.row.userType" />
</template>
</el-table-column>
<el-table-column label="模板编码" align="center" prop="templateCode" />
<el-table-column label="发送人名称" align="center" prop="templateNickname" />
<el-table-column label="模版内容" align="center" prop="templateContent" />
<el-table-column label="模版类型" align="center" prop="templateType">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE" :value="scope.row.templateType" />
</template>
</el-table-column>
<el-table-column label="是否已读" align="center" prop="readStatus">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.readStatus" />
</template>
</el-table-column>
<el-table-column label="阅读时间" align="center" prop="readTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.readTime) }}</span>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)"
v-hasPermi="['system:notify-message:query']">详细</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 站内信详细-->
<el-dialog :title="title" :visible.sync="open" width="700px" v-dialogDrag append-to-body>
<el-form ref="form" :model="form" label-width="160px">
<el-row>
<el-col :span="24">
<el-form-item label="日志主键:">{{ form.id }}</el-form-item>
<el-form-item label="发送时间:">{{ parseTime(form.createTime) }}</el-form-item>
<el-form-item label="用户编号:">{{ form.userId }}</el-form-item>
<el-form-item label="用户类型:">
<dict-tag :type="DICT_TYPE.USER_TYPE" :value="form.userType"/>
</el-form-item>
<el-form-item label="模板编号:">{{ form.templateId }}</el-form-item>
<el-form-item label="模板编码:">{{ form.templateCode }}</el-form-item>
<el-form-item label="模板类型:">
<dict-tag :type="DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE" :value="form.templateType" />
</el-form-item>
<el-form-item label="模版发送人名称:">{{ form.templateNickname }}</el-form-item>
<el-form-item label="邮件内容:">{{ form.templateContent }}</el-form-item>
<el-form-item label="模版参数:">{{ form.templateParams }}</el-form-item>
<el-form-item label="是否已读:">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="form.readStatus" />
</el-form-item>
<el-form-item label="阅读时间:">{{ parseTime(form.readTime) }}</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="open = false"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getNotifyMessagePage } from "@/api/system/notify/message";
export default {
name: "NotifyMessage",
data() {
return {
//
loading: true,
//
showSearch: true,
//
total: 0,
//
list: [],
//
title: "站内信详细",
//
open: false,
//
queryParams: {
pageNo: 1,
pageSize: 10,
userId: null,
userType: null,
templateCode: null,
templateType: null,
createTime: [],
},
//
form: {},
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
//
getNotifyMessagePage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 详细按钮操作 */
handleView(row) {
this.open = true;
this.form = row;
}
}
};
</script>

View File

@ -1,17 +1,14 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="150px">
<el-form-item label="模板标题" prop="title">
<el-input v-model="queryParams.title" placeholder="请输入模板标题" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="读取状态" prop="readStatus">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="是否已读" prop="readStatus">
<el-select v-model="queryParams.readStatus" placeholder="请选择状态" clearable>
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_NOTIFY_READ_STATUS)"
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-form-item label="发送时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
@ -35,17 +32,21 @@
<!-- 列表 -->
<el-table v-loading="loading" ref="tables" :data="list">
<el-table-column type="selection" width="55" />
<el-table-column label="模板标题" align="center" prop="title" />
<el-table-column label="模板内容" align="center" prop="content" width="300" />
<el-table-column label="发送人" align="center" prop="sendUserName" />
<el-table-column label="发送时间" align="center" prop="sendTime" width="180">
<el-table-column label="发送人" align="center" prop="templateNickname" width="120" />
<el-table-column label="发送时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.sendTime) }}</span>
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="读取状态" align="center" prop="readStatus">
<el-table-column label="类型" align="center" prop="templateType" width="80">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE" :value="scope.row.templateType" />
</template>
</el-table-column>
<el-table-column label="内容" align="center" prop="templateContent" />
<el-table-column label="是否已读" align="center" prop="readStatus" width="80">
<template slot-scope="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_NOTIFY_READ_STATUS" :value="scope.row.readStatus"/>
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.readStatus"/>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="150">
@ -62,7 +63,7 @@
</template>
<script>
import { getNotifyMessagePage, updateNotifyMessageListRead, updateNotifyMessageAllRead} from "@/api/system/notify/myNotify";
import {getMyNotifyMessagePage, updateAllNotifyMessageRead, updateNotifyMessageRead} from "@/api/system/notify/message";
export default {
name: "myNotify",
@ -70,25 +71,17 @@ export default {
return {
//
loading: true,
//
exportLoading: false,
//
showSearch: true,
//
total: 0,
//
list: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNo: 1,
pageSize: 10,
readStatus: null,
code: null,
title: null,
createTime: []
},
};
@ -101,7 +94,7 @@ export default {
getList() {
this.loading = true;
//
getNotifyMessagePage(this.queryParams).then(response => {
getMyNotifyMessagePage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
@ -117,25 +110,26 @@ export default {
this.resetForm("queryForm");
this.handleQuery();
},
handleUpdateList(){
handleUpdateList() {
let list = this.$refs["tables"].selection;
if(list.length != 0){
this.handleUpdate(list.map(v=>v.id))
if (list.length === 0) {
return;
}
this.handleUpdate(list.map(v => v.id))
},
handleUpdateSingle(row){
handleUpdateSingle(row) {
this.handleUpdate([row.id])
},
handleUpdate(ids){
updateNotifyMessageListRead(ids).then(response => {
this.$modal.msgSuccess("修改成功");
this.getList();
handleUpdate(ids) {
updateNotifyMessageRead(ids).then(response => {
this.$modal.msgSuccess("标记已读成功!");
this.getList();
});
},
handleUpdateAll(){
updateNotifyMessageAllRead().then(response => {
this.$modal.msgSuccess("修改成功");
this.getList();
updateAllNotifyMessageRead().then(response => {
this.$modal.msgSuccess("全部已读成功!");
this.getList();
});
}
}

View File

@ -1,120 +0,0 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="150px">
<el-form-item label="模板编码" prop="templateCode">
<el-input v-model="queryParams.templateCode" placeholder="请输入模板编码" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="模板标题" prop="title">
<el-input v-model="queryParams.title" placeholder="请输入模板标题" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="发送时间" prop="sendTime">
<el-date-picker v-model="queryParams.sendTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<!-- <el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
v-hasPermi="['system:notify-log:export']">导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row> -->
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="模板编码" align="center" prop="templateCode" />
<el-table-column label="模板标题" align="center" prop="title" />
<el-table-column label="模板内容" align="center" prop="content" width="300" />
<el-table-column label="阅读状态" align="center" prop="readStatus">
<template slot-scope="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_NOTIFY_READ_STATUS" :value="scope.row.readStatus"/>
</template>
</el-table-column>
<el-table-column label="接收人" align="center" prop="receiveUserName" />
<el-table-column label="发送时间" align="center" prop="sendTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.sendTime) }}</span>
</template>
</el-table-column>
<el-table-column label="阅读时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.readTime) }}</span>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import { getNotifyLogPage} from "@/api/system/notify/notifyLog";
export default {
name: "notifyLog",
data() {
return {
//
loading: true,
//
exportLoading: false,
//
showSearch: true,
//
total: 0,
//
list: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNo: 1,
pageSize: 10,
templateCode: null,
title: null,
sendTime: []
},
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
//
getNotifyLogPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
}
}
</script>
<style>
</style>

View File

@ -1,15 +1,15 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="150px">
<el-form-item label="模板编码" prop="code">
<el-input v-model="queryParams.code" placeholder="请输入模板编码" clearable @keyup.enter.native="handleQuery"/>
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="模板名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入模板名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="模板标题" prop="title">
<el-input v-model="queryParams.title" placeholder="请输入模板标题" clearable @keyup.enter.native="handleQuery"/>
<el-form-item label="模版编码" prop="code">
<el-input v-model="queryParams.code" placeholder="请输入模版编码" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="开启状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择开启状态" clearable>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
@ -30,17 +30,19 @@
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['system:notify-template:create']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
v-hasPermi="['system:notify-template:export']">导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="模板编码" align="center" prop="code" />
<el-table-column label="模板标题" align="center" prop="title" />
<el-table-column label="模板名称" align="center" prop="name" />
<el-table-column label="类型" align="center" prop="type">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE" :value="scope.row.type" />
</template>
</el-table-column>
<el-table-column label="发送人名称" align="center" prop="nickname" />
<el-table-column label="模板内容" align="center" prop="content" width="300" />
<el-table-column label="开启状态" align="center" prop="status">
<template slot-scope="scope">
@ -74,12 +76,21 @@
<el-form-item label="模板编号" prop="code">
<el-input v-model="form.code" placeholder="请输入模板编号" />
</el-form-item>
<el-form-item label="模板标题" prop="title">
<el-input v-model="form.title" placeholder="请输入标题名称" />
<el-form-item label="模板名称" prop="name">
<el-input v-model="form.name" placeholder="请输入模版名称" />
</el-form-item>
<el-form-item label="发件人名称" prop="nickname">
<el-input v-model="form.nickname" placeholder="请输入发件人名称" />
</el-form-item>
<el-form-item label="模板内容" prop="content">
<el-input type="textarea" v-model="form.content" placeholder="请输入模板内容" />
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select v-model="form.type" placeholder="请选择类型">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE)"
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
</el-select>
</el-form-item>
<el-form-item label="开启状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
@ -122,8 +133,9 @@
<script>
import { createNotifyTemplate, updateNotifyTemplate, deleteNotifyTemplate, getNotifyTemplate, getNotifyTemplatePage,
exportNotifyTemplateExcel, sendNotify } from "@/api/system/notify/notifyTemplate";
sendNotify } from "@/api/system/notify/template";
import {listSimpleUsers} from "@/api/system/user";
import {CommonStatusEnum} from "@/utils/constants";
export default {
name: "NotifyTemplate",
@ -131,8 +143,6 @@ export default {
return {
//
loading: true,
//
exportLoading: false,
//
showSearch: true,
//
@ -156,11 +166,12 @@ export default {
form: {},
//
rules: {
status: [{ required: true, message: "开启状态不能为空", trigger: "blur" }],
code: [{ required: true, message: "模板编码不能为空", trigger: "blur" }],
title: [{ required: true, message: "模板标题不能为空", trigger: "blur" }],
content: [{ required: true, message: "模板内容不能为空", trigger: "blur" }],
userId: [{ required: true, message: "接收人不能为空", trigger: "blur" }]
name: [{ required: true, message: "模板名称不能为空", trigger: "blur" }],
code: [{ required: true, message: "模版编码不能为空", trigger: "blur" }],
nickname: [{ required: true, message: "发件人名称不能为空", trigger: "blur" }],
content: [{ required: true, message: "模版内容不能为空", trigger: "blur" }],
type: [{ required: true, message: "类型不能为空", trigger: "change" }],
status: [{ required: true, message: "状态不能为空", trigger: "blur" }],
},
//
users: [],
@ -170,8 +181,8 @@ export default {
params: [], //
},
sendNotifyRules: {
mobile: [{ required: true, message: "手机不能为空", trigger: "blur" }],
templateCode: [{ required: true, message: "手机不能为空", trigger: "blur" }],
userId: [{ required: true, message: "接收人不能为空", trigger: "blur" }],
templateCode: [{ required: true, message: "模版编号不能为空", trigger: "blur" }],
templateParams: { }
}
};
@ -203,10 +214,13 @@ export default {
reset() {
this.form = {
id: undefined,
status: undefined,
name: undefined,
code: undefined,
title: undefined,
nickname: undefined,
content: undefined,
type: undefined,
params: undefined,
status: CommonStatusEnum.ENABLE,
remark: undefined,
};
this.resetForm("form");
@ -270,21 +284,6 @@ export default {
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
//
let params = {...this.queryParams};
params.pageNo = undefined;
params.pageSize = undefined;
//
this.$modal.confirm('是否确认导出所有站内信模板数据项?', "警告").then(() => {
this.exportLoading = true;
return exportNotifyTemplateExcel(params);
}).then(response => {
this.$download.excel(response, '短信模板.xls');
this.exportLoading = false;
}).catch(() => {});
},
/** 发送站内信按钮 */
handleSendNotify(row) {
this.resetSendNotify(row);