params);
+
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java
new file mode 100644
index 0000000000..5ee8b4bbcf
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java
@@ -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()}
+ *
+ * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
+ */
+ private volatile Map notifyTemplateCache;
+
+ /**
+ * 初始化站内信模板的本地缓存
+ */
+ @Override
+ @PostConstruct
+ public void initLocalCache() {
+ // 第一步:查询数据
+ List 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 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 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 params) {
+ return StrUtil.format(content, params);
+ }
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java
index bd93cd00a0..e1be7e8d62 100644
--- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java
+++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java
@@ -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");
}
diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageServiceImplTest.java
new file mode 100644
index 0000000000..1087918cca
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageServiceImplTest.java
@@ -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 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 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 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 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 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 randomTemplateParams() {
+ return MapUtil.builder().put(randomString(), randomString())
+ .put(randomString(), randomString()).build();
+ }
+
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImplTest.java
new file mode 100644
index 0000000000..e3ee328537
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImplTest.java
@@ -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 templateParams = MapUtil.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 templateParams = MapUtil.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 templateParams = new HashMap<>();
+ // mock 方法
+
+ // 调用,并断言异常
+ assertServiceException(() -> notifySendService.checkTemplateParams(template, templateParams),
+ NOTIFY_SEND_TEMPLATE_PARAM_MISS, "code");
+ }
+
+
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java
new file mode 100644
index 0000000000..5a6f5a508c
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java
@@ -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 pageResult = notifyTemplateService.getNotifyTemplatePage(reqVO);
+ // 断言
+ assertEquals(1, pageResult.getTotal());
+ assertEquals(1, pageResult.getList().size());
+ assertPojoEquals(dbNotifyTemplate, pageResult.getList().get(0));
+ }
+
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/clean.sql b/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/clean.sql
index 1fb4cbaf9c..1be69f28ba 100644
--- a/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/clean.sql
+++ b/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/clean.sql
@@ -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";
diff --git a/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql b/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql
index 1a2c41b738..bf135f39e5 100644
--- a/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql
+++ b/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql
@@ -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 '站内信消息表';
diff --git a/yudao-server/src/main/java/cn/iocoder/yudao/server/controller/DefaultController.java b/yudao-server/src/main/java/cn/iocoder/yudao/server/controller/DefaultController.java
index 14946218a4..4cf1005d89 100644
--- a/yudao-server/src/main/java/cn/iocoder/yudao/server/controller/DefaultController.java
+++ b/yudao-server/src/main/java/cn/iocoder/yudao/server/controller/DefaultController.java
@@ -21,4 +21,10 @@ public class DefaultController {
"[工作流模块 yudao-module-bpm - 已禁用][参考 https://doc.iocoder.cn/bpm/ 开启]");
}
+ @RequestMapping("/admin-api/mp/**")
+ public CommonResult mp404() {
+ return CommonResult.error(NOT_IMPLEMENTED.getCode(),
+ "[微信公众号 yudao-module-mp - 已禁用][参考 https://doc.iocoder.cn/mp/build/ 开启]");
+ }
+
}
diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml
index 9765157006..29ab3b1d1f 100644
--- a/yudao-server/src/main/resources/application.yaml
+++ b/yudao-server/src/main/resources/application.yaml
@@ -133,6 +133,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:
@@ -159,6 +160,7 @@ yudao:
- system_mail_account
- system_mail_template
- system_mail_log
+ - system_notify_template
- infra_codegen_column
- infra_codegen_table
- infra_test_demo
diff --git a/yudao-ui-admin-vue3/package.json b/yudao-ui-admin-vue3/package.json
index e3b7700b36..35165c102d 100644
--- a/yudao-ui-admin-vue3/package.json
+++ b/yudao-ui-admin-vue3/package.json
@@ -1,6 +1,6 @@
{
"name": "yudao-ui-admin-vue3",
- "version": "1.6.6-snapshot.1925",
+ "version": "1.7.0-snapshot.1925",
"description": "基于vue3、vite4、element-plus、typesScript",
"author": "xingyu",
"private": false,
diff --git a/yudao-ui-admin-vue3/src/api/system/mail/template/index.ts b/yudao-ui-admin-vue3/src/api/system/mail/template/index.ts
index a0a0faf2f8..c044ddd4e4 100644
--- a/yudao-ui-admin-vue3/src/api/system/mail/template/index.ts
+++ b/yudao-ui-admin-vue3/src/api/system/mail/template/index.ts
@@ -21,7 +21,7 @@ export interface MailTemplatePageReqVO extends PageParam {
createTime?: Date[]
}
-export interface MailSmsReqVO {
+export interface MailSendReqVO {
mail: string
templateCode: string
templateParams: Map
@@ -53,6 +53,6 @@ export const deleteMailTemplateApi = async (id: number) => {
}
// 发送邮件
-export const sendMailApi = (data: MailSmsReqVO) => {
+export const sendMailApi = (data: MailSendReqVO) => {
return request.post({ url: '/system/mail-template/send-mail', data })
}
diff --git a/yudao-ui-admin-vue3/src/api/system/notify/message/index.ts b/yudao-ui-admin-vue3/src/api/system/notify/message/index.ts
new file mode 100644
index 0000000000..a42d75c213
--- /dev/null
+++ b/yudao-ui-admin-vue3/src/api/system/notify/message/index.ts
@@ -0,0 +1,66 @@
+import request from '@/config/axios'
+import qs from 'qs'
+
+export interface NotifyMessageVO {
+ id: number
+ userId: number
+ userType: number
+ templateId: number
+ templateCode: string
+ templateNickname: string
+ templateContent: string
+ templateType: number
+ templateParams: string
+ readStatus: boolean
+ readTime: Date
+}
+
+export interface NotifyMessagePageReqVO extends PageParam {
+ userId?: number
+ userType?: number
+ templateCode?: string
+ templateType?: number
+ createTime?: Date[]
+}
+
+export interface NotifyMessageMyPageReqVO extends PageParam {
+ readStatus?: boolean
+ createTime?: Date[]
+}
+
+// 查询站内信消息列表
+export const getNotifyMessagePageApi = async (params: NotifyMessagePageReqVO) => {
+ return await request.get({ url: '/system/notify-message/page', params })
+}
+
+// 查询站内信消息详情
+export const getNotifyMessageApi = async (id: number) => {
+ return await request.get({ url: '/system/notify-message/get?id=' + id })
+}
+
+// 获得我的站内信分页
+export const getMyNotifyMessagePage = async (params: NotifyMessageMyPageReqVO) => {
+ return await request.get({ url: '/system/notify-message/my-page', params })
+}
+
+// 批量标记已读
+export const updateNotifyMessageRead = async (ids) => {
+ return await request.put({
+ url: '/system/notify-message/update-read?' + qs.stringify({ ids: ids }, { indices: false })
+ })
+}
+
+// 标记所有站内信为已读
+export const updateAllNotifyMessageRead = async () => {
+ return await request.put({ url: '/system/notify-message/update-all-read' })
+}
+
+// 获取当前用户的最新站内信列表
+export const getUnreadNotifyMessageListApi = async () => {
+ return await request.get({ url: '/system/notify-message/get-unread-list' })
+}
+
+// 获得当前用户的未读站内信数量
+export const getUnreadNotifyMessageCountApi = async () => {
+ return await request.get({ url: '/system/notify-message/get-unread-count' })
+}
diff --git a/yudao-ui-admin-vue3/src/api/system/notify/template/index.ts b/yudao-ui-admin-vue3/src/api/system/notify/template/index.ts
new file mode 100644
index 0000000000..66530a904a
--- /dev/null
+++ b/yudao-ui-admin-vue3/src/api/system/notify/template/index.ts
@@ -0,0 +1,55 @@
+import request from '@/config/axios'
+
+export interface NotifyTemplateVO {
+ id: number
+ name: string
+ code: string
+ content: string
+ type: number
+ params: string
+ status: number
+ remark: string
+}
+
+export interface NotifyTemplatePageReqVO extends PageParam {
+ name?: string
+ code?: string
+ status?: number
+ createTime?: Date[]
+}
+
+export interface NotifySendReqVO {
+ userId: number
+ templateCode: string
+ templateParams: Map
+}
+
+// 查询站内信模板列表
+export const getNotifyTemplatePageApi = async (params: NotifyTemplatePageReqVO) => {
+ return await request.get({ url: '/system/notify-template/page', params })
+}
+
+// 查询站内信模板详情
+export const getNotifyTemplateApi = async (id: number) => {
+ return await request.get({ url: '/system/notify-template/get?id=' + id })
+}
+
+// 新增站内信模板
+export const createNotifyTemplateApi = async (data: NotifyTemplateVO) => {
+ return await request.post({ url: '/system/notify-template/create', data })
+}
+
+// 修改站内信模板
+export const updateNotifyTemplateApi = async (data: NotifyTemplateVO) => {
+ return await request.put({ url: '/system/notify-template/update', data })
+}
+
+// 删除站内信模板
+export const deleteNotifyTemplateApi = async (id: number) => {
+ return await request.delete({ url: '/system/notify-template/delete?id=' + id })
+}
+
+// 发送站内信
+export const sendNotifyApi = (data: NotifySendReqVO) => {
+ return request.post({ url: '/system/notify-template/send-notify', data })
+}
diff --git a/yudao-ui-admin-vue3/src/hooks/web/useXTable.ts b/yudao-ui-admin-vue3/src/hooks/web/useXTable.ts
index fddb7c46d4..501f982b31 100644
--- a/yudao-ui-admin-vue3/src/hooks/web/useXTable.ts
+++ b/yudao-ui-admin-vue3/src/hooks/web/useXTable.ts
@@ -7,7 +7,7 @@ export interface tableMethod {
deleteBatch: () => void // 批量删除
exportList: (fileName?: string) => void // 导出列表
getCurrentColumn: () => void // 获取当前列
- getRadioRecord: () => void // 获取当前选中列,redio
+ getRadioRecord: () => void // 获取当前选中列,radio
getCheckboxRecords: () => void //获取当前选中列, checkbox
}
diff --git a/yudao-ui-admin-vue3/src/layout/components/Message/src/Message.vue b/yudao-ui-admin-vue3/src/layout/components/Message/src/Message.vue
index ba21cfdcee..ee3647dd34 100644
--- a/yudao-ui-admin-vue3/src/layout/components/Message/src/Message.vue
+++ b/yudao-ui-admin-vue3/src/layout/components/Message/src/Message.vue
@@ -1,77 +1,74 @@
-
+
-
-
+
+
-
+
-
+
- {{ item.title }}
- {{ item.date }}
-
-
-
-
-
-
-
-
-
-
-
- {{ item.title }}
- {{ item.date }}
-
-
-
-
-
-
-
-
-
-
-
- {{ item.title }}
- {{ item.date }}
+
+ {{ item.templateNickname }}:{{ item.templateContent }}
+
+
+ {{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}
+
+
+
+
+
diff --git a/yudao-ui-admin-vue3/src/layout/components/UserInfo/src/UserInfo.vue b/yudao-ui-admin-vue3/src/layout/components/UserInfo/src/UserInfo.vue
index 62b5eb5aa7..73e048ab27 100644
--- a/yudao-ui-admin-vue3/src/layout/components/UserInfo/src/UserInfo.vue
+++ b/yudao-ui-admin-vue3/src/layout/components/UserInfo/src/UserInfo.vue
@@ -41,7 +41,7 @@ const loginOut = () => {
.catch(() => {})
}
const toProfile = async () => {
- push('/userinfo/profile')
+ push('/user/profile')
}
const toDocument = () => {
window.open('https://doc.iocoder.cn/')
diff --git a/yudao-ui-admin-vue3/src/router/modules/remaining.ts b/yudao-ui-admin-vue3/src/router/modules/remaining.ts
index e511e1948d..edaace6a49 100644
--- a/yudao-ui-admin-vue3/src/router/modules/remaining.ts
+++ b/yudao-ui-admin-vue3/src/router/modules/remaining.ts
@@ -71,7 +71,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
]
},
{
- path: '/userinfo',
+ path: '/user',
component: Layout,
name: 'UserInfo',
meta: {
@@ -89,6 +89,18 @@ const remainingRouter: AppRouteRecordRaw[] = [
icon: 'ep:user',
title: t('common.profile')
}
+ },
+ {
+ path: 'notify-message',
+ component: () => import('@/views/system/notify/my/index.vue'),
+ name: 'MyNotifyMessage',
+ meta: {
+ canTo: true,
+ hidden: true,
+ noTagsView: false,
+ icon: 'ep:message',
+ title: '我的站内信'
+ }
}
]
},
diff --git a/yudao-ui-admin-vue3/src/utils/dict.ts b/yudao-ui-admin-vue3/src/utils/dict.ts
index 176ddf92d4..b9dfa1601d 100644
--- a/yudao-ui-admin-vue3/src/utils/dict.ts
+++ b/yudao-ui-admin-vue3/src/utils/dict.ts
@@ -91,6 +91,7 @@ export enum DICT_TYPE {
SYSTEM_ERROR_CODE_TYPE = 'system_error_code_type',
SYSTEM_OAUTH2_GRANT_TYPE = 'system_oauth2_grant_type',
SYSTEM_MAIL_SEND_STATUS = 'system_mail_send_status',
+ SYSTEM_NOTIFY_TEMPLATE_TYPE = 'system_notify_template_type',
// ========== INFRA 模块 ==========
INFRA_BOOLEAN_STRING = 'infra_boolean_string',
diff --git a/yudao-ui-admin-vue3/src/views/system/mail/template/index.vue b/yudao-ui-admin-vue3/src/views/system/mail/template/index.vue
index e7ddd23962..4532d6cc4e 100644
--- a/yudao-ui-admin-vue3/src/views/system/mail/template/index.vue
+++ b/yudao-ui-admin-vue3/src/views/system/mail/template/index.vue
@@ -252,7 +252,7 @@ const handleSendMail = (row: any) => {
}
const sendTest = async () => {
- const data: MailTemplateApi.MailSmsReqVO = {
+ const data: MailTemplateApi.MailSendReqVO = {
mail: sendForm.value.mail,
templateCode: sendForm.value.templateCode,
templateParams: sendForm.value.templateParams as unknown as Map
diff --git a/yudao-ui-admin-vue3/src/views/system/notify/message/index.vue b/yudao-ui-admin-vue3/src/views/system/notify/message/index.vue
new file mode 100644
index 0000000000..cb904d2700
--- /dev/null
+++ b/yudao-ui-admin-vue3/src/views/system/notify/message/index.vue
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-ui-admin-vue3/src/views/system/notify/message/message.data.ts b/yudao-ui-admin-vue3/src/views/system/notify/message/message.data.ts
new file mode 100644
index 0000000000..ff5eb315c4
--- /dev/null
+++ b/yudao-ui-admin-vue3/src/views/system/notify/message/message.data.ts
@@ -0,0 +1,101 @@
+import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas'
+
+// CrudSchema
+const crudSchemas = reactive({
+ primaryKey: 'id', // 默认的主键ID
+ primaryTitle: '编号', // 默认显示的值
+ primaryType: 'seq', // 默认为seq,序号模式
+ action: true,
+ actionWidth: '200', // 3个按钮默认200,如有删减对应增减即可
+ columns: [
+ {
+ title: '用户编号',
+ field: 'userId',
+ isSearch: true
+ },
+ {
+ title: '用户类型',
+ field: 'userType',
+ dictType: DICT_TYPE.USER_TYPE,
+ dictClass: 'string',
+ isSearch: true,
+ table: {
+ width: 80
+ }
+ },
+ {
+ title: '模版编号',
+ field: 'templateId'
+ },
+ {
+ title: '模板编码',
+ field: 'templateCode',
+ isSearch: true,
+ table: {
+ width: 80
+ }
+ },
+ {
+ title: '发送人名称',
+ field: 'templateNickname',
+ table: {
+ width: 120
+ }
+ },
+ {
+ title: '模版内容',
+ field: 'templateContent',
+ table: {
+ width: 200
+ }
+ },
+ {
+ title: '模版类型',
+ field: 'templateType',
+ dictType: DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE,
+ dictClass: 'number',
+ isSearch: true,
+ table: {
+ width: 80
+ }
+ },
+ {
+ title: '模版参数',
+ field: 'templateParams',
+ isTable: false
+ },
+ {
+ title: '是否已读',
+ field: 'readStatus',
+ dictType: DICT_TYPE.INFRA_BOOLEAN_STRING,
+ dictClass: 'boolean',
+ table: {
+ width: 80
+ }
+ },
+ {
+ title: '阅读时间',
+ field: 'readTime',
+ formatter: 'formatDate',
+ table: {
+ width: 180
+ }
+ },
+ {
+ title: '创建时间',
+ field: 'createTime',
+ isForm: false,
+ formatter: 'formatDate',
+ search: {
+ show: true,
+ itemRender: {
+ name: 'XDataTimePicker'
+ }
+ },
+ table: {
+ width: 180
+ }
+ }
+ ]
+})
+export const { allSchemas } = useVxeCrudSchemas(crudSchemas)
diff --git a/yudao-ui-admin-vue3/src/views/system/notify/my/index.vue b/yudao-ui-admin-vue3/src/views/system/notify/my/index.vue
new file mode 100644
index 0000000000..620a7430f0
--- /dev/null
+++ b/yudao-ui-admin-vue3/src/views/system/notify/my/index.vue
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-ui-admin-vue3/src/views/system/notify/my/my.data.ts b/yudao-ui-admin-vue3/src/views/system/notify/my/my.data.ts
new file mode 100644
index 0000000000..103ed8eff0
--- /dev/null
+++ b/yudao-ui-admin-vue3/src/views/system/notify/my/my.data.ts
@@ -0,0 +1,58 @@
+import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas'
+
+// CrudSchema
+const crudSchemas = reactive({
+ primaryKey: 'id',
+ primaryTitle: ' ',
+ primaryType: 'checkbox',
+ action: true,
+ actionWidth: '200', // 3个按钮默认200,如有删减对应增减即可
+ columns: [
+ {
+ title: '发送人名称',
+ field: 'templateNickname',
+ table: {
+ width: 120
+ }
+ },
+ {
+ title: '发送时间',
+ field: 'createTime',
+ isForm: false,
+ formatter: 'formatDate',
+ search: {
+ show: true,
+ itemRender: {
+ name: 'XDataTimePicker'
+ }
+ },
+ table: {
+ width: 180
+ }
+ },
+ {
+ title: '类型',
+ field: 'templateType',
+ dictType: DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE,
+ dictClass: 'number',
+ table: {
+ width: 80
+ }
+ },
+ {
+ title: '内容',
+ field: 'templateContent'
+ },
+ {
+ title: '是否已读',
+ field: 'readStatus',
+ dictType: DICT_TYPE.INFRA_BOOLEAN_STRING,
+ dictClass: 'boolean',
+ table: {
+ width: 80
+ },
+ isSearch: true
+ }
+ ]
+})
+export const { allSchemas } = useVxeCrudSchemas(crudSchemas)
diff --git a/yudao-ui-admin-vue3/src/views/system/notify/template/index.vue b/yudao-ui-admin-vue3/src/views/system/notify/template/index.vue
new file mode 100644
index 0000000000..d243518e0d
--- /dev/null
+++ b/yudao-ui-admin-vue3/src/views/system/notify/template/index.vue
@@ -0,0 +1,251 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-ui-admin-vue3/src/views/system/notify/template/template.data.ts b/yudao-ui-admin-vue3/src/views/system/notify/template/template.data.ts
new file mode 100644
index 0000000000..ae8c7b072b
--- /dev/null
+++ b/yudao-ui-admin-vue3/src/views/system/notify/template/template.data.ts
@@ -0,0 +1,85 @@
+import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas'
+
+// 表单校验
+export const rules = reactive({
+ name: [required],
+ code: [required],
+ content: [required],
+ type: [required],
+ status: [required]
+})
+
+// CrudSchema
+const crudSchemas = reactive({
+ primaryKey: 'id',
+ primaryTitle: '编号',
+ primaryType: null,
+ action: true,
+ actionWidth: '260', // 3个按钮默认200,如有删减对应增减即可
+ columns: [
+ {
+ title: '模版编码',
+ field: 'code',
+ isSearch: true
+ },
+ {
+ title: '模板名称',
+ field: 'name',
+ isSearch: true
+ },
+ {
+ title: '发件人名称',
+ field: 'nickname'
+ },
+ {
+ title: '类型',
+ field: 'type',
+ dictType: DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE,
+ dictClass: 'number'
+ },
+ {
+ title: '模版内容',
+ field: 'content',
+ table: {
+ width: 300
+ },
+ form: {
+ component: 'Input',
+ componentProps: {
+ type: 'textarea',
+ rows: 4
+ },
+ colProps: {
+ span: 24
+ }
+ }
+ },
+ {
+ title: '状态',
+ field: 'status',
+ dictType: DICT_TYPE.COMMON_STATUS,
+ dictClass: 'number',
+ isSearch: true
+ },
+ {
+ title: '备注',
+ field: 'remark'
+ },
+ {
+ title: '创建时间',
+ field: 'createTime',
+ isForm: false,
+ formatter: 'formatDate',
+ search: {
+ show: true,
+ itemRender: {
+ name: 'XDataTimePicker'
+ }
+ },
+ table: {
+ width: 180
+ }
+ }
+ ]
+})
+export const { allSchemas } = useVxeCrudSchemas(crudSchemas)
diff --git a/yudao-ui-admin/package.json b/yudao-ui-admin/package.json
index 738ac509fb..73d4d0903e 100644
--- a/yudao-ui-admin/package.json
+++ b/yudao-ui-admin/package.json
@@ -1,6 +1,6 @@
{
"name": "yudao-ui-admin",
- "version": "1.6.6-snapshot",
+ "version": "1.7.0-snapshot",
"description": "芋道管理系统",
"author": "芋道",
"license": "MIT",
diff --git a/yudao-ui-admin/src/api/system/notify/message.js b/yudao-ui-admin/src/api/system/notify/message.js
new file mode 100644
index 0000000000..5ef63a4f28
--- /dev/null
+++ b/yudao-ui-admin/src/api/system/notify/message.js
@@ -0,0 +1,52 @@
+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'
+ })
+}
diff --git a/yudao-ui-admin/src/api/system/notify/template.js b/yudao-ui-admin/src/api/system/notify/template.js
new file mode 100644
index 0000000000..59c0365406
--- /dev/null
+++ b/yudao-ui-admin/src/api/system/notify/template.js
@@ -0,0 +1,64 @@
+import request from '@/utils/request'
+
+// 创建站内信模板
+export function createNotifyTemplate(data) {
+ return request({
+ url: '/system/notify-template/create',
+ method: 'post',
+ data: data
+ })
+}
+
+// 更新站内信模板
+export function updateNotifyTemplate(data) {
+ return request({
+ url: '/system/notify-template/update',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除站内信模板
+export function deleteNotifyTemplate(id) {
+ return request({
+ url: '/system/notify-template/delete?id=' + id,
+ method: 'delete'
+ })
+}
+
+// 获得站内信模板
+export function getNotifyTemplate(id) {
+ return request({
+ url: '/system/notify-template/get?id=' + id,
+ method: 'get'
+ })
+}
+
+// 获得站内信模板分页
+export function getNotifyTemplatePage(query) {
+ return request({
+ url: '/system/notify-template/page',
+ method: 'get',
+ params: query
+ })
+}
+
+// 创建站内信模板
+export function sendNotify(data) {
+ return request({
+ url: '/system/notify-template/send-notify',
+ method: 'post',
+ data: data
+ })
+}
+
+// 导出站内信模板 Excel
+export function exportNotifyTemplateExcel(query) {
+ return request({
+ url: '/system/notify-template/export-excel',
+ method: 'get',
+ params: query,
+ responseType: 'blob'
+ })
+}
+
diff --git a/yudao-ui-admin/src/components/DocAlert/index.vue b/yudao-ui-admin/src/components/DocAlert/index.vue
index 541b8ed207..1344b09a3a 100644
--- a/yudao-ui-admin/src/components/DocAlert/index.vue
+++ b/yudao-ui-admin/src/components/DocAlert/index.vue
@@ -1,5 +1,9 @@
-
+
+
+ {{ '【' + title + '】文档地址:' + url }}
+
+
+
diff --git a/yudao-ui-admin/src/layout/components/Navbar.vue b/yudao-ui-admin/src/layout/components/Navbar.vue
index fbc2e75797..2bec4f399a 100644
--- a/yudao-ui-admin/src/layout/components/Navbar.vue
+++ b/yudao-ui-admin/src/layout/components/Navbar.vue
@@ -9,6 +9,9 @@
+
+
+
@@ -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([
diff --git a/yudao-ui-admin/src/router/index.js b/yudao-ui-admin/src/router/index.js
index be6fabaf2a..90d5e3b539 100644
--- a/yudao-ui-admin/src/router/index.js
+++ b/yudao-ui-admin/src/router/index.js
@@ -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,
diff --git a/yudao-ui-admin/src/utils/dict.js b/yudao-ui-admin/src/utils/dict.js
index 22c0ef40cc..3da6bd68af 100644
--- a/yudao-ui-admin/src/utils/dict.js
+++ b/yudao-ui-admin/src/utils/dict.js
@@ -26,6 +26,7 @@ export const DICT_TYPE = {
SYSTEM_ERROR_CODE_TYPE: 'system_error_code_type',
SYSTEM_OAUTH2_GRANT_TYPE: 'system_oauth2_grant_type',
SYSTEM_MAIL_SEND_STATUS: 'system_mail_send_status',
+ SYSTEM_NOTIFY_TEMPLATE_TYPE: 'system_notify_template_type',
// ========== INFRA 模块 ==========
INFRA_BOOLEAN_STRING: 'infra_boolean_string',
diff --git a/yudao-ui-admin/src/views/mp/account/index.vue b/yudao-ui-admin/src/views/mp/account/index.vue
index 74f7c2c150..251d137e3e 100644
--- a/yudao-ui-admin/src/views/mp/account/index.vue
+++ b/yudao-ui-admin/src/views/mp/account/index.vue
@@ -1,5 +1,6 @@
+
diff --git a/yudao-ui-admin/src/views/mp/autoReply/index.vue b/yudao-ui-admin/src/views/mp/autoReply/index.vue
index 8f3329c6d1..6d55de735e 100644
--- a/yudao-ui-admin/src/views/mp/autoReply/index.vue
+++ b/yudao-ui-admin/src/views/mp/autoReply/index.vue
@@ -26,6 +26,8 @@ SOFTWARE.
-->
+
+
diff --git a/yudao-ui-admin/src/views/mp/draft/index.vue b/yudao-ui-admin/src/views/mp/draft/index.vue
index 3439b1f6e9..c753949c5b 100644
--- a/yudao-ui-admin/src/views/mp/draft/index.vue
+++ b/yudao-ui-admin/src/views/mp/draft/index.vue
@@ -27,6 +27,8 @@ SOFTWARE.
-->
+
+
diff --git a/yudao-ui-admin/src/views/mp/freePublish/index.vue b/yudao-ui-admin/src/views/mp/freePublish/index.vue
index 03c90c8be0..99865033cf 100644
--- a/yudao-ui-admin/src/views/mp/freePublish/index.vue
+++ b/yudao-ui-admin/src/views/mp/freePublish/index.vue
@@ -25,6 +25,8 @@ SOFTWARE.
-->
+
+
diff --git a/yudao-ui-admin/src/views/mp/material/index.vue b/yudao-ui-admin/src/views/mp/material/index.vue
index a351514270..1bad04fb47 100644
--- a/yudao-ui-admin/src/views/mp/material/index.vue
+++ b/yudao-ui-admin/src/views/mp/material/index.vue
@@ -27,6 +27,8 @@ SOFTWARE.
-->
+
+
diff --git a/yudao-ui-admin/src/views/mp/menu/index.vue b/yudao-ui-admin/src/views/mp/menu/index.vue
index 65e960c949..a5ec5602bb 100644
--- a/yudao-ui-admin/src/views/mp/menu/index.vue
+++ b/yudao-ui-admin/src/views/mp/menu/index.vue
@@ -26,6 +26,8 @@ SOFTWARE.
-->
+
+
diff --git a/yudao-ui-admin/src/views/mp/message/index.vue b/yudao-ui-admin/src/views/mp/message/index.vue
index 57d2a17a79..2b6634db8d 100644
--- a/yudao-ui-admin/src/views/mp/message/index.vue
+++ b/yudao-ui-admin/src/views/mp/message/index.vue
@@ -1,5 +1,6 @@
+
diff --git a/yudao-ui-admin/src/views/mp/statistics/index.vue b/yudao-ui-admin/src/views/mp/statistics/index.vue
index 7c2d54996e..21bb4e85ea 100644
--- a/yudao-ui-admin/src/views/mp/statistics/index.vue
+++ b/yudao-ui-admin/src/views/mp/statistics/index.vue
@@ -1,5 +1,7 @@
+
+
diff --git a/yudao-ui-admin/src/views/mp/tag/index.vue b/yudao-ui-admin/src/views/mp/tag/index.vue
index f67b16bdd5..e5597b2c17 100644
--- a/yudao-ui-admin/src/views/mp/tag/index.vue
+++ b/yudao-ui-admin/src/views/mp/tag/index.vue
@@ -1,5 +1,7 @@
+
+
diff --git a/yudao-ui-admin/src/views/mp/user/index.vue b/yudao-ui-admin/src/views/mp/user/index.vue
index 1af2d6c852..f9901153b6 100644
--- a/yudao-ui-admin/src/views/mp/user/index.vue
+++ b/yudao-ui-admin/src/views/mp/user/index.vue
@@ -1,5 +1,6 @@
+
diff --git a/yudao-ui-admin/src/views/system/mail/log/index.vue b/yudao-ui-admin/src/views/system/mail/log/index.vue
index d0b0b34276..fca363bfba 100755
--- a/yudao-ui-admin/src/views/system/mail/log/index.vue
+++ b/yudao-ui-admin/src/views/system/mail/log/index.vue
@@ -140,7 +140,7 @@ export default {
// 邮件日志列表
list: [],
// 弹出层标题
- title: "",
+ title: "邮件发送日志详细",
// 是否显示弹出层
open: false,
// 查询参数
diff --git a/yudao-ui-admin/src/views/system/notify/message/index.vue b/yudao-ui-admin/src/views/system/notify/message/index.vue
new file mode 100644
index 0000000000..0e354f931e
--- /dev/null
+++ b/yudao-ui-admin/src/views/system/notify/message/index.vue
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.readTime) }}
+
+
+
+
+ {{ parseTime(scope.row.createTime) }}
+
+
+
+
+ 详细
+
+
+
+
+
+
+
+
+
+
+
+ {{ form.id }}
+ {{ parseTime(form.createTime) }}
+ {{ form.userId }}
+
+
+
+ {{ form.templateId }}
+ {{ form.templateCode }}
+
+
+
+ {{ form.templateNickname }}
+ {{ form.templateContent }}
+ {{ form.templateParams }}
+
+
+
+ {{ parseTime(form.readTime) }}
+
+
+
+
+
+
+
+
+
diff --git a/yudao-ui-admin/src/views/system/notify/my/index.vue b/yudao-ui-admin/src/views/system/notify/my/index.vue
new file mode 100644
index 0000000000..56a579bec4
--- /dev/null
+++ b/yudao-ui-admin/src/views/system/notify/my/index.vue
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+
+ 标记已读
+
+
+ 全部已读
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.createTime) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 已读
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-ui-admin/src/views/system/notify/template/index.vue b/yudao-ui-admin/src/views/system/notify/template/index.vue
new file mode 100644
index 0000000000..0a9739590e
--- /dev/null
+++ b/yudao-ui-admin/src/views/system/notify/template/index.vue
@@ -0,0 +1,340 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+
+ 新增
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.createTime) }}
+
+
+
+
+ 测试
+ 修改
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{dict.label}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-ui-admin/src/views/system/oauth2/client/index.vue b/yudao-ui-admin/src/views/system/oauth2/client/index.vue
index f4a6d4b8e8..fb7ce7c453 100755
--- a/yudao-ui-admin/src/views/system/oauth2/client/index.vue
+++ b/yudao-ui-admin/src/views/system/oauth2/client/index.vue
@@ -1,5 +1,6 @@
+
diff --git a/yudao-ui-admin/src/views/system/oauth2/token/index.vue b/yudao-ui-admin/src/views/system/oauth2/token/index.vue
index 797904d4b4..e4b15495e2 100644
--- a/yudao-ui-admin/src/views/system/oauth2/token/index.vue
+++ b/yudao-ui-admin/src/views/system/oauth2/token/index.vue
@@ -1,6 +1,8 @@
+
+