Merge remote-tracking branch 'origin/master' into feature/springdoc

# Conflicts:
#	README.md
This commit is contained in:
xingyu
2023-01-30 10:08:31 +08:00
90 changed files with 4027 additions and 135 deletions

View File

@@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.system.api.notify;
import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import cn.iocoder.yudao.module.system.service.notify.NotifyMessageService;
import cn.iocoder.yudao.module.system.service.notify.NotifySendService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 站内信发送 API 实现类
*
* @author xrcoder
*/
@Service
public class NotifyMessageSendApiImpl implements NotifyMessageSendApi {
@Resource
private NotifySendService notifySendService;
@Override
public Long sendSingleMessageToAdmin(NotifySendSingleToUserReqDTO reqDTO) {
return notifySendService.sendSingleNotifyToAdmin(reqDTO.getUserId(),
reqDTO.getTemplateCode(), reqDTO.getTemplateParams());
}
@Override
public Long sendSingleMessageToMember(NotifySendSingleToUserReqDTO reqDTO) {
return notifySendService.sendSingleNotifyToMember(reqDTO.getUserId(),
reqDTO.getTemplateCode(), reqDTO.getTemplateParams());
}
}

View File

@@ -0,0 +1,95 @@
package cn.iocoder.yudao.module.system.controller.admin.notify;
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;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO;
import cn.iocoder.yudao.module.system.service.notify.NotifyMessageService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Api(tags = "管理后台 - 我的站内信")
@RestController
@RequestMapping("/system/notify-message")
@Validated
public class NotifyMessageController {
@Resource
private NotifyMessageService notifyMessageService;
// ========== 管理所有的站内信 ==========
@GetMapping("/get")
@ApiOperation("获得站内信")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('system:notify-message:query')")
public CommonResult<NotifyMessageRespVO> getNotifyMessage(@RequestParam("id") Long id) {
NotifyMessageDO notifyMessage = notifyMessageService.getNotifyMessage(id);
return success(NotifyMessageConvert.INSTANCE.convert(notifyMessage));
}
@GetMapping("/page")
@ApiOperation("获得站内信分页")
@PreAuthorize("@ss.hasPermission('system:notify-message:query')")
public CommonResult<PageResult<NotifyMessageRespVO>> getNotifyMessagePage(@Valid NotifyMessagePageReqVO pageVO) {
PageResult<NotifyMessageDO> pageResult = notifyMessageService.getNotifyMessagePage(pageVO);
return success(NotifyMessageConvert.INSTANCE.convertPage(pageResult));
}
// ========== 查看自己的站内信 ==========
@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));
}
@PutMapping("/update-read")
@ApiOperation("标记站内信为已读")
@ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class)
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> 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

@@ -0,0 +1,83 @@
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.template.*;
import cn.iocoder.yudao.module.system.convert.notify.NotifyTemplateConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import cn.iocoder.yudao.module.system.service.notify.NotifySendService;
import cn.iocoder.yudao.module.system.service.notify.NotifyTemplateService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Api(tags = "管理后台 - 站内信模版")
@RestController
@RequestMapping("/system/notify-template")
@Validated
public class NotifyTemplateController {
@Resource
private NotifyTemplateService notifyTemplateService;
@Resource
private NotifySendService notifySendService;
@PostMapping("/create")
@ApiOperation("创建站内信模版")
@PreAuthorize("@ss.hasPermission('system:notify-template:create')")
public CommonResult<Long> createNotifyTemplate(@Valid @RequestBody NotifyTemplateCreateReqVO createReqVO) {
return success(notifyTemplateService.createNotifyTemplate(createReqVO));
}
@PutMapping("/update")
@ApiOperation("更新站内信模版")
@PreAuthorize("@ss.hasPermission('system:notify-template:update')")
public CommonResult<Boolean> updateNotifyTemplate(@Valid @RequestBody NotifyTemplateUpdateReqVO updateReqVO) {
notifyTemplateService.updateNotifyTemplate(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@ApiOperation("删除站内信模版")
@ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('system:notify-template:delete')")
public CommonResult<Boolean> deleteNotifyTemplate(@RequestParam("id") Long id) {
notifyTemplateService.deleteNotifyTemplate(id);
return success(true);
}
@GetMapping("/get")
@ApiOperation("获得站内信模版")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('system:notify-template:query')")
public CommonResult<NotifyTemplateRespVO> getNotifyTemplate(@RequestParam("id") Long id) {
NotifyTemplateDO notifyTemplate = notifyTemplateService.getNotifyTemplate(id);
return success(NotifyTemplateConvert.INSTANCE.convert(notifyTemplate));
}
@GetMapping("/page")
@ApiOperation("获得站内信模版分页")
@PreAuthorize("@ss.hasPermission('system:notify-template:query')")
public CommonResult<PageResult<NotifyTemplateRespVO>> getNotifyTemplatePage(@Valid NotifyTemplatePageReqVO pageVO) {
PageResult<NotifyTemplateDO> pageResult = notifyTemplateService.getNotifyTemplatePage(pageVO);
return success(NotifyTemplateConvert.INSTANCE.convertPage(pageResult));
}
@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

@@ -0,0 +1,61 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.message;
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 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class NotifyMessageBaseVO {
@ApiModelProperty(value = "用户编号", required = true, example = "25025")
@NotNull(message = "用户编号不能为空")
private Long userId;
@ApiModelProperty(value = "用户类型", required = true, example = "1", notes = "参见 UserTypeEnum 枚举")
@NotNull(message = "用户类型不能为空")
private Byte userType;
@ApiModelProperty(value = "模版编号", required = true, example = "13013")
@NotNull(message = "模版编号不能为空")
private Long templateId;
@ApiModelProperty(value = "模板编码", required = true, example = "test_01")
@NotNull(message = "模板编码不能为空")
private String templateCode;
@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 LocalDateTime readTime;
}

View File

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

View File

@@ -0,0 +1,38 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.message;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
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;
@ApiModel("管理后台 - 站内信分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class NotifyMessagePageReqVO extends PageParam {
@ApiModelProperty(value = "用户编号", example = "25025")
private Long userId;
@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 LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.message;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
@ApiModel("管理后台 - 站内信 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class NotifyMessageRespVO extends NotifyMessageBaseVO {
@ApiModelProperty(value = "ID", required = true, example = "1024")
private Long id;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
}

View File

@@ -0,0 +1,46 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
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;
/**
* 站内信模版 Base VO提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class NotifyTemplateBaseVO {
@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, example = "1", notes = "对应 system_notify_template_type 字典")
@NotNull(message = "模版类型不能为空")
private Integer type;
@ApiModelProperty(value = "发送人名称", required = true, example = "土豆")
@NotEmpty(message = "发送人名称不能为空")
private String nickname;
@ApiModelProperty(value = "模版内容", required = true, example = "我是模版内容")
@NotEmpty(message = "模版内容不能为空")
private String content;
@ApiModelProperty(value = "状态", required = true, example = "1", notes = "参见 CommonStatusEnum 枚举")
@NotNull(message = "状态不能为空")
@InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}")
private Integer status;
@ApiModelProperty(value = "备注", example = "我是备注")
private String remark;
}

View File

@@ -0,0 +1,11 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template;
import lombok.*;
import io.swagger.annotations.*;
@ApiModel("管理后台 - 站内信模版创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class NotifyTemplateCreateReqVO extends NotifyTemplateBaseVO {
}

View File

@@ -0,0 +1,33 @@
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;
import org.springframework.format.annotation.DateTimeFormat;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ApiModel("管理后台 - 站内信模版分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class NotifyTemplatePageReqVO extends PageParam {
@ApiModelProperty(value = "模版编码", example = "test_01")
private String code;
@ApiModelProperty(value = "模版名称", example = "我是名称")
private String name;
@ApiModelProperty(value = "状态", example = "1", notes = "参见 CommonStatusEnum 枚举类")
private Integer status;
@ApiModelProperty(value = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
@ApiModel("管理后台 - 站内信模版 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class NotifyTemplateRespVO extends NotifyTemplateBaseVO {
@ApiModelProperty(value = "ID", required = true, example = "1024")
private Long id;
@ApiModelProperty(value = "参数数组", example = "name,code")
private List<String> params;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
}

View File

@@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template;
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;
@ApiModel("管理后台 - 站内信模板的发送 Request VO")
@Data
public class NotifyTemplateSendReqVO {
@ApiModelProperty(value = "用户id", required = true, example = "01")
@NotNull(message = "用户id不能为空")
private Long userId;
@ApiModelProperty(value = "模板编码", required = true, example = "01")
@NotEmpty(message = "模板编码不能为空")
private String templateCode;
@ApiModelProperty(value = "模板参数")
private Map<String, Object> templateParams;
}

View File

@@ -0,0 +1,17 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template;
import lombok.*;
import io.swagger.annotations.*;
import javax.validation.constraints.*;
@ApiModel("管理后台 - 站内信模版更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class NotifyTemplateUpdateReqVO extends NotifyTemplateBaseVO {
@ApiModelProperty(value = "ID", required = true, example = "1024")
@NotNull(message = "ID 不能为空")
private Long id;
}

View File

@@ -0,0 +1,28 @@
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.message.NotifyMessageRespVO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* 站内信 Convert
*
* @author xrcoder
*/
@Mapper
public interface NotifyMessageConvert {
NotifyMessageConvert INSTANCE = Mappers.getMapper(NotifyMessageConvert.class);
NotifyMessageRespVO convert(NotifyMessageDO bean);
List<NotifyMessageRespVO> convertList(List<NotifyMessageDO> list);
PageResult<NotifyMessageRespVO> convertPage(PageResult<NotifyMessageDO> page);
}

View File

@@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.system.convert.notify;
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.NotifyTemplateRespVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateUpdateReqVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
/**
* 站内信模版 Convert
*
* @author xrcoder
*/
@Mapper
public interface NotifyTemplateConvert {
NotifyTemplateConvert INSTANCE = Mappers.getMapper(NotifyTemplateConvert.class);
NotifyTemplateDO convert(NotifyTemplateCreateReqVO bean);
NotifyTemplateDO convert(NotifyTemplateUpdateReqVO bean);
NotifyTemplateRespVO convert(NotifyTemplateDO bean);
List<NotifyTemplateRespVO> convertList(List<NotifyTemplateDO> list);
PageResult<NotifyTemplateRespVO> convertPage(PageResult<NotifyTemplateDO> page);
}

View File

@@ -0,0 +1,101 @@
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(value = "system_notify_message", autoResultMap = true)
@KeySequence("system_notify_message_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class NotifyMessageDO extends BaseDO {
/**
* 站内信编号,自增
*/
@TableId
private Long id;
/**
* 用户编号
*
* 关联 MemberUserDO 的 id 字段、或者 AdminUserDO 的 id 字段
*/
private Long userId;
/**
* 用户类型
*
* 枚举 {@link UserTypeEnum}
*/
private Integer userType;
// ========= 模板相关字段 =========
/**
* 模版编号
*
* 关联 {@link NotifyTemplateDO#getId()}
*/
private Long templateId;
/**
* 模版编码
*
* 关联 {@link NotifyTemplateDO#getCode()}
*/
private String templateCode;
/**
* 模版类型
*
* 冗余 {@link NotifyTemplateDO#getType()}
*/
private Integer templateType;
/**
* 模版发送人名称
*
* 冗余 {@link NotifyTemplateDO#getNickname()}
*/
private String templateNickname;
/**
* 模版内容
*
* 基于 {@link NotifyTemplateDO#getContent()} 格式化后的内容
*/
private String templateContent;
/**
* 模版参数
*
* 基于 {@link NotifyTemplateDO#getParams()} 输入后的参数
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private Map<String, Object> templateParams;
// ========= 读取相关字段 =========
/**
* 是否已读
*/
private Boolean readStatus;
/**
* 阅读时间
*/
private LocalDateTime readTime;
}

View File

@@ -0,0 +1,72 @@
package cn.iocoder.yudao.module.system.dal.dataobject.notify;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
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.util.List;
/**
* 站内信模版 DO
*
* @author xrcoder
*/
@TableName(value = "system_notify_template", autoResultMap = true)
@KeySequence("system_notify_template_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class NotifyTemplateDO extends BaseDO {
/**
* ID
*/
@TableId
private Long id;
/**
* 模版名称
*/
private String name;
/**
* 模版编码
*/
private String code;
/**
* 模版类型
*
* 对应 system_notify_template_type 字典
*/
private Integer type;
/**
* 发送人名称
*/
private String nickname;
/**
* 模版内容
*/
private String content;
/**
* 参数数组
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private List<String> params;
/**
* 状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
/**
* 备注
*/
private String remark;
}

View File

@@ -0,0 +1,70 @@
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.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
public interface NotifyMessageMapper extends BaseMapperX<NotifyMessageDO> {
default PageResult<NotifyMessageDO> selectPage(NotifyMessagePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<NotifyMessageDO>()
.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>()
.eqIfPresent(NotifyMessageDO::getReadStatus, reqVO.getReadStatus())
.betweenIfPresent(NotifyMessageDO::getCreateTime, reqVO.getCreateTime())
.eq(NotifyMessageDO::getUserId, userId)
.eq(NotifyMessageDO::getUserType, userType)
.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 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) {
return selectCount(new LambdaQueryWrapperX<NotifyMessageDO>()
.eq(NotifyMessageDO::getReadStatus, false)
.eq(NotifyMessageDO::getUserId, userId)
.eq(NotifyMessageDO::getUserType, userType));
}
}

View File

@@ -0,0 +1,26 @@
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.template.NotifyTemplatePageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface NotifyTemplateMapper extends BaseMapperX<NotifyTemplateDO> {
default NotifyTemplateDO selectByCode(String code) {
return selectOne(NotifyTemplateDO::getCode, code);
}
default PageResult<NotifyTemplateDO> selectPage(NotifyTemplatePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<NotifyTemplateDO>()
.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

@@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.system.mq.consumer.notify;
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
import cn.iocoder.yudao.module.system.mq.message.notify.NotifyTemplateRefreshMessage;
import cn.iocoder.yudao.module.system.service.notify.NotifyTemplateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 针对 {@link NotifyTemplateRefreshMessage} 的消费者
*
* @author xrcoder
*/
@Component
@Slf4j
public class NotifyTemplateRefreshConsumer extends AbstractChannelMessageListener<NotifyTemplateRefreshMessage> {
@Resource
private NotifyTemplateService notifyTemplateService;
@Override
public void onMessage(NotifyTemplateRefreshMessage message) {
log.info("[onMessage][收到 NotifyTemplate 刷新消息]");
notifyTemplateService.initLocalCache();
}
}

View File

@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.system.mq.message.notify;
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 站内信模板的数据刷新 Message
*
* @author xrcoder
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class NotifyTemplateRefreshMessage extends AbstractChannelMessage {
@Override
public String getChannel() {
return "system.notify-template.refresh";
}
}

View File

@@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.system.mq.producer.notify;
import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
import cn.iocoder.yudao.module.system.mq.message.notify.NotifyTemplateRefreshMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* Notify 站内信相关消息的 Producer
*
* @author xrcoder
* @since 2022-08-06
*/
@Slf4j
@Component
public class NotifyProducer {
@Resource
private RedisMQTemplate redisMQTemplate;
/**
* 发送 {@link NotifyTemplateRefreshMessage} 消息
*/
public void sendNotifyTemplateRefreshMessage() {
NotifyTemplateRefreshMessage message = new NotifyTemplateRefreshMessage();
redisMQTemplate.send(message);
}
}

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

@@ -0,0 +1,97 @@
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 接口
*
* @author xrcoder
*/
public interface NotifyMessageService {
/**
* 创建站内信
*
* @param userId 用户编号
* @param userType 用户类型
* @param template 模版信息
* @param templateContent 模版内容
* @param templateParams 模版参数
* @return 站内信编号
*/
Long createNotifyMessage(Long userId, Integer userType,
NotifyTemplateDO template, String templateContent, Map<String, Object> templateParams);
/**
* 获得站内信分页
*
* @param pageReqVO 分页查询
* @return 站内信分页
*/
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 用户编号
* @param userType 用户类型
* @return 返回未读站内信条数
*/
Long getUnreadNotifyMessageCount(Long userId, Integer userType);
/**
* 标记站内信为已读
*
* @param ids 站内信编号集合
* @param userId 用户编号
* @param userType 用户类型
* @return 更新到的条数
*/
int updateNotifyMessageRead(Collection<Long> ids, Long userId, Integer userType);
/**
* 标记所有站内信为已读
*
* @param userId 用户编号
* @param userType 用户类型
* @return 更新到的条数
*/
int updateAllNotifyMessageRead(Long userId, Integer userType);
}

View File

@@ -0,0 +1,77 @@
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 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 java.util.Collection;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
/**
* 站内信 Service 实现类
*
* @author xrcoder
*/
@Service
@Validated
public class NotifyMessageServiceImpl implements NotifyMessageService {
@Resource
private NotifyMessageMapper notifyMessageMapper;
@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();
}
@Override
public PageResult<NotifyMessageDO> getNotifyMessagePage(NotifyMessagePageReqVO pageReqVO) {
return notifyMessageMapper.selectPage(pageReqVO);
}
@Override
public PageResult<NotifyMessageDO> getMyMyNotifyMessagePage(NotifyMessageMyPageReqVO pageReqVO, Long userId, Integer userType) {
return notifyMessageMapper.selectPage(pageReqVO, userId, userType);
}
@Override
public NotifyMessageDO getNotifyMessage(Long id) {
return notifyMessageMapper.selectById(id);
}
@Override
public List<NotifyMessageDO> getUnreadNotifyMessageList(Long userId, Integer userType, Integer size) {
return notifyMessageMapper.selectUnreadListByUserIdAndUserType(userId, userType, size);
}
@Override
public Long getUnreadNotifyMessageCount(Long userId, Integer userType) {
return notifyMessageMapper.selectUnreadCountByUserIdAndUserType(userId, userType);
}
@Override
public int updateNotifyMessageRead(Collection<Long> ids, Long userId, Integer userType) {
return notifyMessageMapper.updateListRead(ids, userId, userType);
}
@Override
public int updateAllNotifyMessageRead(Long userId, Integer userType) {
return notifyMessageMapper.updateListRead(userId, userType);
}
}

View File

@@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.system.service.notify;
import java.util.List;
import java.util.Map;
/**
* 站内信发送 Service 接口
*
* @author xrcoder
*/
public interface NotifySendService {
/**
* 发送单条站内信给管理后台的用户
*
* 在 mobile 为空时,使用 userId 加载对应管理员的手机号
*
* @param userId 用户编号
* @param templateCode 短信模板编号
* @param templateParams 短信模板参数
* @return 发送日志编号
*/
Long sendSingleNotifyToAdmin(Long userId,
String templateCode, Map<String, Object> templateParams);
/**
* 发送单条站内信给用户 APP 的用户
*
* 在 mobile 为空时,使用 userId 加载对应会员的手机号
*
* @param userId 用户编号
* @param templateCode 站内信模板编号
* @param templateParams 站内信模板参数
* @return 发送日志编号
*/
Long sendSingleNotifyToMember(Long userId,
String templateCode, Map<String, Object> templateParams);
/**
* 发送单条站内信给用户
*
* @param userId 用户编号
* @param userType 用户类型
* @param templateCode 站内信模板编号
* @param templateParams 站内信模板参数
* @return 发送日志编号
*/
Long sendSingleNotify( Long userId, Integer userType,
String templateCode, Map<String, Object> templateParams);
default void sendBatchNotify(List<String> mobiles, List<Long> userIds, Integer userType,
String templateCode, Map<String, Object> templateParams) {
throw new UnsupportedOperationException("暂时不支持该操作,感兴趣可以实现该功能哟!");
}
}

View File

@@ -0,0 +1,86 @@
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.NotifyTemplateDO;
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.Map;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
/**
* 站内信发送 Service 实现类
*
* @author xrcoder
*/
@Service
@Validated
@Slf4j
public class NotifySendServiceImpl implements NotifySendService {
@Resource
private NotifyTemplateService notifyTemplateService;
@Resource
private NotifyMessageService notifyMessageService;
@Override
public Long sendSingleNotifyToAdmin(Long userId, String templateCode, Map<String, Object> templateParams) {
return sendSingleNotify(userId, UserTypeEnum.ADMIN.getValue(), templateCode, templateParams);
}
@Override
public Long sendSingleNotifyToMember(Long userId, String templateCode, Map<String, Object> templateParams) {
return sendSingleNotify(userId, UserTypeEnum.MEMBER.getValue(), templateCode, templateParams);
}
@Override
public Long sendSingleNotify(Long userId, Integer userType, String templateCode, Map<String, Object> templateParams) {
// 校验模版
NotifyTemplateDO template = checkNotifyTemplateValid(templateCode);
if (Objects.equals(template.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
log.info("[sendSingleNotify][模版({})已经关闭,无法给用户({}/{})发送]", templateCode, userId, userType);
return null;
}
// 校验参数
checkTemplateParams(template, templateParams);
// 发送站内信
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

@@ -0,0 +1,79 @@
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.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.Map;
/**
* 站内信模版 Service 接口
*
* @author xrcoder
*/
public interface NotifyTemplateService {
/**
* 初始化站内信模板的本地缓存
*/
void initLocalCache();
/**
* 获得站内信模板,从缓存中
*
* @param code 模板编码
* @return 站内信模板
*/
NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code);
/**
* 创建站内信模版
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createNotifyTemplate(@Valid NotifyTemplateCreateReqVO createReqVO);
/**
* 更新站内信模版
*
* @param updateReqVO 更新信息
*/
void updateNotifyTemplate(@Valid NotifyTemplateUpdateReqVO updateReqVO);
/**
* 删除站内信模版
*
* @param id 编号
*/
void deleteNotifyTemplate(Long id);
/**
* 获得站内信模版
*
* @param id 编号
* @return 站内信模版
*/
NotifyTemplateDO getNotifyTemplate(Long id);
/**
* 获得站内信模版分页
*
* @param pageReqVO 分页查询
* @return 站内信模版分页
*/
PageResult<NotifyTemplateDO> getNotifyTemplatePage(NotifyTemplatePageReqVO pageReqVO);
/**
* 格式化站内信内容
*
* @param content 站内信模板的内容
* @param params 站内信内容的参数
* @return 格式化后的内容
*/
String formatNotifyTemplateContent(String content, Map<String, Object> params);
}

View File

@@ -0,0 +1,164 @@
package cn.iocoder.yudao.module.system.service.notify;
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.NotifyTemplatePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateUpdateReqVO;
import cn.iocoder.yudao.module.system.convert.notify.NotifyTemplateConvert;
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 com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
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.*;
/**
* 站内信模版 Service 实现类
*
* @author xrcoder
*/
@Service
@Validated
@Slf4j
public class NotifyTemplateServiceImpl implements NotifyTemplateService {
/**
* 正则表达式,匹配 {} 中的变量
*/
private static final Pattern PATTERN_PARAMS = Pattern.compile("\\{(.*?)}");
@Resource
private NotifyTemplateMapper notifyTemplateMapper;
@Resource
private NotifyProducer notifyProducer;
/**
* 站内信模板缓存
* key站内信模板编码 {@link NotifyTemplateDO#getCode()}
* <p>
* 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
*/
private volatile Map<String, NotifyTemplateDO> notifyTemplateCache;
/**
* 初始化站内信模板的本地缓存
*/
@Override
@PostConstruct
public void initLocalCache() {
// 第一步:查询数据
List<NotifyTemplateDO> templates = notifyTemplateMapper.selectList();
log.info("[initLocalCache][缓存站内信模版,数量为:{}]", templates.size());
// 第二步:构建缓存
notifyTemplateCache = CollectionUtils.convertMap(templates, NotifyTemplateDO::getCode);
}
@Override
public NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code) {
return notifyTemplateCache.get(code);
}
@Override
public Long createNotifyTemplate(NotifyTemplateCreateReqVO createReqVO) {
// 校验站内信编码是否重复
checkNotifyTemplateCodeDuplicate(null, createReqVO.getCode());
// 插入
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) {
// 校验存在
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) {
// 校验存在
validateNotifyTemplateExists(id);
// 删除
notifyTemplateMapper.deleteById(id);
// 发送刷新消息
notifyProducer.sendNotifyTemplateRefreshMessage();
}
private void validateNotifyTemplateExists(Long id) {
if (notifyTemplateMapper.selectById(id) == null) {
throw exception(NOTIFY_TEMPLATE_NOT_EXISTS);
}
}
@Override
public NotifyTemplateDO getNotifyTemplate(Long id) {
return notifyTemplateMapper.selectById(id);
}
@Override
public PageResult<NotifyTemplateDO> getNotifyTemplatePage(NotifyTemplatePageReqVO pageReqVO) {
return notifyTemplateMapper.selectPage(pageReqVO);
}
@VisibleForTesting
public void checkNotifyTemplateCodeDuplicate(Long id, String code) {
NotifyTemplateDO template = notifyTemplateMapper.selectByCode(code);
if (template == null) {
return;
}
// 如果 id 为空,说明不用比较是否为相同 id 的字典类型
if (id == null) {
throw exception(NOTIFY_TEMPLATE_CODE_DUPLICATE, code);
}
if (!template.getId().equals(id)) {
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);
}
}