Forráskód Böngészése

邮箱模块:完善 template 的单元测试

YunaiV 2 éve
szülő
commit
c0b029b244

+ 5 - 0
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java

@@ -41,6 +41,11 @@ public class LocalDateTimeUtils {
         return LocalDateTime.of(year, mouth, day, 0, 0, 0);
     }
 
+    public static LocalDateTime[] buildBetweenTime(int year1, int mouth1, int day1,
+                                                   int year2, int mouth2, int day2) {
+        return new LocalDateTime[]{buildTime(year1, mouth1, day1), buildTime(year2, mouth2, day2)};
+    }
+
     /**
      * 判断当前时间是否在该时间范围内
      *

+ 2 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImpl.java

@@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper;
 import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;
 import com.google.common.annotations.VisibleForTesting;
+import lombok.Getter;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
@@ -55,6 +56,7 @@ public class MailTemplateServiceImpl implements MailTemplateService {
      *
      * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
      */
+    @Getter
     private volatile Map<String, MailTemplateDO> mailTemplateCache;
 
     @Override

+ 163 - 0
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java

@@ -0,0 +1,163 @@
+package cn.iocoder.yudao.module.system.service.mail;
+
+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.mail.vo.template.MailTemplateCreateReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateUpdateReqVO;
+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.mysql.mail.MailTemplateMapper;
+import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;
+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 java.util.Map;
+
+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.randomLongId;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
+import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+* {@link MailTemplateServiceImpl} 的单元测试类
+*
+* @author 芋道源码
+*/
+@Import(MailTemplateServiceImpl.class)
+public class MailTemplateServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private MailTemplateServiceImpl mailTemplateService;
+
+    @Resource
+    private MailTemplateMapper mailTemplateMapper;
+
+    @MockBean
+    private MailProducer mailProducer;
+
+    @Test
+    public void testInitLocalCache() {
+        MailTemplateDO templateDO01 = randomPojo(MailTemplateDO.class);
+        mailTemplateMapper.insert(templateDO01);
+        MailTemplateDO templateDO02 = randomPojo(MailTemplateDO.class);
+        mailTemplateMapper.insert(templateDO02);
+
+        // 调用
+        mailTemplateService.initLocalCache();
+        // 断言 mailTemplateCache 缓存
+        Map<String, MailTemplateDO> mailTemplateCache = mailTemplateService.getMailTemplateCache();
+        assertPojoEquals(templateDO01, mailTemplateCache.get(templateDO01.getCode()));
+        assertPojoEquals(templateDO02, mailTemplateCache.get(templateDO02.getCode()));
+    }
+
+    @Test
+    public void testCreateMailTemplate_success() {
+        // 准备参数
+        MailTemplateCreateReqVO reqVO = randomPojo(MailTemplateCreateReqVO.class);
+
+        // 调用
+        Long mailTemplateId = mailTemplateService.createMailTemplate(reqVO);
+        // 断言
+        assertNotNull(mailTemplateId);
+        // 校验记录的属性是否正确
+        MailTemplateDO mailTemplate = mailTemplateMapper.selectById(mailTemplateId);
+        assertPojoEquals(reqVO, mailTemplate);
+    }
+
+    @Test
+    public void testUpdateMailTemplate_success() {
+        // mock 数据
+        MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class);
+        mailTemplateMapper.insert(dbMailTemplate);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        MailTemplateUpdateReqVO reqVO = randomPojo(MailTemplateUpdateReqVO.class, o -> {
+            o.setId(dbMailTemplate.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        mailTemplateService.updateMailTemplate(reqVO);
+        // 校验是否更新正确
+        MailTemplateDO mailTemplate = mailTemplateMapper.selectById(reqVO.getId()); // 获取最新的
+        assertPojoEquals(reqVO, mailTemplate);
+    }
+
+    @Test
+    public void testUpdateMailTemplate_notExists() {
+        // 准备参数
+        MailTemplateUpdateReqVO reqVO = randomPojo(MailTemplateUpdateReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> mailTemplateService.updateMailTemplate(reqVO), MAIL_TEMPLATE_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteMailTemplate_success() {
+        // mock 数据
+        MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class);
+        mailTemplateMapper.insert(dbMailTemplate);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbMailTemplate.getId();
+
+        // 调用
+        mailTemplateService.deleteMailTemplate(id);
+       // 校验数据不存在了
+       assertNull(mailTemplateMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteMailTemplate_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> mailTemplateService.deleteMailTemplate(id), MAIL_TEMPLATE_NOT_EXISTS);
+    }
+
+    @Test
+    public void testGetMailTemplatePage() {
+       // mock 数据
+       MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class, o -> { // 等会查询到
+           o.setName("源码");
+           o.setCode("test_01");
+           o.setAccountId(1L);
+           o.setStatus(CommonStatusEnum.ENABLE.getStatus());
+           o.setCreateTime(buildTime(2023, 2, 3));
+       });
+       mailTemplateMapper.insert(dbMailTemplate);
+       // 测试 name 不匹配
+       mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setName("芋道")));
+       // 测试 code 不匹配
+       mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCode("test_02")));
+       // 测试 accountId 不匹配
+       mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setAccountId(2L)));
+       // 测试 status 不匹配
+       mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
+       // 测试 createTime 不匹配
+       mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCreateTime(buildTime(2023, 1, 5))));
+       // 准备参数
+       MailTemplatePageReqVO reqVO = new MailTemplatePageReqVO();
+       reqVO.setName("源");
+       reqVO.setCode("est_01");
+       reqVO.setAccountId(1L);
+       reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
+       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 5));
+
+       // 调用
+       PageResult<MailTemplateDO> pageResult = mailTemplateService.getMailTemplatePage(reqVO);
+       // 断言
+       assertEquals(1, pageResult.getTotal());
+       assertEquals(1, pageResult.getList().size());
+       assertPojoEquals(dbMailTemplate, pageResult.getList().get(0));
+    }
+
+}

+ 1 - 0
yudao-module-system/yudao-module-system-biz/src/test/resources/sql/clean.sql

@@ -26,3 +26,4 @@ DELETE FROM "system_oauth2_access_token";
 DELETE FROM "system_oauth2_refresh_token";
 DELETE FROM "system_oauth2_code";
 DELETE FROM "system_mail_account";
+DELETE FROM "system_mail_template";

+ 32 - 13
yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql

@@ -568,17 +568,36 @@ CREATE TABLE IF NOT EXISTS "system_oauth2_code" (
 ) COMMENT 'OAuth2 刷新令牌';
 
 CREATE TABLE IF NOT EXISTS "system_mail_account" (
-     "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
-     "mail" varchar NOT NULL,
-     "username" varchar NOT NULL,
-     "password" varchar NOT NULL,
-     "host" varchar NOT NULL,
-     "port" int NOT NULL,
-     "ssl_enable" bit NOT NULL,
-     "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")
+    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "mail" varchar NOT NULL,
+    "username" varchar NOT NULL,
+    "password" varchar NOT NULL,
+    "host" varchar NOT NULL,
+    "port" int NOT NULL,
+    "ssl_enable" bit NOT NULL,
+    "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_mail_template" (
+    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "name" varchar NOT NULL,
+    "code" varchar NOT NULL,
+    "account_id" bigint NOT NULL,
+    "nickname" varchar,
+    "title" varchar NOT NULL,
+    "content" varchar NOT NULL,
+    "params" varchar NOT NULL,
+    "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 '邮件模版表';

+ 9 - 0
yudao-ui-admin/src/api/system/mail/template.js

@@ -42,3 +42,12 @@ export function getMailTemplatePage(query) {
     params: query
   })
 }
+
+// 发送测试邮件
+export function sendMail(data) {
+  return request({
+    url: '/system/mail-template/send-mail',
+    method: 'post',
+    data: data
+  })
+}

+ 1 - 1
yudao-ui-admin/src/api/system/sms/smsTemplate.js

@@ -43,7 +43,7 @@ export function getSmsTemplatePage(query) {
   })
 }
 
-// 创建短信模板
+// 发送测试短信
 export function sendSms(data) {
   return request({
     url: '/system/sms-template/send-sms',

+ 86 - 7
yudao-ui-admin/src/views/system/mail/template/index.vue

@@ -44,6 +44,7 @@
       <el-table-column label="模板编码" align="center" prop="code" />
       <el-table-column label="模板名称" align="center" prop="name" />
       <el-table-column label="模板标题" align="center" prop="title" />
+      <el-table-column label="模板内容" align="center" prop="content" :show-overflow-tooltip="true" />
       <el-table-column label="邮箱账号" align="center" prop="accountId" width="200">
         <template v-slot="scope">
           {{ accountOptions.find(account => account.id === scope.row.accountId)?.mail }}
@@ -55,14 +56,15 @@
           <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
         </template>
       </el-table-column>
-      <el-table-column label="备注" align="center" prop="remark" />
       <el-table-column label="创建时间" align="center" prop="createTime" width="180">
         <template v-slot="scope">
           <span>{{ parseTime(scope.row.createTime) }}</span>
         </template>
       </el-table-column>
-      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="150">
         <template v-slot="scope">
+          <el-button size="mini" type="text" icon="el-icon-share" @click="handleSend(scope.row)"
+                     v-hasPermi="['system:mail-template:send-mail']">测试</el-button>
           <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
                      v-hasPermi="['system:mail-template:update']">修改</el-button>
           <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
@@ -112,11 +114,30 @@
         <el-button @click="cancel">取 消</el-button>
       </div>
     </el-dialog>
+
+    <!-- 对话框(发送邮件) -->
+    <el-dialog title="测试发送邮件" :visible.sync="sendOpen" width="500px" append-to-body>
+      <el-form ref="sendForm" :model="sendForm" :rules="sendRules" label-width="140px">
+        <el-form-item label="模板内容" prop="content">
+          <el-input v-model="sendForm.content" type="textarea" placeholder="请输入模板内容" readonly />
+        </el-form-item>
+        <el-form-item label="收件邮箱" prop="mail">
+          <el-input v-model="sendForm.mail" placeholder="请输入收件邮箱" />
+        </el-form-item>
+        <el-form-item v-for="param in sendForm.params" :key="param" :label="'参数 {' + param + '}'" :prop="'templateParams.' + param">
+          <el-input v-model="sendForm.templateParams[param]" :placeholder="'请输入 ' + param + ' 参数'" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitSendForm">确 定</el-button>
+        <el-button @click="cancelSend">取 消</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script>
-import { createMailTemplate, updateMailTemplate, deleteMailTemplate, getMailTemplate, getMailTemplatePage } from "@/api/system/mail/template";
+import { createMailTemplate, updateMailTemplate, deleteMailTemplate, getMailTemplate, getMailTemplatePage, sendMail } from "@/api/system/mail/template";
 import Editor from '@/components/Editor';
 import { CommonStatusEnum } from "@/utils/constants";
 import { getSimpleMailAccountList } from "@/api/system/mail/account";
@@ -130,8 +151,6 @@ export default {
     return {
       // 遮罩层
       loading: true,
-      // 导出遮罩层
-      exportLoading: false,
       // 显示搜索条件
       showSearch: true,
       // 总条数
@@ -164,7 +183,18 @@ export default {
         status: [{ required: true, message: "开启状态不能为空", trigger: "blur" }],
       },
       // 邮箱账号
-      accountOptions: []
+      accountOptions: [],
+
+      // 发送邮箱
+      sendOpen: false,
+      sendForm: {
+        params: [], // 模板的参数列表
+      },
+      sendRules: {
+        mail: [{ required: true, message: "收件邮箱不能为空", trigger: "blur" }],
+        templateCode: [{ required: true, message: "模版编码不能为空", trigger: "blur" }],
+        templateParams: { }
+      }
     };
   },
   created() {
@@ -263,7 +293,56 @@ export default {
           this.getList();
           this.$modal.msgSuccess("删除成功");
         }).catch(() => {});
-    }
+    },
+    /** 发送短息按钮 */
+    handleSend(row) {
+      this.resetSend(row);
+      // 设置参数
+      this.sendForm.content = row.content;
+      this.sendForm.params = row.params;
+      this.sendForm.templateCode = row.code;
+      this.sendForm.templateParams = row.params.reduce(function(obj, item) {
+        obj[item] = undefined;
+        return obj;
+      }, {});
+      // 根据 row 重置 rules
+      this.sendRules.templateParams = row.params.reduce(function(obj, item) {
+        obj[item] = { required: true, message: '参数 ' + item + " 不能为空", trigger: "change" };
+        return obj;
+      }, {});
+      // 设置打开
+      this.sendOpen = true;
+    },
+    /** 重置发送邮箱的表单 */
+    resetSend() {
+      // 根据 row 重置表单
+      this.sendForm = {
+        content: undefined,
+        params: undefined,
+        mobile: undefined,
+        templateCode: undefined,
+        templateParams: {}
+      };
+      this.resetForm("sendForm");
+    },
+    /** 取消发送邮箱 */
+    cancelSend() {
+      this.sendOpen = false;
+      this.resetSend();
+    },
+    /** 提交按钮 */
+    submitSendForm() {
+      this.$refs["sendForm"].validate(valid => {
+        if (!valid) {
+          return;
+        }
+        // 添加的提交
+        sendMail(this.sendForm).then(response => {
+          this.$modal.msgSuccess("提交发送成功!发送结果,见发送日志编号:" + response.data);
+          this.sendOpen = false;
+        });
+      });
+    },
   }
 };
 </script>

+ 1 - 1
yudao-ui-admin/src/views/system/sms/smsTemplate.vue

@@ -216,7 +216,7 @@ export default {
       },
       sendSmsRules: {
         mobile: [{ required: true, message: "手机不能为空", trigger: "blur" }],
-        templateCode: [{ required: true, message: "手机不能为空", trigger: "blur" }],
+        templateCode: [{ required: true, message: "模版编码不能为空", trigger: "blur" }],
         templateParams: { }
       }
     };