resultBody);
+
+ /**
+ * 排序值,拦截器根据order值顺序执行
+ *
+ * 值越小,越早执行
+ *
+ * @return 排序值
+ */
+ int getOrder();
+}
diff --git a/src/main/java/cn/iocoder/dashboard/framework/msg/sms/intercepter/SmsLogIntercepter.java b/src/main/java/cn/iocoder/dashboard/framework/msg/sms/intercepter/SmsLogIntercepter.java
new file mode 100644
index 000000000..9a7effd95
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/msg/sms/intercepter/SmsLogIntercepter.java
@@ -0,0 +1,46 @@
+package cn.iocoder.dashboard.framework.msg.sms.intercepter;
+
+import cn.iocoder.dashboard.framework.msg.sms.SmsBody;
+import cn.iocoder.dashboard.framework.msg.sms.SmsResult;
+import cn.iocoder.dashboard.modules.msg.dal.mysql.dao.sms.SmsLogMapper;
+import cn.iocoder.dashboard.util.json.JsonUtils;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Collection;
+
+/**
+ * 短信日志拦截器
+ *
+ * @author zzf
+ * @date 2021/1/22 15:46
+ */
+@Slf4j
+public class SmsLogIntercepter implements SmsIntercepter {
+
+
+ @Override
+ public void beforeSender(SmsBody msgBody, Collection targets) {
+ log.debug("ready send sms, body: {}, target: {}", JsonUtils.toJsonString(msgBody), targets);
+
+ }
+
+ @Override
+ public void afterSender(SmsBody msgBody, Collection targets, SmsResult resultBody) {
+ if (resultBody.getSuccess()) {
+ //
+ } else {
+ log.warn("send sms fail, body: {}, target: {}, resultBody: {}",
+ JsonUtils.toJsonString(msgBody),
+ targets,
+ JsonUtils.toJsonString(resultBody)
+ );
+ }
+
+ }
+
+ @Override
+ public int getOrder() {
+ return 0;
+ }
+}
diff --git a/src/main/java/cn/iocoder/dashboard/framework/msg/sms/proxy/DefaultSmsSenderProxy.java b/src/main/java/cn/iocoder/dashboard/framework/msg/sms/proxy/DefaultSmsSenderProxy.java
new file mode 100644
index 000000000..f9d7e566f
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/msg/sms/proxy/DefaultSmsSenderProxy.java
@@ -0,0 +1,41 @@
+package cn.iocoder.dashboard.framework.msg.sms.proxy;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.iocoder.dashboard.framework.msg.sms.SmsBody;
+import cn.iocoder.dashboard.framework.msg.sms.SmsResult;
+import cn.iocoder.dashboard.framework.msg.sms.SmsSender;
+import cn.iocoder.dashboard.framework.msg.sms.intercepter.AbstractSmsIntercepterChain;
+
+import java.util.Collection;
+
+/**
+ * 消息父接口
+ *
+ * @author zzf
+ * @date 2021/1/22 15:46
+ */
+public class DefaultSmsSenderProxy implements SmsSender {
+
+ private final SmsSender smsSender;
+ private final AbstractSmsIntercepterChain chain;
+
+ @Override
+ public SmsResult send(SmsBody msgBody, Collection targets) {
+ if (ObjectUtil.isNotNull(chain) && ObjectUtil.isNotEmpty(chain.getIntercepterList())) {
+ chain.getIntercepterList().forEach(s -> s.beforeSender(msgBody, targets));
+ }
+
+ SmsResult resultBody = smsSender.send(msgBody, targets);
+
+ if (ObjectUtil.isNotNull(chain) && ObjectUtil.isNotEmpty(chain.getIntercepterList())) {
+ chain.getIntercepterList().forEach(s -> s.afterSender(msgBody, targets, resultBody));
+ }
+ return resultBody;
+ }
+
+ public DefaultSmsSenderProxy(SmsSender smsSender,
+ AbstractSmsIntercepterChain chain) {
+ this.smsSender = smsSender;
+ this.chain = chain;
+ }
+}
diff --git a/src/main/java/cn/iocoder/dashboard/framework/msg/sms/proxy/SmsSenderProxy.java b/src/main/java/cn/iocoder/dashboard/framework/msg/sms/proxy/SmsSenderProxy.java
new file mode 100644
index 000000000..069916e2a
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/msg/sms/proxy/SmsSenderProxy.java
@@ -0,0 +1,34 @@
+package cn.iocoder.dashboard.framework.msg.sms.proxy;
+
+import cn.iocoder.dashboard.framework.msg.sms.SmsSender;
+import cn.iocoder.dashboard.framework.msg.sms.intercepter.SmsIntercepter;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 消息父接口
+ *
+ * @author zzf
+ * @date 2021/1/22 15:46
+ */
+public interface SmsSenderProxy extends SmsSender {
+
+ /**
+ * 添加短信拦截器
+ *
+ * @param smsIntercepter 短信拦截器
+ */
+ default void addSmsIntercepter(SmsIntercepter smsIntercepter) {
+ addSmsIntercepter(Collections.singletonList(smsIntercepter));
+ }
+
+ /**
+ * 添加短信拦截器
+ *
+ * @param smsIntercepterList 短信拦截器数组
+ */
+ void addSmsIntercepter(List smsIntercepterList);
+
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/framework/redis/core/RedisKeyDefine.java b/src/main/java/cn/iocoder/dashboard/framework/redis/core/RedisKeyDefine.java
index b9adcc3f1..ee3a342a0 100644
--- a/src/main/java/cn/iocoder/dashboard/framework/redis/core/RedisKeyDefine.java
+++ b/src/main/java/cn/iocoder/dashboard/framework/redis/core/RedisKeyDefine.java
@@ -1,8 +1,6 @@
package cn.iocoder.dashboard.framework.redis.core;
-import lombok.AllArgsConstructor;
import lombok.Data;
-import lombok.Getter;
import java.time.Duration;
@@ -26,20 +24,15 @@ public class RedisKeyDefine {
}
- @Getter
- @AllArgsConstructor
- public enum TimeoutTypeEnum {
+ /**
+ * 过期时间 - 永不过期
+ */
+ public static final Duration TIMEOUT_FOREVER = null;
- FOREVER(1), // 永不超时
- DYNAMIC(2), // 动态超时
- FIXED(3); // 固定超时
-
- /**
- * 类型
- */
- private final Integer type;
-
- }
+ /**
+ * 过期时间 - 动态,通过参数传入
+ */
+ public static final Duration TIMEOUT_DYNAMIC = null;
/**
* Key 模板
@@ -55,12 +48,10 @@ public class RedisKeyDefine {
* 如果是使用分布式锁,设置为 {@link java.util.concurrent.locks.Lock} 类型
*/
private final Class> valueType;
- /**
- * 超时类型
- */
- private final TimeoutTypeEnum timeoutType;
/**
* 过期时间
+ *
+ * 为空时,表示永不过期 {@link #TIMEOUT_FOREVER}
*/
private final Duration timeout;
@@ -68,20 +59,7 @@ public class RedisKeyDefine {
this.keyTemplate = keyTemplate;
this.keyType = keyType;
this.valueType = valueType;
- this.timeoutType = TimeoutTypeEnum.FIXED;
this.timeout = timeout;
- // 添加注册表
- RedisKeyRegistry.add(this);
- }
-
- public RedisKeyDefine(String keyTemplate, KeyTypeEnum keyType, Class> valueType, TimeoutTypeEnum timeoutType) {
- this.keyTemplate = keyTemplate;
- this.keyType = keyType;
- this.valueType = valueType;
- this.timeoutType = timeoutType;
- this.timeout = Duration.ZERO;
- // 添加注册表
- RedisKeyRegistry.add(this);
}
}
diff --git a/src/main/java/cn/iocoder/dashboard/framework/redis/core/RedisKeyRegistry.java b/src/main/java/cn/iocoder/dashboard/framework/redis/core/RedisKeyRegistry.java
deleted file mode 100644
index 4a220fbd0..000000000
--- a/src/main/java/cn/iocoder/dashboard/framework/redis/core/RedisKeyRegistry.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package cn.iocoder.dashboard.framework.redis.core;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * {@link RedisKeyDefine} 注册表
- */
-public class RedisKeyRegistry {
-
- private static final List defines = new ArrayList<>();
-
- public static void add(RedisKeyDefine define) {
- defines.add(define);
- }
-
- public static List list() {
- return defines;
- }
-
- public static int size() {
- return defines.size();
- }
-
-}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/SmsChannelController.java b/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/SmsChannelController.java
new file mode 100644
index 000000000..c28d9b862
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/SmsChannelController.java
@@ -0,0 +1,54 @@
+package cn.iocoder.dashboard.modules.msg.controller.sms;
+
+import cn.iocoder.dashboard.common.pojo.CommonResult;
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.modules.msg.controller.sms.vo.req.SmsChannelCreateReqVO;
+import cn.iocoder.dashboard.modules.msg.controller.sms.vo.req.SmsChannelPageReqVO;
+import cn.iocoder.dashboard.modules.msg.controller.sms.vo.resp.SmsChannelEnumRespVO;
+import cn.iocoder.dashboard.modules.msg.dal.mysql.daoobject.sms.SmsChannelDO;
+import cn.iocoder.dashboard.modules.msg.service.sms.SmsChannelService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
+
+@Api("短信 渠道/签名 API")
+@RestController
+@RequestMapping("/sms/channel")
+public class SmsChannelController {
+
+ @Resource
+ private SmsChannelService service;
+
+ @ApiOperation("获取渠道/签名分页")
+ @GetMapping("/page")
+ public CommonResult> getPermissionInfo(@Validated SmsChannelPageReqVO reqVO) {
+ return success(service.pageChannels(reqVO));
+ }
+
+ @ApiOperation("获取渠道枚举")
+ @GetMapping("/list/channel-enum")
+ public CommonResult> getChannelEnums() {
+ return success(service.getChannelEnums());
+ }
+
+
+ @ApiOperation("添加消息渠道")
+ @PostMapping("/create")
+ public CommonResult add(@Validated @RequestBody SmsChannelCreateReqVO reqVO) {
+ return success(service.createChannel(reqVO));
+ }
+
+ @ApiOperation("刷新消息渠道信息")
+ @PutMapping("/flush")
+ public CommonResult flushChannel() {
+ return success(service.flushChannel());
+ }
+
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/SmsTemplateController.java b/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/SmsTemplateController.java
new file mode 100644
index 000000000..8bd73b12e
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/SmsTemplateController.java
@@ -0,0 +1,84 @@
+package cn.iocoder.dashboard.modules.msg.controller.sms;
+
+import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
+import cn.iocoder.dashboard.common.pojo.CommonResult;
+import cn.iocoder.dashboard.framework.logger.operatelog.core.annotations.OperateLog;
+import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthLoginReqVO;
+import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthLoginRespVO;
+import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthMenuRespVO;
+import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthPermissionInfoRespVO;
+import cn.iocoder.dashboard.modules.system.convert.auth.SysAuthConvert;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
+import cn.iocoder.dashboard.modules.system.enums.permission.MenuTypeEnum;
+import cn.iocoder.dashboard.modules.system.service.auth.SysAuthService;
+import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
+import cn.iocoder.dashboard.modules.system.service.permission.SysRoleService;
+import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
+import cn.iocoder.dashboard.util.collection.SetUtils;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.util.List;
+
+import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
+import static cn.iocoder.dashboard.framework.security.core.util.SecurityUtils.getLoginUserId;
+import static cn.iocoder.dashboard.framework.security.core.util.SecurityUtils.getLoginUserRoleIds;
+
+@Api("认证 API")
+@RestController
+@RequestMapping("/sms/template")
+public class SmsTemplateController {
+
+ @Resource
+ private SysAuthService authService;
+ @Resource
+ private SysUserService userService;
+ @Resource
+ private SysRoleService roleService;
+ @Resource
+ private SysPermissionService permissionService;
+
+ @ApiOperation("使用账号密码登录")
+ @PostMapping("/login")
+ @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
+ public CommonResult login(@RequestBody @Valid SysAuthLoginReqVO reqVO) {
+ String token = authService.login(reqVO.getUsername(), reqVO.getPassword(), reqVO.getUuid(), reqVO.getCode());
+ // 返回结果
+ return success(SysAuthLoginRespVO.builder().token(token).build());
+ }
+
+ @ApiOperation("获取登陆用户的权限信息")
+ @GetMapping("/get-permission-info")
+ public CommonResult getPermissionInfo() {
+ // 获得用户信息
+ SysUserDO user = userService.getUser(getLoginUserId());
+ if (user == null) {
+ return null;
+ }
+ // 获得角色列表
+ List roleList = roleService.listRolesFromCache(getLoginUserRoleIds());
+ // 获得菜单列表
+ List menuList = permissionService.listRoleMenusFromCache(getLoginUserRoleIds(),
+ SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType(), MenuTypeEnum.BUTTON.getType()),
+ SetUtils.asSet(CommonStatusEnum.ENABLE.getStatus()));
+ // 拼接结果返回
+ return success(SysAuthConvert.INSTANCE.convert(user, roleList, menuList));
+ }
+
+ @ApiOperation("获得登陆用户的菜单列表")
+ @GetMapping("list-menus")
+ public CommonResult> listMenus() {
+ // 获得用户拥有的菜单列表
+ List menuList = permissionService.listRoleMenusFromCache(getLoginUserRoleIds(),
+ SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType()), // 只要目录和菜单类型
+ SetUtils.asSet(CommonStatusEnum.ENABLE.getStatus())); // 只要开启的
+ // 转换成 Tree 结构返回
+ return success(SysAuthConvert.INSTANCE.buildMenuTree(menuList));
+ }
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/vo/SmsChannelAllVO.java b/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/vo/SmsChannelAllVO.java
new file mode 100644
index 000000000..bac72c959
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/vo/SmsChannelAllVO.java
@@ -0,0 +1,58 @@
+package cn.iocoder.dashboard.modules.msg.controller.sms.vo;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 渠道(包含模板)信息VO类
+ *
+ * @author zzf
+ * @date 2021/1/25 17:01
+ */
+@Data
+@EqualsAndHashCode
+public class SmsChannelAllVO implements Serializable {
+
+ /**
+ * id
+ */
+ private Long id;
+
+ /**
+ * 编码(来自枚举类 阿里、华为、七牛等)
+ */
+ private String code;
+
+ /**
+ * 渠道账号id
+ */
+ private String apiKey;
+
+ /**
+ * 渠道账号秘钥
+ */
+ private String apiSecret;
+
+ /**
+ * 实际渠道签名唯一标识
+ */
+ private String apiSignatureId;
+
+ /**
+ * 签名值
+ */
+ private String signature;
+
+ /**
+ * 该渠道名下的短信模板集合
+ */
+ private List templateList;
+
+ public SmsTemplateVO getTemplateByTemplateCode(String tempCode) {
+ return templateList.stream().filter(s -> s.getCode().equals(tempCode)).findFirst().get();
+ }
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/vo/SmsTemplateVO.java b/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/vo/SmsTemplateVO.java
new file mode 100644
index 000000000..04bfe9a62
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/vo/SmsTemplateVO.java
@@ -0,0 +1,30 @@
+package cn.iocoder.dashboard.modules.msg.controller.sms.vo;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 渠道模板VO类
+ *
+ * @author zzf
+ * @date 2021/1/25 17:03
+ */
+@Data
+@EqualsAndHashCode
+public class SmsTemplateVO {
+
+ /**
+ * 业务编码(来自数据字典, 用户自定义业务场景 一个场景可以有多个模板)
+ */
+ private String bizCode;
+ /**
+ * 编码
+ */
+ private String code;
+
+ /**
+ * 实际渠道模板唯一标识
+ */
+ private String apiTemplateId;
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/vo/req/SmsChannelCreateReqVO.java b/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/vo/req/SmsChannelCreateReqVO.java
new file mode 100644
index 000000000..729088b65
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/vo/req/SmsChannelCreateReqVO.java
@@ -0,0 +1,44 @@
+package cn.iocoder.dashboard.modules.msg.controller.sms.vo.req;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+
+@ApiModel("消息渠道创建 Request VO")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode
+public class SmsChannelCreateReqVO implements Serializable {
+
+ @ApiModelProperty("编码(来自枚举类 阿里、华为、七牛等)")
+ private String code;
+
+ @ApiModelProperty("渠道账号id")
+ private String apiKey;
+
+ @ApiModelProperty("渠道账号秘钥")
+ private String apiSecret;
+
+ @ApiModelProperty("优先级(存在多个签名时,选择值最小的,渠道不可用时,按优先级从小到大切换)")
+ private Integer priority;
+
+ @ApiModelProperty("名称")
+ private String name;
+
+ @ApiModelProperty("签名值")
+ private String signature;
+
+ @ApiModelProperty("备注")
+ private String remark;
+
+ @ApiModelProperty("启用状态(0正常 1停用)")
+ private Integer status;
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/vo/req/SmsChannelPageReqVO.java b/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/vo/req/SmsChannelPageReqVO.java
new file mode 100644
index 000000000..dd2df146e
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/vo/req/SmsChannelPageReqVO.java
@@ -0,0 +1,29 @@
+package cn.iocoder.dashboard.modules.msg.controller.sms.vo.req;
+
+import cn.iocoder.dashboard.common.pojo.PageParam;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.util.Date;
+
+import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@ApiModel("消息渠道分页 Request VO")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class SmsChannelPageReqVO extends PageParam {
+
+ @ApiModelProperty(value = "渠道名", example = "阿里", notes = "模糊匹配")
+ private String name;
+
+ @ApiModelProperty(value = "签名值", example = "源码", notes = "模糊匹配")
+ private String signature;
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/vo/resp/SmsChannelEnumRespVO.java b/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/vo/resp/SmsChannelEnumRespVO.java
new file mode 100644
index 000000000..a3e56e0ca
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/vo/resp/SmsChannelEnumRespVO.java
@@ -0,0 +1,21 @@
+package cn.iocoder.dashboard.modules.msg.controller.sms.vo.resp;
+
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+
+@ApiModel("用户分页 Request VO")
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode
+public class SmsChannelEnumRespVO implements Serializable {
+
+ private String code;
+
+ private String name;
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/vo/resp/SmsChannelPageRespVO.java b/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/vo/resp/SmsChannelPageRespVO.java
new file mode 100644
index 000000000..603f13e07
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/controller/sms/vo/resp/SmsChannelPageRespVO.java
@@ -0,0 +1,43 @@
+package cn.iocoder.dashboard.modules.msg.controller.sms.vo.resp;
+
+import cn.iocoder.dashboard.common.pojo.PageParam;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.util.Date;
+
+import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@ApiModel("用户分页 Request VO")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class SmsChannelPageRespVO extends PageParam {
+
+ @ApiModelProperty(value = "用户账号", example = "yudao", notes = "模糊匹配")
+ private String username;
+
+ @ApiModelProperty(value = "手机号码", example = "yudao", notes = "模糊匹配")
+ private String mobile;
+
+ @ApiModelProperty(value = "展示状态", example = "1", notes = "参见 SysCommonStatusEnum 枚举类")
+ private Integer status;
+
+ @ApiModelProperty(value = "开始时间", example = "2020-10-24")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private Date beginTime;
+
+ @ApiModelProperty(value = "结束时间", example = "2020-10-24")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private Date endTime;
+
+ @ApiModelProperty(value = "部门编号", example = "1024", notes = "同时筛选子部门")
+ private Long deptId;
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/convert/sms/SmsChannelConvert.java b/src/main/java/cn/iocoder/dashboard/modules/msg/convert/sms/SmsChannelConvert.java
new file mode 100644
index 000000000..4728586a3
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/convert/sms/SmsChannelConvert.java
@@ -0,0 +1,34 @@
+package cn.iocoder.dashboard.modules.msg.convert.sms;
+
+import cn.iocoder.dashboard.common.enums.SmsChannelEnum;
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.modules.msg.controller.sms.vo.SmsChannelAllVO;
+import cn.iocoder.dashboard.modules.msg.controller.sms.vo.req.SmsChannelCreateReqVO;
+import cn.iocoder.dashboard.modules.msg.controller.sms.vo.resp.SmsChannelEnumRespVO;
+import cn.iocoder.dashboard.modules.msg.dal.mysql.daoobject.sms.SmsChannelDO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserUpdateReqVO;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+@Mapper
+public interface SmsChannelConvert {
+
+ SmsChannelConvert INSTANCE = Mappers.getMapper(SmsChannelConvert.class);
+
+ @Mapping(source = "records", target = "list")
+ PageResult convertPage(IPage page);
+
+ SmsChannelDO convert(SmsChannelCreateReqVO bean);
+
+ SmsChannelDO convert(SysUserUpdateReqVO bean);
+
+ List convertEnum(List bean);
+
+ List convert(List bean);
+
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/convert/sms/SmsTemplateConvert.java b/src/main/java/cn/iocoder/dashboard/modules/msg/convert/sms/SmsTemplateConvert.java
new file mode 100644
index 000000000..313e06c92
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/convert/sms/SmsTemplateConvert.java
@@ -0,0 +1,26 @@
+package cn.iocoder.dashboard.modules.msg.convert.sms;
+
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.modules.msg.controller.sms.vo.SmsTemplateVO;
+import cn.iocoder.dashboard.modules.msg.dal.mysql.daoobject.sms.SmsChannelDO;
+import cn.iocoder.dashboard.modules.msg.dal.mysql.daoobject.sms.SmsTemplateDO;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+@Mapper
+public interface SmsTemplateConvert {
+
+ SmsTemplateConvert INSTANCE = Mappers.getMapper(SmsTemplateConvert.class);
+
+ @Mapping(source = "records", target = "list")
+ PageResult convertPage(IPage page);
+
+ List convert(List bean);
+
+ SmsTemplateVO convert(SmsTemplateDO bean);
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/dal/mysql/dao/sms/SmsChannelMapper.java b/src/main/java/cn/iocoder/dashboard/modules/msg/dal/mysql/dao/sms/SmsChannelMapper.java
new file mode 100644
index 000000000..f7ede3502
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/dal/mysql/dao/sms/SmsChannelMapper.java
@@ -0,0 +1,31 @@
+package cn.iocoder.dashboard.modules.msg.dal.mysql.dao.sms;
+
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
+import cn.iocoder.dashboard.framework.mybatis.core.util.MyBatisUtils;
+import cn.iocoder.dashboard.modules.msg.controller.sms.vo.req.SmsChannelPageReqVO;
+import cn.iocoder.dashboard.modules.msg.dal.mysql.daoobject.sms.SmsChannelDO;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface SmsChannelMapper extends BaseMapper {
+
+ default IPage selectChannelPage(SmsChannelPageReqVO reqVO) {
+ return selectPage(MyBatisUtils.buildPage(reqVO), new LambdaQueryWrapper()
+ .like(StrUtil.isNotBlank(reqVO.getName()), SmsChannelDO::getName, reqVO.getName())
+ .like(StrUtil.isNotBlank(reqVO.getSignature()), SmsChannelDO::getName, reqVO.getSignature())
+ );
+ }
+
+ default List selectEnabledList() {
+ return selectList(new LambdaQueryWrapper()
+ .eq(SmsChannelDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
+ .orderByAsc(SmsChannelDO::getId)
+ );
+ }
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/dal/mysql/dao/sms/SmsLogMapper.java b/src/main/java/cn/iocoder/dashboard/modules/msg/dal/mysql/dao/sms/SmsLogMapper.java
new file mode 100644
index 000000000..263783b47
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/dal/mysql/dao/sms/SmsLogMapper.java
@@ -0,0 +1,10 @@
+package cn.iocoder.dashboard.modules.msg.dal.mysql.dao.sms;
+
+import cn.iocoder.dashboard.modules.msg.dal.mysql.daoobject.sms.SmsLog;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SmsLogMapper extends BaseMapper {
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/dal/mysql/dao/sms/SmsTemplateMapper.java b/src/main/java/cn/iocoder/dashboard/modules/msg/dal/mysql/dao/sms/SmsTemplateMapper.java
new file mode 100644
index 000000000..f2d2fb21b
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/dal/mysql/dao/sms/SmsTemplateMapper.java
@@ -0,0 +1,39 @@
+package cn.iocoder.dashboard.modules.msg.dal.mysql.dao.sms;
+
+import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
+import cn.iocoder.dashboard.modules.msg.dal.mysql.daoobject.sms.SmsTemplateDO;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface SmsTemplateMapper extends BaseMapper {
+
+ /**
+ * 根据短信渠道id查询短信模板集合
+ *
+ * @param channelId 渠道id
+ * @return 模板集合
+ */
+ default List selectListByChannelId(Long channelId) {
+ return selectList(new LambdaQueryWrapper()
+ .eq(SmsTemplateDO::getChannelId, channelId)
+ .eq(SmsTemplateDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
+ .orderByAsc(SmsTemplateDO::getId)
+ );
+ }
+
+ /**
+ * 查询有效短信模板集合
+ *
+ * @return 有效短信模板集合
+ */
+ default List selectEnabledList() {
+ return selectList(new LambdaQueryWrapper()
+ .eq(SmsTemplateDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
+ .orderByAsc(SmsTemplateDO::getId)
+ );
+ }
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/dal/mysql/daoobject/sms/SmsChannelDO.java b/src/main/java/cn/iocoder/dashboard/modules/msg/dal/mysql/daoobject/sms/SmsChannelDO.java
new file mode 100644
index 000000000..3c0b5f396
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/dal/mysql/daoobject/sms/SmsChannelDO.java
@@ -0,0 +1,65 @@
+package cn.iocoder.dashboard.modules.msg.dal.mysql.daoobject.sms;
+
+import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+/**
+ * 短信渠道
+ *
+ * @author zzf
+ * @since 2021-01-25
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName(value = "sms_channel", autoResultMap = true)
+public class SmsChannelDO extends BaseDO {
+
+ /**
+ * 自增编号
+ */
+ private Long id;
+
+ /**
+ * 编码(来自枚举类 阿里、华为、七牛等)
+ */
+ private String code;
+
+ /**
+ * 渠道账号id
+ */
+ private String apiKey;
+
+ /**
+ * 渠道账号秘钥
+ */
+ private String apiSecret;
+
+ /**
+ * 实际渠道签名唯一标识
+ */
+ private String apiSignatureId;
+
+ /**
+ * 名称
+ */
+ private String name;
+
+ /**
+ * 签名值
+ */
+ private String signature;
+
+ /**
+ * 备注
+ */
+ private String remark;
+
+ /**
+ * 启用状态(0正常 1停用)
+ */
+ private Integer status;
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/dal/mysql/daoobject/sms/SmsLog.java b/src/main/java/cn/iocoder/dashboard/modules/msg/dal/mysql/daoobject/sms/SmsLog.java
new file mode 100644
index 000000000..e5af0471e
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/dal/mysql/daoobject/sms/SmsLog.java
@@ -0,0 +1,71 @@
+package cn.iocoder.dashboard.modules.msg.dal.mysql.daoobject.sms;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 短信日志
+ *
+ * @author zzf
+ * @since 2021-01-25
+ */
+@Data
+@EqualsAndHashCode
+@TableName(value = "sms_log", autoResultMap = true)
+public class SmsLog implements Serializable {
+
+ /**
+ * 自增编号
+ */
+ private Long id;
+
+ /**
+ * 短信渠道编码(来自枚举类)
+ */
+ private String channelCode;
+
+ /**
+ * 实际渠道短信唯一标识
+ */
+ private String apiSmsId;
+
+ /**
+ * 模板id
+ */
+ private Long templateId;
+
+ /**
+ * 手机号
+ */
+ private String phone;
+
+ /**
+ * 内容
+ */
+ private String content;
+
+ /**
+ * 备注
+ */
+ private String remark;
+
+ /**
+ * 发送状态(0发送中 1成功 2失败)
+ */
+ private Integer sendStatus;
+
+ /**
+ * 创建者
+ */
+ private String createBy;
+
+ /**
+ * 创建时间
+ */
+ private Date createTime;
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/dal/mysql/daoobject/sms/SmsTemplateDO.java b/src/main/java/cn/iocoder/dashboard/modules/msg/dal/mysql/daoobject/sms/SmsTemplateDO.java
new file mode 100644
index 000000000..39257a71d
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/dal/mysql/daoobject/sms/SmsTemplateDO.java
@@ -0,0 +1,81 @@
+package cn.iocoder.dashboard.modules.msg.dal.mysql.daoobject.sms;
+
+import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+/**
+ * 短信模板
+ *
+ * @author zzf
+ * @since 2021-01-25
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName(value = "sms_template", autoResultMap = true)
+public class SmsTemplateDO extends BaseDO {
+
+ /**
+ * 自增编号
+ */
+ private Long id;
+
+ /**
+ * 短信渠道编码(来自枚举类)
+ */
+ private String channelCode;
+
+ /**
+ * 短信渠道id (对于前端来说就是绑定一个签名)
+ */
+ private Long channelId;
+
+ /**
+ * 消息类型 [0验证码 1短信通知 2推广短信 3国际/港澳台消息]
+ */
+ private Integer type;
+
+ /**
+ * 业务编码(来自数据字典, 用户自定义业务场景 一个场景可以有多个模板)
+ */
+ private String bizCode;
+
+ /**
+ * 编码
+ */
+ private String code;
+
+ /**
+ * 名称
+ */
+ private String name;
+
+ /**
+ * 实际渠道模板唯一标识
+ */
+ private String apiTemplateId;
+
+ /**
+ * 内容
+ */
+ private String content;
+
+ /**
+ * 参数数组(自动根据内容生成)
+ */
+ private String params;
+
+ /**
+ * 备注
+ */
+ private String remark;
+
+ /**
+ * 启用状态(0正常 1停用)
+ */
+ private Integer status;
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/package-info.java b/src/main/java/cn/iocoder/dashboard/modules/msg/package-info.java
new file mode 100644
index 000000000..3a0e7635c
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * msg 包,专门专门用于发送消息的功能,支撑上层的通用与核心业务。
+ * 例如说:短信、邮件、app通知等等
+ *
+ */
+package cn.iocoder.dashboard.modules.msg;
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/service/sms/SmsChannelService.java b/src/main/java/cn/iocoder/dashboard/modules/msg/service/sms/SmsChannelService.java
new file mode 100644
index 000000000..ed2c047e8
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/service/sms/SmsChannelService.java
@@ -0,0 +1,34 @@
+package cn.iocoder.dashboard.modules.msg.service.sms;
+
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.modules.msg.controller.sms.vo.SmsChannelAllVO;
+import cn.iocoder.dashboard.modules.msg.controller.sms.vo.req.SmsChannelCreateReqVO;
+import cn.iocoder.dashboard.modules.msg.controller.sms.vo.req.SmsChannelPageReqVO;
+import cn.iocoder.dashboard.modules.msg.controller.sms.vo.resp.SmsChannelEnumRespVO;
+import cn.iocoder.dashboard.modules.msg.dal.mysql.daoobject.sms.SmsChannelDO;
+
+import java.util.List;
+
+/**
+ * 短信渠道Service接口
+ *
+ * @author zzf
+ * @date 2021/1/25 9:24
+ */
+public interface SmsChannelService {
+
+ PageResult pageChannels(SmsChannelPageReqVO reqVO);
+
+ Long createChannel(SmsChannelCreateReqVO reqVO);
+
+ List getChannelEnums();
+
+ /**
+ * 查询渠道(包含名下模块)信息集合
+ *
+ * @return 渠道(包含名下模块)信息集合
+ */
+ List listChannelAllEnabledInfo();
+
+ boolean flushChannel();
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/service/sms/SmsLogService.java b/src/main/java/cn/iocoder/dashboard/modules/msg/service/sms/SmsLogService.java
new file mode 100644
index 000000000..70f4d9eeb
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/service/sms/SmsLogService.java
@@ -0,0 +1,10 @@
+package cn.iocoder.dashboard.modules.msg.service.sms;
+
+/**
+ * 短信渠道Service接口
+ *
+ * @author zzf
+ * @date 2021/1/25 9:24
+ */
+public interface SmsLogService {
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/service/sms/SmsTemplateService.java b/src/main/java/cn/iocoder/dashboard/modules/msg/service/sms/SmsTemplateService.java
new file mode 100644
index 000000000..2c1364934
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/service/sms/SmsTemplateService.java
@@ -0,0 +1,10 @@
+package cn.iocoder.dashboard.modules.msg.service.sms;
+
+/**
+ * 短信渠道Service接口
+ *
+ * @author zzf
+ * @date 2021/1/25 9:24
+ */
+public interface SmsTemplateService {
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/service/sms/impl/SmsChannelServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/msg/service/sms/impl/SmsChannelServiceImpl.java
new file mode 100644
index 000000000..e5202f491
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/service/sms/impl/SmsChannelServiceImpl.java
@@ -0,0 +1,88 @@
+package cn.iocoder.dashboard.modules.msg.service.sms.impl;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import cn.iocoder.dashboard.common.enums.SmsChannelEnum;
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.framework.msg.sms.factory.AbstractSmsSenderFactory;
+import cn.iocoder.dashboard.modules.msg.controller.sms.vo.SmsChannelAllVO;
+import cn.iocoder.dashboard.modules.msg.controller.sms.vo.req.SmsChannelCreateReqVO;
+import cn.iocoder.dashboard.modules.msg.controller.sms.vo.req.SmsChannelPageReqVO;
+import cn.iocoder.dashboard.modules.msg.controller.sms.vo.resp.SmsChannelEnumRespVO;
+import cn.iocoder.dashboard.modules.msg.convert.sms.SmsChannelConvert;
+import cn.iocoder.dashboard.modules.msg.convert.sms.SmsTemplateConvert;
+import cn.iocoder.dashboard.modules.msg.dal.mysql.dao.sms.SmsChannelMapper;
+import cn.iocoder.dashboard.modules.msg.dal.mysql.dao.sms.SmsTemplateMapper;
+import cn.iocoder.dashboard.modules.msg.dal.mysql.daoobject.sms.SmsChannelDO;
+import cn.iocoder.dashboard.modules.msg.dal.mysql.daoobject.sms.SmsTemplateDO;
+import cn.iocoder.dashboard.modules.msg.service.sms.SmsChannelService;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 短信渠道Service实现类
+ *
+ * @author zzf
+ * @date 2021/1/25 9:25
+ */
+@Service
+public class SmsChannelServiceImpl implements SmsChannelService {
+
+ @Resource
+ private SmsChannelMapper mapper;
+
+ @Resource
+ private SmsTemplateMapper templateMapper;
+
+ @Override
+ public PageResult pageChannels(SmsChannelPageReqVO reqVO) {
+ return SmsChannelConvert.INSTANCE.convertPage(mapper.selectChannelPage(reqVO));
+ }
+
+ @Override
+ public Long createChannel(SmsChannelCreateReqVO reqVO) {
+ SmsChannelDO channelDO = SmsChannelConvert.INSTANCE.convert(reqVO);
+ mapper.insert(channelDO);
+ return channelDO.getId();
+ }
+
+ @Override
+ public List getChannelEnums() {
+ return SmsChannelConvert.INSTANCE.convertEnum(Arrays.asList(SmsChannelEnum.values()));
+ }
+
+ @Override
+ public List listChannelAllEnabledInfo() {
+ List channelDOList = mapper.selectEnabledList();
+ if (ObjectUtil.isNull(channelDOList)) {
+ return null;
+ }
+ List channelAllVOList = SmsChannelConvert.INSTANCE.convert(channelDOList);
+
+ channelAllVOList.forEach(smsChannelDO -> {
+
+ List templateDOList = templateMapper.selectListByChannelId(smsChannelDO.getId());
+ if (ObjectUtil.isNull(templateDOList)) {
+ templateDOList = new ArrayList<>();
+ }
+ smsChannelDO.setTemplateList(SmsTemplateConvert.INSTANCE.convert(templateDOList));
+ });
+ return channelAllVOList;
+ }
+
+ @Override
+ public boolean flushChannel() {
+ AbstractSmsSenderFactory smsSenderFactory = SpringUtil.getBean(AbstractSmsSenderFactory.class);
+ if (smsSenderFactory == null) {
+ return false;
+ }
+
+ smsSenderFactory.flush(listChannelAllEnabledInfo());
+
+ return true;
+ }
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/service/sms/impl/SmsLogServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/msg/service/sms/impl/SmsLogServiceImpl.java
new file mode 100644
index 000000000..f7e348a4d
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/service/sms/impl/SmsLogServiceImpl.java
@@ -0,0 +1,15 @@
+package cn.iocoder.dashboard.modules.msg.service.sms.impl;
+
+import cn.iocoder.dashboard.modules.msg.service.sms.SmsLogService;
+import org.springframework.stereotype.Service;
+
+/**
+ * 短信日志Service实现类
+ *
+ * @author zzf
+ * @date 2021/1/25 9:25
+ */
+@Service
+public class SmsLogServiceImpl implements SmsLogService {
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/msg/service/sms/impl/SmsTemplateServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/msg/service/sms/impl/SmsTemplateServiceImpl.java
new file mode 100644
index 000000000..caf6d8e1c
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/msg/service/sms/impl/SmsTemplateServiceImpl.java
@@ -0,0 +1,14 @@
+package cn.iocoder.dashboard.modules.msg.service.sms.impl;
+
+import cn.iocoder.dashboard.modules.msg.service.sms.SmsTemplateService;
+import org.springframework.stereotype.Service;
+
+/**
+ * 短信模板Service实现类
+ *
+ * @author zzf
+ * @date 2021/1/25 9:25
+ */
+@Service
+public class SmsTemplateServiceImpl implements SmsTemplateService {
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java b/src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java
index 1bbde5784..5aa257a97 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java
@@ -75,4 +75,11 @@ public interface SysErrorCodeConstants {
// ========== 文件 1002009000 ==========
ErrorCode FILE_PATH_EXISTS = new ErrorCode(1002009001, "文件路径已经存在");
+
+ // ========== 消息 1003001000 ==========
+ ErrorCode SMS_CHANNEL_NOT_FOUND = new ErrorCode(1003001001, "没有短信渠道信息, 请初始化sms_channel表数据。");
+ ErrorCode SMS_TEMPLATE_NOT_FOUND = new ErrorCode(1003001002, "没有短信模板信息, 请初始化sms_template表数据。");
+ ErrorCode SMS_SENDER_NOT_FOUND = new ErrorCode(1003001003, "没有找到对应的短信发送对象,请检查sms_channel表和sms_template表数据");
+ ErrorCode INVALID_CHANNEL_CODE = new ErrorCode(1003001004, "非法的短信渠道code,请检查sms_channel表的code值是否与SmsChannelEnum中的code值一致。");
+
}