diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/SmsPlatform.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/SmsPlatform.java
new file mode 100644
index 0000000000000000000000000000000000000000..597db31228edb7e9b41cc0a65c7197870948e68c
--- /dev/null
+++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/SmsPlatform.java
@@ -0,0 +1,80 @@
+package cn.iocoder.mall.admin.api;
+
+import cn.iocoder.mall.admin.api.bo.sms.SmsSignBO;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 短信平台
+ *
+ * @author Sin
+ * @time 2019/5/16 6:33 PM
+ */
+public interface SmsPlatform {
+
+    @Data
+    @Accessors(chain = true)
+    class Result {
+        /**
+         * 编号
+         */
+        private String id;
+        /**
+         * 审核状态
+         */
+        private Integer applyStatus;
+        /**
+         * 审核内容
+         */
+        private String applyMessage;
+    }
+
+    /**
+     * 签名 - 创建
+     *
+     * @param sign
+     */
+    Result createSign(String sign);
+
+    /**
+     * 签名 - 获取
+     *
+     * @param sign
+     */
+    Result getSign(String sign);
+
+    /**
+     * 签名 - 更新
+     *
+     * @param oldSign
+     * @param sign
+     */
+    Result updateSign(String oldSign, String sign);
+
+    /**
+     * 模板 - 创建
+     *
+     * @param sign 选用的哪个签名
+     * @param template 模板内容
+     * @param tplType 1 为验证码类型,其他为 null
+     */
+    Result createTemplate(String sign, String template, Integer tplType);
+
+    /**
+     * 获取模板信息
+     *
+     * @param tipId
+     */
+    Result getTemplate(String tipId);
+
+    /**
+     * 更新模板内容
+     *
+     * @param tipId 选用的哪个签名
+     * @param template 模板内容
+     * @param tplType 1 为验证码类型,其他为 null
+     */
+    Result updateTemplate(String tipId, String template, Integer tplType);
+}
diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/SmsService.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/SmsService.java
new file mode 100644
index 0000000000000000000000000000000000000000..a3d36139b2722aca610229154a635bf7e5707f7a
--- /dev/null
+++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/SmsService.java
@@ -0,0 +1,60 @@
+package cn.iocoder.mall.admin.api;
+
+import cn.iocoder.mall.admin.api.bo.sms.SmsSignBO;
+import cn.iocoder.mall.admin.api.bo.sms.SmsTemplateBO;
+
+/**
+ * 短信服务
+ *
+ * @author Sin
+ * @time 2019/5/16 9:54 AM
+ */
+public interface SmsService {
+
+    /**
+     * 签名 - 创建
+     *
+     * @param sign
+     */
+    void createSign(String sign);
+
+    /**
+     * 签名 - 获取
+     *
+     * @param sign
+     */
+    SmsSignBO getSign(String sign);
+
+    /**
+     * 签名 - 更新
+     *
+     * @param oldSign
+     * @param sign
+     */
+    void updateSign(String oldSign, String sign);
+
+    /**
+     * 模板 - 创建
+     *
+     * @param smsSignId 选用的哪个签名
+     * @param template 模板内容
+     * @param tplType 1 为验证码类型,其他为 null
+     */
+    void createTemplate(Integer smsSignId, String template, Integer tplType);
+
+    /**
+     * 获取模板信息
+     *
+     * @param id
+     */
+    SmsTemplateBO getTemplate(String id);
+
+    /**
+     * 更新模板内容
+     *
+     * @param id 模板id
+     * @param template 模板内容
+     * @param tplType 1 为验证码类型,其他为 null
+     */
+    void updateTemplate(String id, String template, Integer tplType);
+}
diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/sms/SmsSignBO.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/sms/SmsSignBO.java
new file mode 100644
index 0000000000000000000000000000000000000000..220b01856f7dad76ee6b853d2ffc018d4520b305
--- /dev/null
+++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/sms/SmsSignBO.java
@@ -0,0 +1,40 @@
+package cn.iocoder.mall.admin.api.bo.sms;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * 短信签名
+ *
+ * @author Sin
+ * @time 2019/5/16 6:30 PM
+ */
+@Data
+@Accessors(chain = true)
+public class SmsSignBO {
+
+    /**
+     * 编号
+     */
+    private Integer id;
+    /**
+     * 签名id 这个是第三方的
+     */
+    private Integer signId;
+    /**
+     * 签名名称
+     */
+    private String sign;
+    /**
+     * 审核状态
+     *
+     * - 1、审核中
+     * - 2、审核成功
+     * - 3、审核失败
+     */
+    private Integer applyStatus;
+    /**
+     * 审核信息
+     */
+    private String applyMessage;
+}
diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/sms/SmsTemplateBO.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/sms/SmsTemplateBO.java
new file mode 100644
index 0000000000000000000000000000000000000000..e2094eeeeebd5d0fe160f2ac670adba9f3ccbdf6
--- /dev/null
+++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/sms/SmsTemplateBO.java
@@ -0,0 +1,44 @@
+package cn.iocoder.mall.admin.api.bo.sms;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * 短信 template
+ *
+ * @author Sin
+ * @time 2019/5/16 7:41 PM
+ */
+@Data
+@Accessors(chain = true)
+public class SmsTemplateBO {
+
+    /**
+     * 编号
+     */
+    private Integer id;
+    /**
+     * 模板编号 (第三方的)
+     */
+    private Integer smsSignId;
+    /**
+     * 短信签名 id
+     */
+    private String platformId;
+    /**
+     * 短信模板
+     */
+    private String template;
+    /**
+     * 审核状态
+     *
+     * 1、审核中
+     * 2、审核成功
+     * 3、审核失败
+     */
+    private Integer applyStatus;
+    /**
+     * 审核信息
+     */
+    private String applyMessage;
+}
diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/AdminErrorCodeEnum.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/AdminErrorCodeEnum.java
index 61f4dfd1d820a99d24c054aca78f4c9489d130a3..3764d63a7be4640006f2b0892357028ec74e21f7 100644
--- a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/AdminErrorCodeEnum.java
+++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/AdminErrorCodeEnum.java
@@ -46,6 +46,15 @@ public enum AdminErrorCodeEnum {
     DATA_DICT_EXISTS(1002005000, "该数据字典已经存在"),
     DATA_DICT_NOT_EXISTS(1002005001, "该数据字典不存在"),
 
+    // ========== 短信模板 1002006000 ==========
+    SMS_SIGN_ADD_FAIL(1002006000, "短信签名添加失败"),
+    SMS_SIGN_NOT_EXISTENT(1002006001, "短信签名不存在"),
+    SMS_SIGN_IS_EXISTENT(1002006002, "短信签名已存在"),
+    SMS_SIGN_UPDATE_FAIL(1002006003, "短信更新失败"),
+
+    SMS_TEMPLATE_ADD_FAIL(1002006020, "短信签名不存在"),
+    SMS_TEMPLATE_NOT_EXISTENT(1002006021, "短信签名不存在"),
+    SMS_TEMPLATE_IS_EXISTENT(1002006022, "短信签名不存在"),
     ;
 
     private final int code;
diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/SmsApplyStatusEnum.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/SmsApplyStatusEnum.java
new file mode 100644
index 0000000000000000000000000000000000000000..eaf6856872b6194173804aa865204b08aac136f7
--- /dev/null
+++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/SmsApplyStatusEnum.java
@@ -0,0 +1,31 @@
+package cn.iocoder.mall.admin.api.constant;
+
+/**
+ * 短信审核状态
+ *
+ * @author Sin
+ * @time 2019/5/16 12:48 PM
+ */
+public enum SmsApplyStatusEnum {
+
+    CHECKING(1, "审核中"),
+    SUCCESS(2, "审核成功"),
+    FAIL(3, "审核失败"),
+    ;
+
+    private final int code;
+    private final String message;
+
+    SmsApplyStatusEnum(int code, String message) {
+        this.code = code;
+        this.message = message;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+}
diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/exception/SmsFailException.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/exception/SmsFailException.java
new file mode 100644
index 0000000000000000000000000000000000000000..631d8513fb8aafc11b63c11e0a7a6667b3a1d026
--- /dev/null
+++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/exception/SmsFailException.java
@@ -0,0 +1,14 @@
+package cn.iocoder.mall.admin.api.exception;
+
+import cn.iocoder.common.framework.exception.ServiceException;
+
+/**
+ * @author Sin
+ * @time 2019/5/16 11:17 AM
+ */
+public class SmsFailException extends ServiceException {
+
+    public SmsFailException(Integer code, String message) {
+        super(code, message);
+    }
+}
diff --git a/system/system-service-impl/pom.xml b/system/system-service-impl/pom.xml
index 3360a821c436aae3b564bf9e99075b953fbad1ee..344d934438123d4d56248121aaa15a9670234912 100644
--- a/system/system-service-impl/pom.xml
+++ b/system/system-service-impl/pom.xml
@@ -67,7 +67,17 @@
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.yunpian.sdk</groupId>
+            <artifactId>yunpian-java-sdk</artifactId>
+            <version>1.2.7</version>
+        </dependency>
 
+        <!--  test  -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/convert/SmsSignConvert.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/convert/SmsSignConvert.java
new file mode 100644
index 0000000000000000000000000000000000000000..0173e30c702981d5fab082f137a53930edec9403
--- /dev/null
+++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/convert/SmsSignConvert.java
@@ -0,0 +1,22 @@
+package cn.iocoder.mall.admin.convert;
+
+import cn.iocoder.mall.admin.api.bo.sms.SmsSignBO;
+import cn.iocoder.mall.admin.dataobject.SmsSignDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mappings;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * 短信 签名
+ *
+ * @author Sin
+ * @time 2019/5/16 6:31 PM
+ */
+@Mapper
+public interface SmsSignConvert {
+
+    SmsSignConvert INSTANCE = Mappers.getMapper(SmsSignConvert.class);
+
+    @Mappings({})
+    SmsSignBO convert(SmsSignDO smsSignDO);
+}
diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/convert/SmsTemplateConvert.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/convert/SmsTemplateConvert.java
new file mode 100644
index 0000000000000000000000000000000000000000..d76c694b0c61e9a8303c20e13b4e07d2da4fcb2c
--- /dev/null
+++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/convert/SmsTemplateConvert.java
@@ -0,0 +1,22 @@
+package cn.iocoder.mall.admin.convert;
+
+import cn.iocoder.mall.admin.api.bo.sms.SmsTemplateBO;
+import cn.iocoder.mall.admin.dataobject.SmsTemplateDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mappings;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * 短信 template
+ *
+ * @author Sin
+ * @time 2019/5/16 7:43 PM
+ */
+@Mapper
+public interface SmsTemplateConvert {
+
+    SmsTemplateConvert INSTANCE = Mappers.getMapper(SmsTemplateConvert.class);
+
+    @Mappings({})
+    SmsTemplateBO convert(SmsTemplateDO smsTemplateDO);
+}
diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/SmsSignMapper.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/SmsSignMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..351e0326ce9dfaac941cf2c65b039dd3ffb5620c
--- /dev/null
+++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/SmsSignMapper.java
@@ -0,0 +1,15 @@
+package cn.iocoder.mall.admin.dao;
+
+import cn.iocoder.mall.admin.dataobject.SmsSignDO;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.springframework.stereotype.Repository;
+
+/**
+ * 短信
+ *
+ * @author Sin
+ * @time 2019/5/16 6:18 PM
+ */
+@Repository
+public interface SmsSignMapper extends BaseMapper<SmsSignDO> {
+}
diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/SmsTemplateMapper.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/SmsTemplateMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..91fa040572460f66d94929a940566da70c699d82
--- /dev/null
+++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/SmsTemplateMapper.java
@@ -0,0 +1,16 @@
+package cn.iocoder.mall.admin.dao;
+
+import cn.iocoder.common.framework.dataobject.BaseDO;
+import cn.iocoder.mall.admin.dataobject.SmsTemplateDO;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.springframework.stereotype.Repository;
+
+/**
+ * 短信 template
+ *
+ * @author Sin
+ * @time 2019/5/16 6:18 PM
+ */
+@Repository
+public interface SmsTemplateMapper extends BaseMapper<SmsTemplateDO> {
+}
diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/SmsSignDO.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/SmsSignDO.java
new file mode 100644
index 0000000000000000000000000000000000000000..f25afcd1f7acd87cd9c4ab2447adeb2c0c5c8102
--- /dev/null
+++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/SmsSignDO.java
@@ -0,0 +1,45 @@
+package cn.iocoder.mall.admin.dataobject;
+
+import cn.iocoder.common.framework.dataobject.DeletableDO;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * 短信签名
+ *
+ *  签名是短信发送前缀 如:【阿里云】、【小红书】
+ *
+ * @author Sin
+ * @time 2019/5/16 12:28 PM
+ */
+@Data
+@Accessors(chain = true)
+public class SmsSignDO extends DeletableDO {
+
+    /**
+     * 编号
+     */
+    @TableId("id")
+    private Integer id;
+    /**
+     * 签名id 这个是第三方的
+     */
+    private String platformId;
+    /**
+     * 签名名称
+     */
+    private String sign;
+    /**
+     * 审核状态
+     *
+     * - 1、审核中
+     * - 2、审核成功
+     * - 3、审核失败
+     */
+    private Integer applyStatus;
+    /**
+     * 审核信息
+     */
+    private String applyMessage;
+}
diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/SmsTemplateDO.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/SmsTemplateDO.java
new file mode 100644
index 0000000000000000000000000000000000000000..00e7f18f12821816b5f677c6137bbbe246b14754
--- /dev/null
+++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/SmsTemplateDO.java
@@ -0,0 +1,45 @@
+package cn.iocoder.mall.admin.dataobject;
+
+import cn.iocoder.common.framework.dataobject.DeletableDO;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * 短信 模板
+ *
+ * @author Sin
+ * @time 2019/5/16 12:31 PM
+ */
+@Data
+@Accessors(chain = true)
+public class SmsTemplateDO extends DeletableDO {
+
+    /**
+     * 编号
+     */
+    private Integer id;
+    /**
+     * 模板编号 (第三方的)
+     */
+    private Integer smsSignId;
+    /**
+     * 短信签名 id
+     */
+    private String platformId;
+    /**
+     * 短信模板
+     */
+    private String template;
+    /**
+     * 审核状态
+     *
+     * 1、审核中
+     * 2、审核成功
+     * 3、审核失败
+     */
+    private Integer applyStatus;
+    /**
+     * 审核信息
+     */
+    private String applyMessage;
+}
diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/SmsServiceImpl.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/SmsServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..c65184024581b6964e441b42a5f60d81ec5285e4
--- /dev/null
+++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/SmsServiceImpl.java
@@ -0,0 +1,172 @@
+package cn.iocoder.mall.admin.service;
+
+import cn.iocoder.common.framework.constant.DeletedStatusEnum;
+import cn.iocoder.mall.admin.api.SmsPlatform;
+import cn.iocoder.mall.admin.api.SmsService;
+import cn.iocoder.mall.admin.api.bo.sms.SmsSignBO;
+import cn.iocoder.mall.admin.api.bo.sms.SmsTemplateBO;
+import cn.iocoder.mall.admin.api.constant.AdminErrorCodeEnum;
+import cn.iocoder.mall.admin.api.exception.SmsFailException;
+import cn.iocoder.mall.admin.convert.SmsSignConvert;
+import cn.iocoder.mall.admin.convert.SmsTemplateConvert;
+import cn.iocoder.mall.admin.dao.SmsSignMapper;
+import cn.iocoder.mall.admin.dao.SmsTemplateMapper;
+import cn.iocoder.mall.admin.dataobject.SmsSignDO;
+import cn.iocoder.mall.admin.dataobject.SmsTemplateDO;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Date;
+
+/**
+ * 短信
+ *
+ * @author Sin
+ * @time 2019/5/16 10:30 AM
+ */
+@Service
+@org.apache.dubbo.config.annotation.Service(validation = "true", version = "${dubbo.provider.SmsService.version}")
+public class SmsServiceImpl implements SmsService {
+
+    @Autowired
+    private SmsSignMapper smsSignMapper;
+    @Autowired
+    private SmsTemplateMapper smsTemplateMapper;
+
+    @Autowired
+    @Qualifier("smsYunPianPlatform")
+    private SmsPlatform smsPlatform;
+
+    @Override
+    @Transactional
+    public void createSign(String sign) {
+
+        // 避免重复
+        SmsSignDO smsSignDO = smsSignMapper.selectOne(
+                new QueryWrapper<SmsSignDO>().eq("sign", sign));
+
+        if (smsSignDO != null) {
+            throw new SmsFailException(AdminErrorCodeEnum.SMS_SIGN_IS_EXISTENT.getCode(),
+                    AdminErrorCodeEnum.SMS_SIGN_IS_EXISTENT.getMessage());
+        }
+
+        // 创建平台 sign
+        SmsPlatform.Result result = smsPlatform.createSign(sign);
+
+        // 保存数据库
+        smsSignMapper.insert(
+                (SmsSignDO) new SmsSignDO()
+                        .setSign(sign)
+                        .setPlatformId(result.getId())
+                        .setApplyStatus(result.getApplyStatus())
+                        .setDeleted(DeletedStatusEnum.DELETED_NO.getValue())
+                        .setUpdateTime(new Date())
+        );
+    }
+
+    @Override
+    public SmsSignBO getSign(String sign) {
+        SmsSignDO smsSignDO = smsSignMapper.selectOne(
+                new QueryWrapper<SmsSignDO>().eq("sign", sign));
+
+        if (smsSignDO == null) {
+            throw new SmsFailException(AdminErrorCodeEnum.SMS_SIGN_NOT_EXISTENT.getCode(),
+                    AdminErrorCodeEnum.SMS_SIGN_NOT_EXISTENT.getMessage());
+        }
+
+        return SmsSignConvert.INSTANCE.convert(smsSignDO);
+    }
+
+    @Override
+    @Transactional
+    public void updateSign(String oldSign, String sign) {
+        // 避免重复
+        SmsSignDO smsSignDO = smsSignMapper.selectOne(
+                new QueryWrapper<SmsSignDO>().eq("sign", oldSign));
+
+        if (smsSignDO == null) {
+            throw new SmsFailException(AdminErrorCodeEnum.SMS_SIGN_NOT_EXISTENT.getCode(),
+                    AdminErrorCodeEnum.SMS_SIGN_NOT_EXISTENT.getMessage());
+        }
+
+        // 更新平台
+        SmsPlatform.Result result = smsPlatform.updateSign(oldSign, sign);
+
+        // æ›´æ–°
+        smsSignMapper.updateById(
+                (SmsSignDO) new SmsSignDO()
+                        .setId(smsSignDO.getId())
+                        .setPlatformId(result.getId())
+                        .setSign(sign)
+                        .setApplyStatus(result.getApplyStatus())
+                        .setUpdateTime(new Date())
+        );
+    }
+
+    @Override
+    public void createTemplate(Integer smsSignId, String template, Integer tplType) {
+
+        SmsSignDO smsSignDO = smsSignMapper.selectOne(
+                new QueryWrapper<SmsSignDO>().eq("id", smsSignId));
+
+        if (smsSignDO == null) {
+            throw new SmsFailException(AdminErrorCodeEnum.SMS_SIGN_NOT_EXISTENT.getCode(),
+                    AdminErrorCodeEnum.SMS_SIGN_NOT_EXISTENT.getMessage());
+        }
+
+        // 调用平台
+        SmsPlatform.Result result = smsPlatform
+                .createTemplate(smsSignDO.getSign(), template, tplType);
+
+        // 保存数据库
+        smsTemplateMapper.insert(
+                (SmsTemplateDO) new SmsTemplateDO()
+                        .setId(null)
+                        .setSmsSignId(smsSignId)
+                        .setPlatformId(result.getId())
+                        .setTemplate(template)
+                        .setApplyStatus(result.getApplyStatus())
+                        .setApplyMessage(result.getApplyMessage())
+                        .setDeleted(DeletedStatusEnum.DELETED_NO.getValue())
+                        .setCreateTime(new Date())
+        );
+    }
+
+    @Override
+    public SmsTemplateBO getTemplate(String id) {
+        SmsTemplateDO smsTemplateDO = smsTemplateMapper.selectOne(
+                new QueryWrapper<SmsTemplateDO>().eq("id", id));
+
+        if (smsTemplateDO == null) {
+            throw new SmsFailException(AdminErrorCodeEnum.SMS_TEMPLATE_NOT_EXISTENT.getCode(),
+                    AdminErrorCodeEnum.SMS_TEMPLATE_NOT_EXISTENT.getMessage());
+        }
+
+        return SmsTemplateConvert.INSTANCE.convert(smsTemplateDO);
+    }
+
+    @Override
+    public void updateTemplate(String id, String template, Integer tplType) {
+        SmsTemplateDO smsTemplateDO = smsTemplateMapper.selectOne(
+                new QueryWrapper<SmsTemplateDO>().eq("id", id));
+
+        if (smsTemplateDO == null) {
+            throw new SmsFailException(AdminErrorCodeEnum.SMS_TEMPLATE_NOT_EXISTENT.getCode(),
+                    AdminErrorCodeEnum.SMS_TEMPLATE_NOT_EXISTENT.getMessage());
+        }
+
+        SmsPlatform.Result result = smsPlatform.updateTemplate(
+                smsTemplateDO.getPlatformId(), template, tplType);
+
+        smsTemplateMapper.update(
+                (SmsTemplateDO) new SmsTemplateDO()
+                        .setApplyStatus(result.getApplyStatus())
+                        .setApplyMessage(result.getApplyMessage())
+                        .setUpdateTime(new Date()),
+                new QueryWrapper<SmsTemplateDO>().eq("id", id)
+        );
+    }
+}
diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/SmsYunPianPlatform.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/SmsYunPianPlatform.java
new file mode 100644
index 0000000000000000000000000000000000000000..8d852a9d3502a9c6be465a8a7956da311f40c954
--- /dev/null
+++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/SmsYunPianPlatform.java
@@ -0,0 +1,272 @@
+package cn.iocoder.mall.admin.service;
+
+import cn.iocoder.mall.admin.api.SmsPlatform;
+import cn.iocoder.mall.admin.api.constant.AdminErrorCodeEnum;
+import cn.iocoder.mall.admin.api.constant.SmsApplyStatusEnum;
+import cn.iocoder.mall.admin.api.exception.SmsFailException;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.http.HttpEntity;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 云片 短信平台
+ *
+ * @author Sin
+ * @time 2019/5/16 6:34 PM
+ */
+@Service
+public class SmsYunPianPlatform implements SmsPlatform {
+
+    protected static final Logger LOGGER = LoggerFactory.getLogger(SmsPlatform.class);
+
+    private static final int SUCCESS_CODE = 0;
+
+    //查账户信息的http地址
+    private static final String URI_GET_USER_INFO =
+            "https://sms.yunpian.com/v2/user/get.json";
+
+    //智能匹配模板发送接口的http地址
+    private static final String URI_SEND_SMS =
+            "https://sms.yunpian.com/v2/sms/single_send.json";
+
+    //模板发送接口的http地址
+    private static final String URI_TPL_SEND_SMS =
+            "https://sms.yunpian.com/v2/sms/tpl_single_send.json";
+
+    //发送语音验证码接口的http地址
+    private static final String URI_SEND_VOICE =
+            "https://voice.yunpian.com/v2/voice/send.json";
+
+    //绑定主叫、被叫关系的接口http地址
+    private static final String URI_SEND_BIND =
+            "https://call.yunpian.com/v2/call/bind.json";
+
+    //解绑主叫、被叫关系的接口http地址
+    private static final String URI_SEND_UNBIND =
+            "https://call.yunpian.com/v2/call/unbind.json";
+
+    /**
+     * 签名 - 添加
+     */
+    private static final String URL_SIGN_ADD = "https://sms.yunpian.com/v2/sign/add.json";
+    /**
+     * 签名 - 获取
+     */
+    private static final String URL_SIGN_GET = "https://sms.yunpian.com/v2/sign/get.json";
+    /**
+     * 签名 - 更新
+     */
+    private static final String URL_SIGN_UPDATE = "https://sms.yunpian.com/v2/sign/update.json";
+    /**
+     * 模板 - 添加
+     */
+    private static final String URL_TEMPLATE_ADD = "https://sms.yunpian.com/v2/tpl/add.json";
+    /**
+     * 模板 - 获取
+     */
+    private static final String URL_TEMPLATE_GET = "https://sms.yunpian.com/v2/tpl/get.json";
+    /**
+     * 模板 - 更新
+     */
+    private static final String URL_TEMPLATE_UPDATE = "https://sms.yunpian.com/v2/tpl/update.json";
+
+    //编码格式。发送编码格式统一用UTF-8
+    private static String ENCODING = "UTF-8";
+
+    @Value("${sms.apiKey}")
+    private String apiKey;
+
+    @Override
+    public Result createSign(String sign) {
+        // 调用 短信平台
+        Map<String, String> params = new LinkedHashMap<>();
+        params.put("apikey", apiKey);
+        params.put("sign", sign);
+        params.put("notify", "true");
+        String result = post(URL_SIGN_ADD, params);
+        JSONObject jsonObject = JSON.parseObject(result);
+        if (!(jsonObject.getInteger("code") == SUCCESS_CODE)) {
+            throw new SmsFailException(AdminErrorCodeEnum.SMS_SIGN_ADD_FAIL.getCode(),
+                    AdminErrorCodeEnum.SMS_SIGN_ADD_FAIL.getMessage());
+        }
+
+        JSONObject signJSONObject = (JSONObject) jsonObject.get("sign");
+        Integer applyState = smsStatusMapping(signJSONObject.getString("apply_state"));
+        return new Result().setId(null).setApplyStatus(applyState).setApplyMessage(null);
+    }
+
+    @Override
+    public Result getSign(String sign) {
+        Map<String, String> params = new LinkedHashMap<>();
+        params.put("apikey", apiKey);
+        params.put("sign", sign);
+        params.put("page_num", "1");
+        params.put("page_size", "20");
+        String result = post(URL_SIGN_GET, params);
+        JSONObject jsonObject = JSON.parseObject(result);
+
+        if (!(jsonObject.getInteger("code") == SUCCESS_CODE)) {
+            throw new SmsFailException(AdminErrorCodeEnum.SMS_SIGN_ADD_FAIL.getCode(),
+                    AdminErrorCodeEnum.SMS_SIGN_ADD_FAIL.getMessage());
+        }
+
+        JSONArray jsonArray = jsonObject.getJSONArray("sign");
+        if (jsonArray.size() <= 0) {
+            throw new SmsFailException(AdminErrorCodeEnum.SMS_SIGN_NOT_EXISTENT.getCode(),
+                    AdminErrorCodeEnum.SMS_SIGN_NOT_EXISTENT.getMessage());
+        }
+
+        JSONObject signJSONObject = (JSONObject) jsonArray.get(0);
+        String checkStatus = signJSONObject.getString("check_status");
+        String applyMessage = signJSONObject.getString("remark");
+        Integer applyStatus = smsStatusMapping(checkStatus);
+        return new Result().setId(null).setApplyStatus(applyStatus).setApplyMessage(applyMessage);
+    }
+
+    @Override
+    public Result updateSign(String oldSign, String sign) {
+        Map<String, String> params = new LinkedHashMap<>();
+        params.put("apikey", apiKey);
+        params.put("old_sign", oldSign);
+        params.put("sign", sign);
+        String result = post(URL_SIGN_UPDATE, params);
+        JSONObject jsonObject = JSON.parseObject(result);
+
+        if (!(jsonObject.getInteger("code") == SUCCESS_CODE)) {
+            throw new SmsFailException(AdminErrorCodeEnum.SMS_SIGN_UPDATE_FAIL.getCode(),
+                    AdminErrorCodeEnum.SMS_SIGN_UPDATE_FAIL.getMessage());
+        }
+
+        JSONObject signJSONObject = (JSONObject) jsonObject.get("sign");
+        Integer applyState = smsStatusMapping(signJSONObject.getString("apply_state"));
+        return new Result().setId(null).setApplyStatus(applyState).setApplyMessage(null);
+    }
+
+    @Override
+    public Result createTemplate(String sign, String template, Integer tplType) {
+        Map<String, String> params = new LinkedHashMap<>();
+        params.put("apikey", apiKey);
+        params.put("tpl_content", sign + template);
+        if (tplType != null) {
+            params.put("tplType", String.valueOf(tplType));
+        }
+        String result = post(URL_TEMPLATE_ADD, params);
+        JSONObject jsonObject = JSON.parseObject(result);
+        String tipId = jsonObject.getString("tpl_id");
+        String checkStatus = jsonObject.getString("check_status");
+        String reason = jsonObject.getString("reason");
+        Integer applyStatus = smsStatusMapping(checkStatus);
+        return new Result().setId(tipId).setApplyStatus(applyStatus).setApplyMessage(reason);
+    }
+
+    @Override
+    public Result getTemplate(String tipId) {
+        Map<String, String> params = new LinkedHashMap<>();
+        params.put("apikey", apiKey);
+        params.put("tipId", tipId);
+        String result = post(URL_TEMPLATE_GET, params);
+        JSONObject jsonObject = JSON.parseObject(result);
+
+        String checkStatus = jsonObject.getString("check_status");
+        Integer applyStatus = smsStatusMapping(checkStatus);
+        String reason = jsonObject.getString("reason");
+        return new Result().setId(tipId).setApplyStatus(applyStatus).setApplyMessage(reason);
+    }
+
+    @Override
+    public Result updateTemplate(String tipId, String template, Integer tplType) {
+        Map<String, String> params = new LinkedHashMap<>();
+        params.put("apikey", apiKey);
+        params.put("tipId", tipId);
+        params.put("template", template);
+        String result = post(URL_TEMPLATE_UPDATE, params);
+        JSONObject jsonObject = JSON.parseObject(result);
+
+        String checkStatus = jsonObject.getString("check_status");
+        Integer applyStatus = smsStatusMapping(checkStatus);
+        String reason = jsonObject.getString("reason");
+        return new Result().setId(tipId).setApplyStatus(applyStatus).setApplyMessage(reason);
+    }
+
+    /**
+     * 短信 status 和 云片状态 映射关系
+     *
+     * @param checkStatus
+     * @return
+     */
+    private Integer smsStatusMapping(String checkStatus) {
+        Integer applyStatus;
+        switch (checkStatus) {
+            case "SUCCESS":
+                applyStatus = SmsApplyStatusEnum.SUCCESS.getCode();
+                break;
+            case "FAIL":
+                applyStatus = SmsApplyStatusEnum.FAIL.getCode();
+                break;
+            default:
+                applyStatus = SmsApplyStatusEnum.CHECKING.getCode();
+                break;
+        }
+        return applyStatus;
+    }
+
+    /**
+     * 基于HttpClient 4.3的通用POST方法
+     *
+     * @param url       提交的URL
+     * @param paramsMap 提交<参数,值>Map
+     * @return 提交响应
+     */
+
+    public static String post(String url, Map<String, String> paramsMap) {
+        CloseableHttpClient client = HttpClients.createDefault();
+        String responseText = "";
+        CloseableHttpResponse response = null;
+        try {
+            HttpPost method = new HttpPost(url);
+            if (paramsMap != null) {
+                List<NameValuePair> paramList = new ArrayList<>();
+                for (Map.Entry<String, String> param : paramsMap.entrySet()) {
+                    NameValuePair pair = new BasicNameValuePair(param.getKey(),
+                            param.getValue());
+                    paramList.add(pair);
+                }
+                method.setEntity(new UrlEncodedFormEntity(paramList, ENCODING));
+            }
+            response = client.execute(method);
+            HttpEntity entity = response.getEntity();
+            if (entity != null) {
+                responseText = EntityUtils.toString(entity, ENCODING);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                response.close();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        LOGGER.debug("云片短信平台 res: {}", responseText);
+        return responseText;
+    }
+}
diff --git a/system/system-service-impl/src/main/resources/config/application.yaml b/system/system-service-impl/src/main/resources/config/application.yaml
index 2030b15735d82a7bfe4ed858a4516453b6465acd..7b8f80fb4ea0987a176b660cbc7520cef7e8e1f3 100644
--- a/system/system-service-impl/src/main/resources/config/application.yaml
+++ b/system/system-service-impl/src/main/resources/config/application.yaml
@@ -24,6 +24,10 @@ mybatis-plus:
   mapper-locations: classpath*:mapper/*.xml
   type-aliases-package: cn.iocoder.mall.admin.dataobject
 
+# sms
+sms:
+  apiKey: d4705399e71e822fe3a90f801ed95bd9
+
 # dubbo
 dubbo:
   application:
@@ -49,6 +53,8 @@ dubbo:
       version: 1.0.0
     RoleService:
       version: 1.0.0
+    SmsService:
+      version: 1.0.0
 
 # logging
 logging:
diff --git a/system/system-service-impl/src/test/java/cn/iocoder/mall/admin/SystemApplicationTest.java b/system/system-service-impl/src/test/java/cn/iocoder/mall/admin/SystemApplicationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d0acf0f3e42d86a4c97f28d9d7d87a6ab1c0b36a
--- /dev/null
+++ b/system/system-service-impl/src/test/java/cn/iocoder/mall/admin/SystemApplicationTest.java
@@ -0,0 +1,20 @@
+package cn.iocoder.mall.admin;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.scheduling.annotation.EnableAsync;
+
+/**
+ * 短信 application (test)
+ *
+ * @author Sin
+ * @time 2019/5/16 10:53 AM
+ */
+@SpringBootApplication(scanBasePackages = {"cn.iocoder.mall.admin"})
+@EnableAsync(proxyTargetClass = true)
+public class SystemApplicationTest {
+
+    public static void main(String[] args) {
+        SpringApplication.run(SystemApplicationTest.class);
+    }
+}
diff --git a/system/system-service-impl/src/test/java/cn/iocoder/mall/admin/package-info.java b/system/system-service-impl/src/test/java/cn/iocoder/mall/admin/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a1304b65e34b2fb8d80188eaafeff506ac03e0e
--- /dev/null
+++ b/system/system-service-impl/src/test/java/cn/iocoder/mall/admin/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * @author Sin
+ * @time 2019/5/16 10:52 AM
+ */
+package cn.iocoder.mall.admin;
\ No newline at end of file
diff --git a/system/system-service-impl/src/test/java/cn/iocoder/mall/admin/service/SmsServiceImplTest.java b/system/system-service-impl/src/test/java/cn/iocoder/mall/admin/service/SmsServiceImplTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..07c1e6cc63cd070633fe0b1590bc7f61cc1ea294
--- /dev/null
+++ b/system/system-service-impl/src/test/java/cn/iocoder/mall/admin/service/SmsServiceImplTest.java
@@ -0,0 +1,29 @@
+package cn.iocoder.mall.admin.service;
+
+import cn.iocoder.mall.admin.SystemApplicationTest;
+import cn.iocoder.mall.admin.service.SmsServiceImpl;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/**
+ * 短信 test
+ *
+ * @author Sin
+ * @time 2019/5/16 10:52 AM
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = SystemApplicationTest.class)
+public class SmsServiceImplTest {
+
+    @Autowired
+    private SmsServiceImpl smsService;
+
+    @Test
+    public void createSignTest() {
+//        smsService.createSign("测试签名1");
+        smsService.getSign("测试签名1");
+    }
+}