Commit e62495f65076dde537b559b6b39a1fc9eaa0eedd

Authored by YunaiV
2 parents deded703 860d0d01

Merge branch 'master' of https://gitee.com/zhijiantianya/ruoyi-vue-pro

Showing 141 changed files with 8690 additions and 185 deletions

Too many changes.

To preserve performance only 100 of 141 files are displayed.

@@ -46,6 +46,7 @@ @@ -46,6 +46,7 @@
46 <easyexcel.verion>2.2.7</easyexcel.verion> 46 <easyexcel.verion>2.2.7</easyexcel.verion>
47 <velocity.version>2.2</velocity.version> 47 <velocity.version>2.2</velocity.version>
48 <screw.version>1.0.5</screw.version> 48 <screw.version>1.0.5</screw.version>
  49 + <!-- 三方云服务相关 -->
49 </properties> 50 </properties>
50 51
51 <!-- 依赖声明 --> 52 <!-- 依赖声明 -->
@@ -271,6 +272,27 @@ @@ -271,6 +272,27 @@
271 <version>${screw.version}</version> 272 <version>${screw.version}</version>
272 </dependency> 273 </dependency>
273 274
  275 + <!-- 三方云服务相关 -->
  276 +
  277 + <!-- SMS SDK begin -->
  278 + <dependency>
  279 + <groupId>com.yunpian.sdk</groupId>
  280 + <artifactId>yunpian-java-sdk</artifactId>
  281 + <version>1.2.7</version>
  282 + </dependency>
  283 +
  284 + <dependency>
  285 + <groupId>com.aliyun</groupId>
  286 + <artifactId>aliyun-java-sdk-core</artifactId>
  287 + <version>4.5.18</version>
  288 + </dependency>
  289 + <dependency>
  290 + <groupId>com.aliyun</groupId>
  291 + <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
  292 + <version>2.1.0</version>
  293 + </dependency>
  294 + <!-- SMS SDK end -->
  295 +
274 </dependencies> 296 </dependencies>
275 297
276 298
ruoyi-ui/.env.demo1024 0 → 100644
  1 +NODE_ENV = production
  2 +
  3 +# 测试环境配置
  4 +ENV = 'staging'
  5 +
  6 +# 芋道管理系统/测试环境
  7 +VUE_APP_BASE_API = 'http://127.0.0.1:48080'
ruoyi-ui/package.json
@@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
8 "dev": "vue-cli-service serve", 8 "dev": "vue-cli-service serve",
9 "build:prod": "vue-cli-service build", 9 "build:prod": "vue-cli-service build",
10 "build:stage": "vue-cli-service build --mode staging", 10 "build:stage": "vue-cli-service build --mode staging",
  11 + "build:demo1024": "vue-cli-service build --mode demo1024",
11 "preview": "node build/index.js --preview", 12 "preview": "node build/index.js --preview",
12 "lint": "eslint --ext .js,.vue src" 13 "lint": "eslint --ext .js,.vue src"
13 }, 14 },
ruoyi-ui/src/api/system/sms/smsChannel.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 创建短信渠道
  4 +export function createSmsChannel(data) {
  5 + return request({
  6 + url: '/system/sms-channel/create',
  7 + method: 'post',
  8 + data: data
  9 + })
  10 +}
  11 +
  12 +// 更新短信渠道
  13 +export function updateSmsChannel(data) {
  14 + return request({
  15 + url: '/system/sms-channel/update',
  16 + method: 'put',
  17 + data: data
  18 + })
  19 +}
  20 +
  21 +// 删除短信渠道
  22 +export function deleteSmsChannel(id) {
  23 + return request({
  24 + url: '/system/sms-channel/delete?id=' + id,
  25 + method: 'delete'
  26 + })
  27 +}
  28 +
  29 +// 获得短信渠道
  30 +export function getSmsChannel(id) {
  31 + return request({
  32 + url: '/system/sms-channel/get?id=' + id,
  33 + method: 'get'
  34 + })
  35 +}
  36 +
  37 +// 获得短信渠道分页
  38 +export function getSmsChannelPage(query) {
  39 + return request({
  40 + url: '/system/sms-channel/page',
  41 + method: 'get',
  42 + params: query
  43 + })
  44 +}
  45 +
  46 +// 获得短信渠道精简列表
  47 +export function getSimpleSmsChannels() {
  48 + return request({
  49 + url: '/system/sms-channel/list-all-simple',
  50 + method: 'get',
  51 + })
  52 +}
ruoyi-ui/src/api/system/sms/smsLog.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 获得短信日志分页
  4 +export function getSmsLogPage(query) {
  5 + return request({
  6 + url: '/system/sms-log/page',
  7 + method: 'get',
  8 + params: query
  9 + })
  10 +}
  11 +
  12 +// 导出短信日志 Excel
  13 +export function exportSmsLogExcel(query) {
  14 + return request({
  15 + url: '/system/sms-log/export-excel',
  16 + method: 'get',
  17 + params: query,
  18 + responseType: 'blob'
  19 + })
  20 +}
ruoyi-ui/src/api/system/sms/smsTemplate.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 创建短信模板
  4 +export function createSmsTemplate(data) {
  5 + return request({
  6 + url: '/system/sms-template/create',
  7 + method: 'post',
  8 + data: data
  9 + })
  10 +}
  11 +
  12 +// 更新短信模板
  13 +export function updateSmsTemplate(data) {
  14 + return request({
  15 + url: '/system/sms-template/update',
  16 + method: 'put',
  17 + data: data
  18 + })
  19 +}
  20 +
  21 +// 删除短信模板
  22 +export function deleteSmsTemplate(id) {
  23 + return request({
  24 + url: '/system/sms-template/delete?id=' + id,
  25 + method: 'delete'
  26 + })
  27 +}
  28 +
  29 +// 获得短信模板
  30 +export function getSmsTemplate(id) {
  31 + return request({
  32 + url: '/system/sms-template/get?id=' + id,
  33 + method: 'get'
  34 + })
  35 +}
  36 +
  37 +// 获得短信模板分页
  38 +export function getSmsTemplatePage(query) {
  39 + return request({
  40 + url: '/system/sms-template/page',
  41 + method: 'get',
  42 + params: query
  43 + })
  44 +}
  45 +
  46 +// 创建短信模板
  47 +export function sendSms(data) {
  48 + return request({
  49 + url: '/system/sms-template/send-sms',
  50 + method: 'post',
  51 + data: data
  52 + })
  53 +}
  54 +
  55 +// 导出短信模板 Excel
  56 +export function exportSmsTemplateExcel(query) {
  57 + return request({
  58 + url: '/system/sms-template/export-excel',
  59 + method: 'get',
  60 + params: query,
  61 + responseType: 'blob'
  62 + })
  63 +}
  64 +
ruoyi-ui/src/utils/dict.js
@@ -17,6 +17,10 @@ export const DICT_TYPE = { @@ -17,6 +17,10 @@ export const DICT_TYPE = {
17 SYS_OPERATE_TYPE: 'sys_operate_type', 17 SYS_OPERATE_TYPE: 'sys_operate_type',
18 SYS_LOGIN_RESULT: 'sys_login_result', 18 SYS_LOGIN_RESULT: 'sys_login_result',
19 SYS_CONFIG_TYPE: 'sys_config_type', 19 SYS_CONFIG_TYPE: 'sys_config_type',
  20 + SYS_SMS_CHANNEL_CODE: 'sys_sms_channel_code',
  21 + SYS_SMS_TEMPLATE_TYPE: 'sys_sms_template_type',
  22 + SYS_SMS_SEND_STATUS: 'sys_sms_send_status',
  23 + SYS_SMS_RECEIVE_STATUS: 'sys_sms_receive_status',
20 24
21 INF_REDIS_TIMEOUT_TYPE: 'inf_redis_timeout_type', 25 INF_REDIS_TIMEOUT_TYPE: 'inf_redis_timeout_type',
22 INF_JOB_STATUS: 'inf_job_status', 26 INF_JOB_STATUS: 'inf_job_status',
ruoyi-ui/src/views/system/operatelog/index.vue
@@ -163,7 +163,6 @@ export default { @@ -163,7 +163,6 @@ export default {
163 businessType: undefined, 163 businessType: undefined,
164 status: undefined 164 status: undefined
165 }, 165 },
166 -  
167 }; 166 };
168 }, 167 },
169 created() { 168 created() {
ruoyi-ui/src/views/system/sms/smsChannel.vue 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 +
  4 + <!-- 搜索工作栏 -->
  5 + <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
  6 + <el-form-item label="短信签名" prop="signature">
  7 + <el-input v-model="queryParams.signature" placeholder="请输入短信签名" clearable size="small" @keyup.enter.native="handleQuery"/>
  8 + </el-form-item>
  9 + <el-form-item label="启用状态" prop="status">
  10 + <el-select v-model="queryParams.status" placeholder="请选择启用状态" clearable size="small">
  11 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_COMMON_STATUS)"
  12 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  13 + </el-select>
  14 + </el-form-item>
  15 + <el-form-item label="创建时间">
  16 + <el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd"
  17 + type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
  18 + </el-form-item>
  19 + <el-form-item>
  20 + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
  21 + <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
  22 + </el-form-item>
  23 + </el-form>
  24 +
  25 + <!-- 操作工具栏 -->
  26 + <el-row :gutter="10" class="mb8">
  27 + <el-col :span="1.5">
  28 + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
  29 + v-hasPermi="['system:sms-channel:create']">新增</el-button>
  30 + </el-col>
  31 + <el-col :span="1.5">
  32 + <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
  33 + v-hasPermi="['system:sms-channel:export']">导出</el-button>
  34 + </el-col>
  35 + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  36 + </el-row>
  37 +
  38 + <!-- 列表 -->
  39 + <el-table v-loading="loading" :data="list">
  40 + <el-table-column label="编号" align="center" prop="id" />
  41 + <el-table-column label="短信签名" align="center" prop="signature" />
  42 + <el-table-column label="渠道编码" align="center" prop="code">
  43 + <template slot-scope="scope">
  44 + <span>{{ getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, scope.row.code) }}</span>
  45 + </template>
  46 + </el-table-column>>
  47 + <el-table-column label="启用状态" align="center" prop="status">
  48 + <template slot-scope="scope">
  49 + <span>{{ getDictDataLabel(DICT_TYPE.SYS_COMMON_STATUS, scope.row.status) }}</span>
  50 + </template>
  51 + </el-table-column>>
  52 + <el-table-column label="备注" align="center" prop="remark" />
  53 + <el-table-column label="短信 API 的账号" align="center" prop="apiKey" />
  54 + <el-table-column label="短信 API 的秘钥" align="center" prop="apiSecret" />
  55 + <el-table-column label="短信发送回调 URL" align="center" prop="callbackUrl" />
  56 + <el-table-column label="创建时间" align="center" prop="createTime" width="180">
  57 + <template slot-scope="scope">
  58 + <span>{{ parseTime(scope.row.createTime) }}</span>
  59 + </template>
  60 + </el-table-column>
  61 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  62 + <template slot-scope="scope">
  63 + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
  64 + v-hasPermi="['system:sms-channel:update']">修改</el-button>
  65 + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  66 + v-hasPermi="['system:sms-channel:delete']">删除</el-button>
  67 + </template>
  68 + </el-table-column>
  69 + </el-table>
  70 + <!-- 分页组件 -->
  71 + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
  72 + @pagination="getList"/>
  73 +
  74 + <!-- 对话框(添加 / 修改) -->
  75 + <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
  76 + <el-form ref="form" :model="form" :rules="rules" label-width="80px">
  77 + <el-form-item label="短信签名" prop="signature">
  78 + <el-input v-model="form.signature" placeholder="请输入短信签名" />
  79 + </el-form-item>
  80 + <el-form-item label="渠道编码" prop="code">
  81 + <el-input v-model="form.code" placeholder="请输入渠道编码" />
  82 + </el-form-item>
  83 + <el-form-item label="启用状态">
  84 + <el-radio-group v-model="form.status">
  85 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.SYS_COMMON_STATUS)"
  86 + :key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
  87 + </el-radio-group>
  88 + </el-form-item>
  89 + <el-form-item label="备注" prop="remark">
  90 + <el-input v-model="form.remark" placeholder="请输入备注" />
  91 + </el-form-item>
  92 + <el-form-item label="短信 API 的账号" prop="apiKey">
  93 + <el-input v-model="form.apiKey" placeholder="请输入短信 API 的账号" />
  94 + </el-form-item>
  95 + <el-form-item label="短信 API 的秘钥" prop="apiSecret">
  96 + <el-input v-model="form.apiSecret" placeholder="请输入短信 API 的秘钥" />
  97 + </el-form-item>
  98 + <el-form-item label="短信发送回调 URL" prop="callbackUrl">
  99 + <el-input v-model="form.callbackUrl" placeholder="请输入短信发送回调 URL" />
  100 + </el-form-item>
  101 + </el-form>
  102 + <div slot="footer" class="dialog-footer">
  103 + <el-button type="primary" @click="submitForm">确 定</el-button>
  104 + <el-button @click="cancel">取 消</el-button>
  105 + </div>
  106 + </el-dialog>
  107 + </div>
  108 +</template>
  109 +
  110 +<script>
  111 +import { createSmsChannel, updateSmsChannel, deleteSmsChannel, getSmsChannel, getSmsChannelPage,
  112 + getSimpleSmsChannels } from "@/api/system/sms/smsChannel";
  113 +
  114 +export default {
  115 + name: "SmsChannel",
  116 + components: {
  117 + },
  118 + data() {
  119 + return {
  120 + // 遮罩层
  121 + loading: true,
  122 + // 显示搜索条件
  123 + showSearch: true,
  124 + // 总条数
  125 + total: 0,
  126 + // 短信渠道列表
  127 + list: [],
  128 + // 弹出层标题
  129 + title: "",
  130 + // 是否显示弹出层
  131 + open: false,
  132 + dateRangeCreateTime: [],
  133 + // 查询参数
  134 + queryParams: {
  135 + pageNo: 1,
  136 + pageSize: 10,
  137 + signature: null,
  138 + status: null,
  139 + },
  140 + // 表单参数
  141 + form: {},
  142 + // 表单校验
  143 + rules: {
  144 + signature: [{ required: true, message: "短信签名不能为空", trigger: "blur" }],
  145 + code: [{ required: true, message: "渠道编码不能为空", trigger: "blur" }],
  146 + status: [{ required: true, message: "启用状态不能为空", trigger: "blur" }],
  147 + apiKey: [{ required: true, message: "短信 API 的账号不能为空", trigger: "blur" }],
  148 + },
  149 + };
  150 + },
  151 + created() {
  152 + this.getList();
  153 + },
  154 + methods: {
  155 + /** 查询列表 */
  156 + getList() {
  157 + this.loading = true;
  158 + // 处理查询参数
  159 + let params = {...this.queryParams};
  160 + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
  161 + // 执行查询
  162 + getSmsChannelPage(params).then(response => {
  163 + this.list = response.data.list;
  164 + this.total = response.data.total;
  165 + this.loading = false;
  166 + });
  167 + },
  168 + /** 取消按钮 */
  169 + cancel() {
  170 + this.open = false;
  171 + this.reset();
  172 + },
  173 + /** 表单重置 */
  174 + reset() {
  175 + this.form = {
  176 + id: undefined,
  177 + signature: undefined,
  178 + code: undefined,
  179 + status: undefined,
  180 + remark: undefined,
  181 + apiKey: undefined,
  182 + apiSecret: undefined,
  183 + callbackUrl: undefined,
  184 + };
  185 + this.resetForm("form");
  186 + },
  187 + /** 搜索按钮操作 */
  188 + handleQuery() {
  189 + this.queryParams.pageNo = 1;
  190 + this.getList();
  191 + },
  192 + /** 重置按钮操作 */
  193 + resetQuery() {
  194 + this.dateRangeCreateTime = [];
  195 + this.resetForm("queryForm");
  196 + this.handleQuery();
  197 + },
  198 + /** 新增按钮操作 */
  199 + handleAdd() {
  200 + this.reset();
  201 + this.open = true;
  202 + this.title = "添加短信渠道";
  203 + },
  204 + /** 修改按钮操作 */
  205 + handleUpdate(row) {
  206 + this.reset();
  207 + const id = row.id;
  208 + getSmsChannel(id).then(response => {
  209 + this.form = response.data;
  210 + this.open = true;
  211 + this.title = "修改短信渠道";
  212 + });
  213 + },
  214 + /** 提交按钮 */
  215 + submitForm() {
  216 + this.$refs["form"].validate(valid => {
  217 + if (!valid) {
  218 + return;
  219 + }
  220 + // 修改的提交
  221 + if (this.form.id != null) {
  222 + updateSmsChannel(this.form).then(response => {
  223 + this.msgSuccess("修改成功");
  224 + this.open = false;
  225 + this.getList();
  226 + });
  227 + return;
  228 + }
  229 + // 添加的提交
  230 + createSmsChannel(this.form).then(response => {
  231 + this.msgSuccess("新增成功");
  232 + this.open = false;
  233 + this.getList();
  234 + });
  235 + });
  236 + },
  237 + /** 删除按钮操作 */
  238 + handleDelete(row) {
  239 + const id = row.id;
  240 + this.$confirm('是否确认删除短信渠道编号为"' + id + '"的数据项?', "警告", {
  241 + confirmButtonText: "确定",
  242 + cancelButtonText: "取消",
  243 + type: "warning"
  244 + }).then(function() {
  245 + return deleteSmsChannel(id);
  246 + }).then(() => {
  247 + this.getList();
  248 + this.msgSuccess("删除成功");
  249 + })
  250 + },
  251 + /** 导出按钮操作 */
  252 + handleExport() {
  253 + // 处理查询参数
  254 + let params = {...this.queryParams};
  255 + params.pageNo = undefined;
  256 + params.pageSize = undefined;
  257 + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
  258 + // 执行导出
  259 + this.$confirm('是否确认导出所有短信渠道数据项?', "警告", {
  260 + confirmButtonText: "确定",
  261 + cancelButtonText: "取消",
  262 + type: "warning"
  263 + }).then(function() {
  264 + return exportSmsChannelExcel(params);
  265 + }).then(response => {
  266 + this.downloadExcel(response, '短信渠道.xls');
  267 + })
  268 + }
  269 + }
  270 +};
  271 +</script><template>
  272 + <div class="app-container">
  273 +
  274 + <!-- 搜索工作栏 -->
  275 + <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
  276 + <el-form-item label="短信签名" prop="signature">
  277 + <el-input v-model="queryParams.signature" placeholder="请输入短信签名" clearable size="small" @keyup.enter.native="handleQuery"/>
  278 + </el-form-item>
  279 + <el-form-item label="启用状态" prop="status">
  280 + <el-select v-model="queryParams.status" placeholder="请选择启用状态" clearable size="small">
  281 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_COMMON_STATUS)"
  282 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  283 + </el-select>
  284 + </el-form-item>
  285 + <el-form-item label="创建时间">
  286 + <el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd"
  287 + type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
  288 + </el-form-item>
  289 + <el-form-item>
  290 + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
  291 + <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
  292 + </el-form-item>
  293 + </el-form>
  294 +
  295 + <!-- 操作工具栏 -->
  296 + <el-row :gutter="10" class="mb8">
  297 + <el-col :span="1.5">
  298 + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
  299 + v-hasPermi="['system:sms-channel:create']">新增</el-button>
  300 + </el-col>
  301 + <el-col :span="1.5">
  302 + <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
  303 + v-hasPermi="['system:sms-channel:export']">导出</el-button>
  304 + </el-col>
  305 + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  306 + </el-row>
  307 +
  308 + <!-- 列表 -->
  309 + <el-table v-loading="loading" :data="list">
  310 + <el-table-column label="编号" align="center" prop="id" />
  311 + <el-table-column label="短信签名" align="center" prop="signature" />
  312 + <el-table-column label="渠道编码" align="center" prop="code">
  313 + <template slot-scope="scope">
  314 + <span>{{ getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, scope.row.code) }}</span>
  315 + </template>
  316 + </el-table-column>>
  317 + <el-table-column label="启用状态" align="center" prop="status">
  318 + <template slot-scope="scope">
  319 + <span>{{ getDictDataLabel(DICT_TYPE.SYS_COMMON_STATUS, scope.row.status) }}</span>
  320 + </template>
  321 + </el-table-column>>
  322 + <el-table-column label="备注" align="center" prop="remark" />
  323 + <el-table-column label="创建时间" align="center" prop="createTime" width="180">
  324 + <template slot-scope="scope">
  325 + <span>{{ parseTime(scope.row.createTime) }}</span>
  326 + </template>
  327 + </el-table-column>
  328 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  329 + <template slot-scope="scope">
  330 + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
  331 + v-hasPermi="['system:sms-channel:update']">修改</el-button>
  332 + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  333 + v-hasPermi="['system:sms-channel:delete']">删除</el-button>
  334 + </template>
  335 + </el-table-column>
  336 + </el-table>
  337 + <!-- 分页组件 -->
  338 + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
  339 + @pagination="getList"/>
  340 +
  341 + <!-- 对话框(添加 / 修改) -->
  342 + <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
  343 + <el-form ref="form" :model="form" :rules="rules" label-width="130px">
  344 + <el-form-item label="短信签名" prop="signature">
  345 + <el-input v-model="form.signature" placeholder="请输入短信签名" />
  346 + </el-form-item>
  347 + <el-form-item label="渠道编码" prop="code">
  348 + <el-select v-model="form.code" placeholder="请选择渠道编码" :disabled="form.id > 0">
  349 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_SMS_CHANNEL_CODE)"
  350 + :key="dict.value" :label="dict.label" :value="dict.value" />
  351 + </el-select>
  352 + </el-form-item>
  353 + <el-form-item label="启用状态">
  354 + <el-radio-group v-model="form.status">
  355 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.SYS_COMMON_STATUS)"
  356 + :key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
  357 + </el-radio-group>
  358 + </el-form-item>
  359 + <el-form-item label="备注" prop="remark">
  360 + <el-input v-model="form.remark" placeholder="请输入备注" />
  361 + </el-form-item>
  362 + <el-form-item label="短信 API 的账号" prop="apiKey">
  363 + <el-input v-model="form.apiKey" placeholder="请输入短信 API 的账号" />
  364 + </el-form-item>
  365 + <el-form-item v-if="form.code !== 'YUN_PIAN'" label="短信 API 的秘钥" prop="apiSecret">
  366 + <el-input v-model="form.apiSecret" placeholder="请输入短信 API 的秘钥" />
  367 + </el-form-item>
  368 + <el-form-item label="短信发送回调 URL" prop="callbackUrl">
  369 + <el-input v-model="form.callbackUrl" placeholder="请输入短信发送回调 URL" />
  370 + </el-form-item>
  371 + </el-form>
  372 + <div slot="footer" class="dialog-footer">
  373 + <el-button type="primary" @click="submitForm">确 定</el-button>
  374 + <el-button @click="cancel">取 消</el-button>
  375 + </div>
  376 + </el-dialog>
  377 + </div>
  378 +</template>
  379 +
  380 +<script>
  381 +import { createSmsChannel, updateSmsChannel, deleteSmsChannel, getSmsChannel, getSmsChannelPage } from "@/api/system/sms/smsChannel";
  382 +
  383 +export default {
  384 + name: "SmsChannel",
  385 + components: {
  386 + },
  387 + data() {
  388 + return {
  389 + // 遮罩层
  390 + loading: true,
  391 + // 显示搜索条件
  392 + showSearch: true,
  393 + // 总条数
  394 + total: 0,
  395 + // 短信渠道列表
  396 + list: [],
  397 + // 弹出层标题
  398 + title: "",
  399 + // 是否显示弹出层
  400 + open: false,
  401 + dateRangeCreateTime: [],
  402 + // 查询参数
  403 + queryParams: {
  404 + pageNo: 1,
  405 + pageSize: 10,
  406 + signature: null,
  407 + status: null,
  408 + },
  409 + // 表单参数
  410 + form: {},
  411 + // 表单校验
  412 + apiKeyEnableChannelCodes: ['YUN_PIAN'],
  413 + rules: {
  414 + signature: [{ required: true, message: "短信签名不能为空", trigger: "blur" }],
  415 + code: [{ required: true, message: "渠道编码不能为空", trigger: "blur" }],
  416 + status: [{ required: true, message: "启用状态不能为空", trigger: "blur" }],
  417 + apiKey: [{ required: true, message: "短信 API 的账号不能为空", trigger: "blur" }],
  418 + apiSecret: [{ required: true, message: "短信 API 的秘钥不能为空", trigger: "blur" }],
  419 + }
  420 + };
  421 + },
  422 + created() {
  423 + this.getList();
  424 + },
  425 + methods: {
  426 + /** 查询列表 */
  427 + getList() {
  428 + this.loading = true;
  429 + // 处理查询参数
  430 + let params = {...this.queryParams};
  431 + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
  432 + // 执行查询
  433 + getSmsChannelPage(params).then(response => {
  434 + this.list = response.data.list;
  435 + this.total = response.data.total;
  436 + this.loading = false;
  437 + });
  438 + },
  439 + /** 取消按钮 */
  440 + cancel() {
  441 + this.open = false;
  442 + this.reset();
  443 + },
  444 + /** 表单重置 */
  445 + reset() {
  446 + this.form = {
  447 + id: undefined,
  448 + signature: undefined,
  449 + code: undefined,
  450 + status: undefined,
  451 + remark: undefined,
  452 + apiKey: undefined,
  453 + apiSecret: undefined,
  454 + callbackUrl: undefined,
  455 + };
  456 + this.resetForm("form");
  457 + },
  458 + /** 搜索按钮操作 */
  459 + handleQuery() {
  460 + this.queryParams.pageNo = 1;
  461 + this.getList();
  462 + },
  463 + /** 重置按钮操作 */
  464 + resetQuery() {
  465 + this.dateRangeCreateTime = [];
  466 + this.resetForm("queryForm");
  467 + this.handleQuery();
  468 + },
  469 + /** 新增按钮操作 */
  470 + handleAdd() {
  471 + this.reset();
  472 + this.open = true;
  473 + this.title = "添加短信渠道";
  474 + },
  475 + /** 修改按钮操作 */
  476 + handleUpdate(row) {
  477 + this.reset();
  478 + const id = row.id;
  479 + getSmsChannel(id).then(response => {
  480 + this.form = response.data;
  481 + this.open = true;
  482 + this.title = "修改短信渠道";
  483 + });
  484 + },
  485 + /** 提交按钮 */
  486 + submitForm() {
  487 + this.$refs["form"].validate(valid => {
  488 + if (!valid) {
  489 + return;
  490 + }
  491 + // 修改的提交
  492 + if (this.form.id != null) {
  493 + updateSmsChannel(this.form).then(response => {
  494 + this.msgSuccess("修改成功");
  495 + this.open = false;
  496 + this.getList();
  497 + });
  498 + return;
  499 + }
  500 + // 添加的提交
  501 + createSmsChannel(this.form).then(response => {
  502 + this.msgSuccess("新增成功");
  503 + this.open = false;
  504 + this.getList();
  505 + });
  506 + });
  507 + },
  508 + /** 删除按钮操作 */
  509 + handleDelete(row) {
  510 + const id = row.id;
  511 + this.$confirm('是否确认删除短信渠道编号为"' + id + '"的数据项?', "警告", {
  512 + confirmButtonText: "确定",
  513 + cancelButtonText: "取消",
  514 + type: "warning"
  515 + }).then(function() {
  516 + return deleteSmsChannel(id);
  517 + }).then(() => {
  518 + this.getList();
  519 + this.msgSuccess("删除成功");
  520 + })
  521 + },
  522 + /** 导出按钮操作 */
  523 + handleExport() {
  524 + // 处理查询参数
  525 + let params = {...this.queryParams};
  526 + params.pageNo = undefined;
  527 + params.pageSize = undefined;
  528 + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
  529 + // 执行导出
  530 + this.$confirm('是否确认导出所有短信渠道数据项?', "警告", {
  531 + confirmButtonText: "确定",
  532 + cancelButtonText: "取消",
  533 + type: "warning"
  534 + }).then(function() {
  535 + return exportSmsChannelExcel(params);
  536 + }).then(response => {
  537 + this.downloadExcel(response, '短信渠道.xls');
  538 + })
  539 + }
  540 + }
  541 +};
  542 +</script>
ruoyi-ui/src/views/system/sms/smsLog.vue 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 +
  4 + <!-- 搜索工作栏 -->
  5 + <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
  6 + <el-form-item label="手机号" prop="mobile">
  7 + <el-input v-model="queryParams.mobile" placeholder="请输入手机号" clearable size="small" @keyup.enter.native="handleQuery"/>
  8 + </el-form-item>
  9 + <el-form-item label="短信渠道" prop="channelId">
  10 + <el-select v-model="queryParams.channelId" placeholder="请选择短信渠道" clearable size="small">
  11 + <el-option v-for="channel in channelOptions"
  12 + :key="channel.id" :value="channel.id"
  13 + :label="channel.signature + '【' + getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, channel.code) + '】'" />
  14 + </el-select>
  15 + </el-form-item>
  16 + <el-form-item label="模板编号" prop="templateId">
  17 + <el-input v-model="queryParams.templateId" placeholder="请输入模板编号" clearable size="small" @keyup.enter.native="handleQuery"/>
  18 + </el-form-item>
  19 + <el-form-item label="发送状态" prop="sendStatus">
  20 + <el-select v-model="queryParams.sendStatus" placeholder="请选择发送状态" clearable size="small">
  21 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_SMS_SEND_STATUS)"
  22 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  23 + </el-select>
  24 + </el-form-item>
  25 + <el-form-item label="发送时间">
  26 + <el-date-picker v-model="dateRangeSendTime" size="small" style="width: 240px" value-format="yyyy-MM-dd"
  27 + type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
  28 + </el-form-item>
  29 + <el-form-item label="接收状态" prop="receiveStatus">
  30 + <el-select v-model="queryParams.receiveStatus" placeholder="请选择接收状态" clearable size="small">
  31 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_SMS_RECEIVE_STATUS)"
  32 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  33 + </el-select>
  34 + </el-form-item>
  35 + <el-form-item label="接收时间">
  36 + <el-date-picker v-model="dateRangeReceiveTime" size="small" style="width: 240px" value-format="yyyy-MM-dd"
  37 + type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
  38 + </el-form-item>
  39 + <el-form-item>
  40 + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
  41 + <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
  42 + </el-form-item>
  43 + </el-form>
  44 +
  45 + <!-- 操作工具栏 -->
  46 + <el-row :gutter="10" class="mb8">
  47 + <el-col :span="1.5">
  48 + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
  49 + v-hasPermi="['system:sms-log:create']">新增</el-button>
  50 + </el-col>
  51 + <el-col :span="1.5">
  52 + <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
  53 + v-hasPermi="['system:sms-log:export']">导出</el-button>
  54 + </el-col>
  55 + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  56 + </el-row>
  57 +
  58 + <!-- 列表 -->
  59 + <el-table v-loading="loading" :data="list">
  60 + <el-table-column label="编号" align="center" prop="id" />
  61 + <el-table-column label="创建时间" align="center" prop="createTime" width="180">
  62 + <template slot-scope="scope">
  63 + <span>{{ parseTime(scope.row.createTime) }}</span>
  64 + </template>
  65 + </el-table-column>
  66 + <el-table-column label="手机号" align="center" prop="mobile" width="120">
  67 + <template slot-scope="scope">
  68 + <div>{{ scope.row.mobile }}</div>
  69 + <div v-if="scope.row.userType && scope.row.userId">
  70 + {{ getDictDataLabel(DICT_TYPE.USER_TYPE, scope.row.userType) + '(' + scope.row.userId + ')' }}
  71 + </div>
  72 + </template>
  73 + </el-table-column>
  74 + <el-table-column label="短信内容" align="center" prop="templateContent" width="300" />
  75 + <el-table-column label="发送状态" align="center" width="180">
  76 + <template slot-scope="scope">
  77 + <div>{{ getDictDataLabel(DICT_TYPE.SYS_SMS_SEND_STATUS, scope.row.sendStatus) }}</div>
  78 + <div>{{ parseTime(scope.row.sendTime) }}</div>
  79 + </template>
  80 + </el-table-column>
  81 + <el-table-column label="接收状态" align="center" width="180">
  82 + <template slot-scope="scope">
  83 + <div>{{ getDictDataLabel(DICT_TYPE.SYS_SMS_RECEIVE_STATUS, scope.row.receiveStatus) }}</div>
  84 + <div>{{ parseTime(scope.row.receiveTime) }}</div>
  85 + </template>
  86 + </el-table-column>
  87 + <el-table-column label="短信渠道" align="center" width="120">
  88 + <template slot-scope="scope">
  89 + <div>{{ formatChannelSignature(scope.row.channelId) }}</div>
  90 + <div>【{{ getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, scope.row.channelCode) }}】</div>
  91 + </template>
  92 + </el-table-column>
  93 + <el-table-column label="模板编号" align="center" prop="templateId" />
  94 + <el-table-column label="短信类型" align="center" prop="templateType">
  95 + <template slot-scope="scope">
  96 + <span>{{ getDictDataLabel(DICT_TYPE.SYS_SMS_TEMPLATE_TYPE, scope.row.templateType) }}</span>
  97 + </template>
  98 + </el-table-column>
  99 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  100 + <template slot-scope="scope">
  101 + <el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row,scope.index)"
  102 + v-hasPermi="['system:sms-log:query']">详细</el-button>
  103 + </template>
  104 + </el-table-column>
  105 + </el-table>
  106 + <!-- 分页组件 -->
  107 + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
  108 + @pagination="getList"/>
  109 +
  110 + <!-- 短信日志详细 -->
  111 + <el-dialog title="短信日志详细" :visible.sync="open" width="700px" append-to-body>
  112 + <el-form ref="form" :model="form" label-width="140px" size="mini">
  113 + <el-row>
  114 + <el-col :span="24">
  115 + <el-form-item label="日志主键:">{{ form.id }}</el-form-item>
  116 + </el-col>
  117 + <el-col :span="24">
  118 + <el-form-item label="短信渠道:">
  119 + {{ formatChannelSignature(form.channelId) }}【{{ getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, form.channelCode) }}】
  120 + </el-form-item>
  121 + </el-col>
  122 + <el-col :span="24">
  123 + <el-form-item label="短信模板:">
  124 + {{ form.templateId }} | {{ form.templateCode}} | {{ getDictDataLabel(DICT_TYPE.SYS_SMS_TEMPLATE_TYPE, form.templateType) }}
  125 + </el-form-item>
  126 + </el-col>
  127 + <el-col :span="24">
  128 + <el-form-item label="API 的模板编号:">{{ form.apiTemplateId }}</el-form-item>
  129 + </el-col>
  130 + <el-col :span="24">
  131 + <el-form-item label="用户信息:">{{ form.mobile }}
  132 + <span v-if="form.userType && form.userId"> | {{ getDictDataLabel(DICT_TYPE.USER_TYPE, form.userType) }} | {{ form.userId }}</span>
  133 + </el-form-item>
  134 + </el-col>
  135 + <el-col :span="24">
  136 + <el-form-item label="短信内容:">{{ form.templateContent }}</el-form-item>
  137 + </el-col>
  138 + <el-col :span="24">
  139 + <el-form-item label="短信参数:">{{ form.templateParams }}</el-form-item>
  140 + </el-col>
  141 + <el-col :span="24">
  142 + <el-form-item label="创建时间:">{{ parseTime(form.createTime) }}</el-form-item>
  143 + </el-col>
  144 + <el-col :span="24">
  145 + <el-form-item label="发送状态:">{{ getDictDataLabel(DICT_TYPE.SYS_SMS_SEND_STATUS, form.sendStatus) }}</el-form-item>
  146 + </el-col>
  147 + <el-col :span="24">
  148 + <el-form-item label="发送时间:">{{ parseTime(form.sendTime) }}</el-form-item>
  149 + </el-col>
  150 + <el-col :span="24">
  151 + <el-form-item label="发送结果:">{{ form.sendCode }} | {{ form.sendMsg }}
  152 + </el-form-item>
  153 + </el-col>
  154 + <el-col :span="24">
  155 + <el-form-item label="API 发送结果:">{{ form.apiSendCode }} | {{ form.apiSendMsg }}</el-form-item>
  156 + </el-col>
  157 + <el-col :span="24">
  158 + <el-form-item label="API 短信编号:">{{ form.apiSerialNo }}</el-form-item>
  159 + </el-col>
  160 + <el-col :span="24">
  161 + <el-form-item label="API 请求编号:">{{ form.apiRequestId }}</el-form-item>
  162 + </el-col>
  163 + <el-col :span="24">
  164 + <el-form-item label="接收状态:">{{ getDictDataLabel(DICT_TYPE.SYS_SMS_RECEIVE_STATUS, form.receiveStatus) }}</el-form-item>
  165 + </el-col>
  166 + <el-col :span="24">
  167 + <el-form-item label="接收时间:">{{ parseTime(form.receiveTime) }}</el-form-item>
  168 + </el-col>
  169 + <el-col :span="24">
  170 + <el-form-item label="API 接收结果:">{{ form.apiReceiveCode }} | {{ form.apiReceiveMsg }}
  171 + </el-form-item>
  172 + </el-col>
  173 + </el-row>
  174 + </el-form>
  175 + <div slot="footer" class="dialog-footer">
  176 + <el-button @click="open = false">关 闭</el-button>
  177 + </div>
  178 + </el-dialog>
  179 +
  180 + </div>
  181 +</template>
  182 +
  183 +<script>
  184 +import { getSmsLogPage, exportSmsLogExcel } from "@/api/system/sms/smsLog";
  185 +import { getSimpleSmsChannels } from "@/api/system/sms/smsChannel";
  186 +
  187 +export default {
  188 + name: "SmsLog",
  189 + components: {
  190 + },
  191 + data() {
  192 + return {
  193 + // 遮罩层
  194 + loading: true,
  195 + // 显示搜索条件
  196 + showSearch: true,
  197 + // 总条数
  198 + total: 0,
  199 + // 短信日志列表
  200 + list: [],
  201 + // 弹出层标题
  202 + title: "",
  203 + // 是否显示弹出层
  204 + open: false,
  205 + dateRangeSendTime: [],
  206 + dateRangeReceiveTime: [],
  207 + // 表单参数
  208 + form: {},
  209 + // 查询参数
  210 + queryParams: {
  211 + pageNo: 1,
  212 + pageSize: 10,
  213 + channelId: null,
  214 + templateId: null,
  215 + mobile: null,
  216 + sendStatus: null,
  217 + receiveStatus: null,
  218 + },
  219 + // 短信渠道
  220 + channelOptions: [],
  221 + };
  222 + },
  223 + created() {
  224 + this.getList();
  225 + // 获得短信渠道
  226 + getSimpleSmsChannels().then(response => {
  227 + this.channelOptions = response.data;
  228 + })
  229 + },
  230 + methods: {
  231 + /** 查询列表 */
  232 + getList() {
  233 + this.loading = true;
  234 + // 处理查询参数
  235 + let params = {...this.queryParams};
  236 + this.addBeginAndEndTime(params, this.dateRangeSendTime, 'sendTime');
  237 + this.addBeginAndEndTime(params, this.dateRangeReceiveTime, 'receiveTime');
  238 + // 执行查询
  239 + getSmsLogPage(params).then(response => {
  240 + this.list = response.data.list;
  241 + this.total = response.data.total;
  242 + this.loading = false;
  243 + });
  244 + },
  245 + /** 取消按钮 */
  246 + cancel() {
  247 + this.open = false;
  248 + this.reset();
  249 + },
  250 + /** 搜索按钮操作 */
  251 + handleQuery() {
  252 + this.queryParams.pageNo = 1;
  253 + this.getList();
  254 + },
  255 + /** 重置按钮操作 */
  256 + resetQuery() {
  257 + this.dateRangeSendTime = [];
  258 + this.dateRangeReceiveTime = [];
  259 + this.resetForm("queryForm");
  260 + this.handleQuery();
  261 + },
  262 + /** 导出按钮操作 */
  263 + handleExport() {
  264 + // 处理查询参数
  265 + let params = {...this.queryParams};
  266 + params.pageNo = undefined;
  267 + params.pageSize = undefined;
  268 + this.addBeginAndEndTime(params, this.dateRangeSendTime, 'sendTime');
  269 + this.addBeginAndEndTime(params, this.dateRangeReceiveTime, 'receiveTime');
  270 + // 执行导出
  271 + this.$confirm('是否确认导出所有短信日志数据项?', "警告", {
  272 + confirmButtonText: "确定",
  273 + cancelButtonText: "取消",
  274 + type: "warning"
  275 + }).then(function() {
  276 + return exportSmsLogExcel(params);
  277 + }).then(response => {
  278 + this.downloadExcel(response, '短信日志.xls');
  279 + })
  280 + },
  281 + /** 详细按钮操作 */
  282 + handleView(row) {
  283 + this.open = true;
  284 + this.form = row;
  285 + },
  286 + /** 格式化短信渠道 */
  287 + formatChannelSignature(channelId) {
  288 + for (const channel of this.channelOptions) {
  289 + if (channel.id === channelId) {
  290 + return channel.signature;
  291 + }
  292 + }
  293 + return '找不到签名:' + channelId;
  294 + }
  295 + }
  296 +};
  297 +</script>
ruoyi-ui/src/views/system/sms/smsTemplate.vue 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 +
  4 + <!-- 搜索工作栏 -->
  5 + <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="150px">
  6 + <el-form-item label="短信类型" prop="type">
  7 + <el-select v-model="queryParams.type" placeholder="请选择短信类型" clearable size="small">
  8 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_SMS_TEMPLATE_TYPE)"
  9 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  10 + </el-select>
  11 + </el-form-item>
  12 + <el-form-item label="开启状态" prop="status">
  13 + <el-select v-model="queryParams.status" placeholder="请选择开启状态" clearable size="small">
  14 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_COMMON_STATUS)"
  15 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  16 + </el-select>
  17 + </el-form-item>
  18 + <el-form-item label="模板编码" prop="code">
  19 + <el-input v-model="queryParams.code" placeholder="请输入模板编码" clearable size="small" @keyup.enter.native="handleQuery"/>
  20 + </el-form-item>
  21 + <el-form-item label="短信 API 的模板编号" prop="apiTemplateId">
  22 + <el-input v-model="queryParams.apiTemplateId" placeholder="请输入短信 API 的模板编号" clearable size="small" @keyup.enter.native="handleQuery"/>
  23 + </el-form-item>
  24 + <el-form-item label="短信渠道" prop="channelId">
  25 + <el-select v-model="queryParams.channelId" placeholder="请选择短信渠道" clearable size="small">
  26 + <el-option v-for="channel in channelOptions"
  27 + :key="channel.id" :value="channel.id"
  28 + :label="channel.signature + '【' + getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, channel.code) + '】'" />
  29 + </el-select>
  30 + </el-form-item>
  31 + <el-form-item label="创建时间">
  32 + <el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd"
  33 + type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
  34 + </el-form-item>
  35 + <el-form-item>
  36 + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
  37 + <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
  38 + </el-form-item>
  39 + </el-form>
  40 +
  41 + <!-- 操作工具栏 -->
  42 + <el-row :gutter="10" class="mb8">
  43 + <el-col :span="1.5">
  44 + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
  45 + v-hasPermi="['system:sms-template:create']">新增</el-button>
  46 + </el-col>
  47 + <el-col :span="1.5">
  48 + <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
  49 + v-hasPermi="['system:sms-template:export']">导出</el-button>
  50 + </el-col>
  51 + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  52 + </el-row>
  53 +
  54 + <!-- 列表 -->
  55 + <el-table v-loading="loading" :data="list">
  56 + <el-table-column label="模板编码" align="center" prop="code" />
  57 + <el-table-column label="模板名称" align="center" prop="name" />
  58 + <el-table-column label="模板内容" align="center" prop="content" width="300" />
  59 + <el-table-column label="短信类型" align="center" prop="type">
  60 + <template slot-scope="scope">
  61 + <span>{{ getDictDataLabel(DICT_TYPE.SYS_SMS_TEMPLATE_TYPE, scope.row.type) }}</span>
  62 + </template>
  63 + </el-table-column>
  64 + <el-table-column label="开启状态" align="center" prop="status">
  65 + <template slot-scope="scope">
  66 + <span>{{ getDictDataLabel(DICT_TYPE.SYS_COMMON_STATUS, scope.row.status) }}</span>
  67 + </template>
  68 + </el-table-column>
  69 + <el-table-column label="备注" align="center" prop="remark" />
  70 + <el-table-column label="短信 API 的模板编号" align="center" prop="apiTemplateId" width="180" />
  71 + <el-table-column label="短信渠道" align="center" width="120">
  72 + <template slot-scope="scope">
  73 + <div>{{ formatChannelSignature(scope.row.channelId) }}</div>
  74 + <div>【{{ getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, scope.row.channelCode) }}】</div>
  75 + </template>
  76 + </el-table-column>
  77 + <el-table-column label="创建时间" align="center" prop="createTime" width="180">
  78 + <template slot-scope="scope">
  79 + <span>{{ parseTime(scope.row.createTime) }}</span>
  80 + </template>
  81 + </el-table-column>
  82 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="150">
  83 + <template slot-scope="scope">
  84 + <el-button size="mini" type="text" icon="el-icon-share" @click="handleSendSms(scope.row)"
  85 + v-hasPermi="['system:sms-template:send-sms']">测试</el-button>
  86 + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
  87 + v-hasPermi="['system:sms-template:update']">修改</el-button>
  88 + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  89 + v-hasPermi="['system:sms-template:delete']">删除</el-button>
  90 + </template>
  91 + </el-table-column>
  92 + </el-table>
  93 + <!-- 分页组件 -->
  94 + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
  95 + @pagination="getList"/>
  96 +
  97 + <!-- 对话框(添加 / 修改) -->
  98 + <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
  99 + <el-form ref="form" :model="form" :rules="rules" label-width="140px">
  100 + <el-form-item label="短信渠道编号" prop="channelId">
  101 + <el-select v-model="form.channelId" placeholder="请选择短信渠道编号">
  102 + <el-option v-for="channel in channelOptions"
  103 + :key="channel.id" :value="channel.id"
  104 + :label="channel.signature + '【' + getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, channel.code) + '】'" />
  105 + </el-select>
  106 + </el-form-item>
  107 + <el-form-item label="短信类型" prop="type">
  108 + <el-select v-model="form.type" placeholder="请选择短信类型">
  109 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_SMS_TEMPLATE_TYPE)"
  110 + :key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
  111 + </el-select>
  112 + </el-form-item>
  113 + <el-form-item label="模板编号" prop="code">
  114 + <el-input v-model="form.code" placeholder="请输入模板编号" />
  115 + </el-form-item>
  116 + <el-form-item label="模板名称" prop="name">
  117 + <el-input v-model="form.name" placeholder="请输入模板名称" />
  118 + </el-form-item>
  119 + <el-form-item label="模板内容" prop="content">
  120 + <el-input type="textarea" v-model="form.content" placeholder="请输入模板内容" />
  121 + </el-form-item>
  122 + <el-form-item label="开启状态" prop="status">
  123 + <el-radio-group v-model="form.status">
  124 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.SYS_COMMON_STATUS)"
  125 + :key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
  126 + </el-radio-group>
  127 + </el-form-item>
  128 + <el-form-item label="短信 API 模板编号" prop="apiTemplateId">
  129 + <el-input v-model="form.apiTemplateId" placeholder="请输入短信 API 的模板编号" />
  130 + </el-form-item>
  131 + <el-form-item label="备注" prop="remark">
  132 + <el-input v-model="form.remark" placeholder="请输入备注" />
  133 + </el-form-item>
  134 + </el-form>
  135 + <div slot="footer" class="dialog-footer">
  136 + <el-button type="primary" @click="submitForm">确 定</el-button>
  137 + <el-button @click="cancel">取 消</el-button>
  138 + </div>
  139 + </el-dialog>
  140 +
  141 + <!-- 对话框(发送短信) -->
  142 + <el-dialog title="测试发送短信" :visible.sync="sendSmsOpen" width="500px" append-to-body>
  143 + <el-form ref="sendSmsForm" :model="sendSmsForm" :rules="sendSmsRules" label-width="140px">
  144 + <el-form-item label="模板内容" prop="content">
  145 + <el-input v-model="sendSmsForm.content" type="textarea" placeholder="请输入模板内容" readonly />
  146 + </el-form-item>
  147 + <el-form-item label="手机号" prop="mobile">
  148 + <el-input v-model="sendSmsForm.mobile" placeholder="请输入手机号" />
  149 + </el-form-item>
  150 + <el-form-item v-for="param in sendSmsForm.params" :label="'参数 {' + param + '}'" :prop="'templateParams.' + param">
  151 + <el-input v-model="sendSmsForm.templateParams[param]" :placeholder="'请输入 ' + param + ' 参数'" />
  152 + </el-form-item>
  153 + </el-form>
  154 + <div slot="footer" class="dialog-footer">
  155 + <el-button type="primary" @click="submitSendSmsForm">确 定</el-button>
  156 + <el-button @click="cancelSendSms">取 消</el-button>
  157 + </div>
  158 + </el-dialog>
  159 +
  160 + </div>
  161 +</template>
  162 +
  163 +<script>
  164 +import { createSmsTemplate, updateSmsTemplate, deleteSmsTemplate, getSmsTemplate, getSmsTemplatePage,
  165 + exportSmsTemplateExcel, sendSms } from "@/api/system/sms/smsTemplate";
  166 +import { getSimpleSmsChannels } from "@/api/system/sms/smsChannel";
  167 +
  168 +export default {
  169 + name: "SmsTemplate",
  170 + data() {
  171 + return {
  172 + // 遮罩层
  173 + loading: true,
  174 + // 显示搜索条件
  175 + showSearch: true,
  176 + // 总条数
  177 + total: 0,
  178 + // 短信模板列表
  179 + list: [],
  180 + // 弹出层标题
  181 + title: "",
  182 + // 是否显示弹出层
  183 + open: false,
  184 + dateRangeCreateTime: [],
  185 + // 查询参数
  186 + queryParams: {
  187 + pageNo: 1,
  188 + pageSize: 10,
  189 + type: null,
  190 + status: null,
  191 + code: null,
  192 + content: null,
  193 + apiTemplateId: null,
  194 + channelId: null,
  195 + },
  196 + // 表单参数
  197 + form: {},
  198 + // 表单校验
  199 + rules: {
  200 + type: [{ required: true, message: "短信类型不能为空", trigger: "change" }],
  201 + status: [{ required: true, message: "开启状态不能为空", trigger: "blur" }],
  202 + code: [{ required: true, message: "模板编码不能为空", trigger: "blur" }],
  203 + name: [{ required: true, message: "模板名称不能为空", trigger: "blur" }],
  204 + content: [{ required: true, message: "模板内容不能为空", trigger: "blur" }],
  205 + apiTemplateId: [{ required: true, message: "短信 API 的模板编号不能为空", trigger: "blur" }],
  206 + channelId: [{ required: true, message: "短信渠道编号不能为空", trigger: "change" }],
  207 + },
  208 + // 短信渠道
  209 + channelOptions: [],
  210 + // 发送短信
  211 + sendSmsOpen: false,
  212 + sendSmsForm: {
  213 + params: [], // 模板的参数列表
  214 + },
  215 + sendSmsRules: {
  216 + mobile: [{ required: true, message: "手机不能为空", trigger: "blur" }],
  217 + templateCode: [{ required: true, message: "手机不能为空", trigger: "blur" }],
  218 + templateParams: { }
  219 + }
  220 + };
  221 + },
  222 + created() {
  223 + this.getList();
  224 + // 获得短信渠道
  225 + getSimpleSmsChannels().then(response => {
  226 + this.channelOptions = response.data;
  227 + })
  228 + },
  229 + methods: {
  230 + /** 查询列表 */
  231 + getList() {
  232 + this.loading = true;
  233 + // 处理查询参数
  234 + let params = {...this.queryParams};
  235 + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
  236 + // 执行查询
  237 + getSmsTemplatePage(params).then(response => {
  238 + this.list = response.data.list;
  239 + this.total = response.data.total;
  240 + this.loading = false;
  241 + });
  242 + },
  243 + /** 取消按钮 */
  244 + cancel() {
  245 + this.open = false;
  246 + this.reset();
  247 + },
  248 + /** 表单重置 */
  249 + reset() {
  250 + this.form = {
  251 + id: undefined,
  252 + type: undefined,
  253 + status: undefined,
  254 + code: undefined,
  255 + name: undefined,
  256 + content: undefined,
  257 + remark: undefined,
  258 + apiTemplateId: undefined,
  259 + channelId: undefined,
  260 + };
  261 + this.resetForm("form");
  262 + },
  263 + /** 搜索按钮操作 */
  264 + handleQuery() {
  265 + this.queryParams.pageNo = 1;
  266 + this.getList();
  267 + },
  268 + /** 重置按钮操作 */
  269 + resetQuery() {
  270 + this.dateRangeCreateTime = [];
  271 + this.resetForm("queryForm");
  272 + this.handleQuery();
  273 + },
  274 + /** 新增按钮操作 */
  275 + handleAdd() {
  276 + this.reset();
  277 + this.open = true;
  278 + this.title = "添加短信模板";
  279 + },
  280 + /** 修改按钮操作 */
  281 + handleUpdate(row) {
  282 + this.reset();
  283 + const id = row.id;
  284 + getSmsTemplate(id).then(response => {
  285 + this.form = response.data;
  286 + this.open = true;
  287 + this.title = "修改短信模板";
  288 + });
  289 + },
  290 + /** 提交按钮 */
  291 + submitForm() {
  292 + this.$refs["form"].validate(valid => {
  293 + if (!valid) {
  294 + return;
  295 + }
  296 + // 修改的提交
  297 + if (this.form.id != null) {
  298 + updateSmsTemplate(this.form).then(response => {
  299 + this.msgSuccess("修改成功");
  300 + this.open = false;
  301 + this.getList();
  302 + });
  303 + return;
  304 + }
  305 + // 添加的提交
  306 + createSmsTemplate(this.form).then(response => {
  307 + this.msgSuccess("新增成功");
  308 + this.open = false;
  309 + this.getList();
  310 + });
  311 + });
  312 + },
  313 + /** 删除按钮操作 */
  314 + handleDelete(row) {
  315 + const id = row.id;
  316 + this.$confirm('是否确认删除短信模板编号为"' + id + '"的数据项?', "警告", {
  317 + confirmButtonText: "确定",
  318 + cancelButtonText: "取消",
  319 + type: "warning"
  320 + }).then(function() {
  321 + return deleteSmsTemplate(id);
  322 + }).then(() => {
  323 + this.getList();
  324 + this.msgSuccess("删除成功");
  325 + })
  326 + },
  327 + /** 导出按钮操作 */
  328 + handleExport() {
  329 + // 处理查询参数
  330 + let params = {...this.queryParams};
  331 + params.pageNo = undefined;
  332 + params.pageSize = undefined;
  333 + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
  334 + // 执行导出
  335 + this.$confirm('是否确认导出所有短信模板数据项?', "警告", {
  336 + confirmButtonText: "确定",
  337 + cancelButtonText: "取消",
  338 + type: "warning"
  339 + }).then(function() {
  340 + return exportSmsTemplateExcel(params);
  341 + }).then(response => {
  342 + this.downloadExcel(response, '短信模板.xls');
  343 + })
  344 + },
  345 + /** 发送短息按钮 */
  346 + handleSendSms(row) {
  347 + this.resetSendSms(row);
  348 + // 设置参数
  349 + this.sendSmsForm.content = row.content;
  350 + this.sendSmsForm.params = row.params;
  351 + this.sendSmsForm.templateCode = row.code;
  352 + this.sendSmsForm.templateParams = row.params.reduce(function(obj, item) {
  353 + obj[item] = undefined;
  354 + return obj;
  355 + }, {});
  356 + // 根据 row 重置 rules
  357 + this.sendSmsRules.templateParams = row.params.reduce(function(obj, item) {
  358 + obj[item] = { required: true, message: '参数 ' + item + " 不能为空", trigger: "change" };
  359 + return obj;
  360 + }, {});
  361 + // 设置打开
  362 + this.sendSmsOpen = true;
  363 + },
  364 + /** 重置发送短信的表单 */
  365 + resetSendSms() {
  366 + // 根据 row 重置表单
  367 + this.sendSmsForm = {
  368 + content: undefined,
  369 + params: undefined,
  370 + mobile: undefined,
  371 + templateCode: undefined,
  372 + templateParams: {}
  373 + };
  374 + this.resetForm("sendSmsForm");
  375 + },
  376 + /** 取消发送短信 */
  377 + cancelSendSms() {
  378 + this.sendSmsOpen = false;
  379 + this.resetSendSms();
  380 + },
  381 + /** 提交按钮 */
  382 + submitSendSmsForm() {
  383 + this.$refs["sendSmsForm"].validate(valid => {
  384 + if (!valid) {
  385 + return;
  386 + }
  387 + // 添加的提交
  388 + sendSms(this.sendSmsForm).then(response => {
  389 + this.msgSuccess("提交发送成功!发送结果,见发送日志编号:" + response.data);
  390 + this.sendSmsOpen = false;
  391 + });
  392 + });
  393 + },
  394 + /** 格式化短信渠道 */
  395 + formatChannelSignature(channelId) {
  396 + for (const channel of this.channelOptions) {
  397 + if (channel.id === channelId) {
  398 + return channel.signature;
  399 + }
  400 + }
  401 + return '找不到签名:' + channelId;
  402 + }
  403 + }
  404 +};
  405 +</script>
sql/ruoyi-vue-pro.sql
1 /* 1 /*
2 Navicat Premium Data Transfer 2 Navicat Premium Data Transfer
3 3
4 - Source Server : 127.0.0.1 4 + Source Server : local-mysql001
5 Source Server Type : MySQL 5 Source Server Type : MySQL
6 Source Server Version : 50718 6 Source Server Version : 50718
7 Source Host : localhost:3306 7 Source Host : localhost:3306
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 Target Server Version : 50718 11 Target Server Version : 50718
12 File Encoding : 65001 12 File Encoding : 65001
13 13
14 - Date: 21/03/2021 18:53:24 14 + Date: 05/04/2021 23:51:38
15 */ 15 */
16 16
17 SET NAMES utf8mb4; 17 SET NAMES utf8mb4;
@@ -43,7 +43,7 @@ CREATE TABLE `inf_api_access_log` ( @@ -43,7 +43,7 @@ CREATE TABLE `inf_api_access_log` (
43 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', 43 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´',
44 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤', 44 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤',
45 PRIMARY KEY (`id`) USING BTREE 45 PRIMARY KEY (`id`) USING BTREE
46 -) ENGINE=InnoDB AUTO_INCREMENT=1318 DEFAULT CHARSET=utf8mb4 COMMENT='API 访问日志表'; 46 +) ENGINE=InnoDB AUTO_INCREMENT=1909 DEFAULT CHARSET=utf8mb4 COMMENT='API 访问日志表';
47 47
48 -- ---------------------------- 48 -- ----------------------------
49 -- Records of inf_api_access_log 49 -- Records of inf_api_access_log
@@ -84,7 +84,7 @@ CREATE TABLE `inf_api_error_log` ( @@ -84,7 +84,7 @@ CREATE TABLE `inf_api_error_log` (
84 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', 84 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´',
85 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤', 85 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤',
86 PRIMARY KEY (`id`) USING BTREE 86 PRIMARY KEY (`id`) USING BTREE
87 -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COMMENT='系统异常日志'; 87 +) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COMMENT='系统异常日志';
88 88
89 -- ---------------------------- 89 -- ----------------------------
90 -- Records of inf_api_error_log 90 -- Records of inf_api_error_log
@@ -201,7 +201,7 @@ CREATE TABLE `inf_job_log` ( @@ -201,7 +201,7 @@ CREATE TABLE `inf_job_log` (
201 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', 201 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´',
202 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤', 202 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤',
203 PRIMARY KEY (`id`) USING BTREE 203 PRIMARY KEY (`id`) USING BTREE
204 -) ENGINE=InnoDB AUTO_INCREMENT=627 DEFAULT CHARSET=utf8mb4 COMMENT='定时任务日志表'; 204 +) ENGINE=InnoDB AUTO_INCREMENT=1035 DEFAULT CHARSET=utf8mb4 COMMENT='定时任务日志表';
205 205
206 -- ---------------------------- 206 -- ----------------------------
207 -- Records of inf_job_log 207 -- Records of inf_job_log
@@ -264,7 +264,7 @@ CREATE TABLE `sys_dict_data` ( @@ -264,7 +264,7 @@ CREATE TABLE `sys_dict_data` (
264 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', 264 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´',
265 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤', 265 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤',
266 PRIMARY KEY (`id`) USING BTREE 266 PRIMARY KEY (`id`) USING BTREE
267 -) ENGINE=InnoDB AUTO_INCREMENT=65 DEFAULT CHARSET=utf8mb4 COMMENT='字典数æ®è¡¨'; 267 +) ENGINE=InnoDB AUTO_INCREMENT=70 DEFAULT CHARSET=utf8mb4 COMMENT='字典数æ®è¡¨';
268 268
269 -- ---------------------------- 269 -- ----------------------------
270 -- Records of sys_dict_data 270 -- Records of sys_dict_data
@@ -324,6 +324,11 @@ INSERT INTO `sys_dict_data` VALUES (61, 2, &#39;管ç†å‘˜&#39;, &#39;2&#39;, &#39;user_type&#39;, 0, NUL @@ -324,6 +324,11 @@ INSERT INTO `sys_dict_data` VALUES (61, 2, &#39;管ç†å‘˜&#39;, &#39;2&#39;, &#39;user_type&#39;, 0, NUL
324 INSERT INTO `sys_dict_data` VALUES (62, 0, '未处ç†', '0', 'inf_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:19', '', '2021-02-26 08:11:23', b'0'); 324 INSERT INTO `sys_dict_data` VALUES (62, 0, '未处ç†', '0', 'inf_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:19', '', '2021-02-26 08:11:23', b'0');
325 INSERT INTO `sys_dict_data` VALUES (63, 1, '已处ç†', '1', 'inf_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:26', '', '2021-02-26 08:11:29', b'0'); 325 INSERT INTO `sys_dict_data` VALUES (63, 1, '已处ç†', '1', 'inf_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:26', '', '2021-02-26 08:11:29', b'0');
326 INSERT INTO `sys_dict_data` VALUES (64, 2, '已忽略', '2', 'inf_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:34', '', '2021-02-26 08:11:34', b'0'); 326 INSERT INTO `sys_dict_data` VALUES (64, 2, '已忽略', '2', 'inf_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:34', '', '2021-02-26 08:11:34', b'0');
  327 +INSERT INTO `sys_dict_data` VALUES (65, 0, '云片', 'YUN_PIAN', 'sys_sms_channel_code', 0, NULL, '1', '2021-04-05 01:05:14', '1', '2021-04-05 01:05:14', b'0');
  328 +INSERT INTO `sys_dict_data` VALUES (66, 0, '阿里云', 'ALIYUN', 'sys_sms_channel_code', 0, NULL, '1', '2021-04-05 01:05:26', '1', '2021-04-05 01:05:26', b'0');
  329 +INSERT INTO `sys_dict_data` VALUES (67, 1, '验è¯ç ', '1', 'sys_sms_template_type', 0, NULL, '1', '2021-04-05 21:50:57', '1', '2021-04-05 21:50:57', b'0');
  330 +INSERT INTO `sys_dict_data` VALUES (68, 2, '通知', '2', 'sys_sms_template_type', 0, NULL, '1', '2021-04-05 21:51:08', '1', '2021-04-05 21:51:08', b'0');
  331 +INSERT INTO `sys_dict_data` VALUES (69, 0, 'è¥é”€', '3', 'sys_sms_template_type', 0, NULL, '1', '2021-04-05 21:51:15', '1', '2021-04-05 21:51:15', b'0');
327 COMMIT; 332 COMMIT;
328 333
329 -- ---------------------------- 334 -- ----------------------------
@@ -343,7 +348,7 @@ CREATE TABLE `sys_dict_type` ( @@ -343,7 +348,7 @@ CREATE TABLE `sys_dict_type` (
343 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤', 348 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤',
344 PRIMARY KEY (`id`) USING BTREE, 349 PRIMARY KEY (`id`) USING BTREE,
345 UNIQUE KEY `dict_type` (`type`) 350 UNIQUE KEY `dict_type` (`type`)
346 -) ENGINE=InnoDB AUTO_INCREMENT=111 DEFAULT CHARSET=utf8mb4 COMMENT='字典类型表'; 351 +) ENGINE=InnoDB AUTO_INCREMENT=113 DEFAULT CHARSET=utf8mb4 COMMENT='字典类型表';
347 352
348 -- ---------------------------- 353 -- ----------------------------
349 -- Records of sys_dict_type 354 -- Records of sys_dict_type
@@ -366,6 +371,8 @@ INSERT INTO `sys_dict_type` VALUES (107, &#39;定时任务状æ€&#39;, &#39;inf_job_status&#39;, @@ -366,6 +371,8 @@ INSERT INTO `sys_dict_type` VALUES (107, &#39;定时任务状æ€&#39;, &#39;inf_job_status&#39;,
366 INSERT INTO `sys_dict_type` VALUES (108, '定时任务日志状æ€', 'inf_job_log_status', 0, NULL, '', '2021-02-08 10:03:51', '', '2021-02-08 10:03:51', b'0'); 371 INSERT INTO `sys_dict_type` VALUES (108, '定时任务日志状æ€', 'inf_job_log_status', 0, NULL, '', '2021-02-08 10:03:51', '', '2021-02-08 10:03:51', b'0');
367 INSERT INTO `sys_dict_type` VALUES (109, '用户类型', 'user_type', 0, NULL, '', '2021-02-26 00:15:51', '', '2021-02-26 00:15:51', b'0'); 372 INSERT INTO `sys_dict_type` VALUES (109, '用户类型', 'user_type', 0, NULL, '', '2021-02-26 00:15:51', '', '2021-02-26 00:15:51', b'0');
368 INSERT INTO `sys_dict_type` VALUES (110, 'API 异常数æ®çš„处ç†çжæ€', 'inf_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:01', '', '2021-02-26 07:07:01', b'0'); 373 INSERT INTO `sys_dict_type` VALUES (110, 'API 异常数æ®çš„处ç†çжæ€', 'inf_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:01', '', '2021-02-26 07:07:01', b'0');
  374 +INSERT INTO `sys_dict_type` VALUES (111, '短信渠é“ç¼–ç ', 'sys_sms_channel_code', 0, NULL, '1', '2021-04-05 01:04:50', '1', '2021-04-05 01:04:50', b'0');
  375 +INSERT INTO `sys_dict_type` VALUES (112, '短信模æ¿çš„类型', 'sys_sms_template_type', 0, NULL, '1', '2021-04-05 21:50:43', '1', '2021-04-05 21:50:43', b'0');
369 COMMIT; 376 COMMIT;
370 377
371 -- ---------------------------- 378 -- ----------------------------
@@ -386,7 +393,7 @@ CREATE TABLE `sys_login_log` ( @@ -386,7 +393,7 @@ CREATE TABLE `sys_login_log` (
386 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', 393 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´',
387 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤', 394 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤',
388 PRIMARY KEY (`id`) USING BTREE 395 PRIMARY KEY (`id`) USING BTREE
389 -) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8mb4 COMMENT='系统访问记录'; 396 +) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8mb4 COMMENT='系统访问记录';
390 397
391 -- ---------------------------- 398 -- ----------------------------
392 -- Records of sys_login_log 399 -- Records of sys_login_log
@@ -415,7 +422,7 @@ CREATE TABLE `sys_menu` ( @@ -415,7 +422,7 @@ CREATE TABLE `sys_menu` (
415 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', 422 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´',
416 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤', 423 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤',
417 PRIMARY KEY (`id`) USING BTREE 424 PRIMARY KEY (`id`) USING BTREE
418 -) ENGINE=InnoDB AUTO_INCREMENT=1093 DEFAULT CHARSET=utf8mb4 COMMENT='èœå•æƒé™è¡¨'; 425 +) ENGINE=InnoDB AUTO_INCREMENT=1100 DEFAULT CHARSET=utf8mb4 COMMENT='èœå•æƒé™è¡¨';
419 426
420 -- ---------------------------- 427 -- ----------------------------
421 -- Records of sys_menu 428 -- Records of sys_menu
@@ -530,6 +537,12 @@ INSERT INTO `sys_menu` VALUES (1089, &#39;日志查询&#39;, &#39;infra:api-error-log:query&#39; @@ -530,6 +537,12 @@ INSERT INTO `sys_menu` VALUES (1089, &#39;日志查询&#39;, &#39;infra:api-error-log:query&#39;
530 INSERT INTO `sys_menu` VALUES (1090, '文件管ç†', '', 2, 0, 2, 'file', 'upload', 'infra/file/index', 0, '', '2021-03-12 20:16:20', '1', '2021-03-13 11:07:05', b'0'); 537 INSERT INTO `sys_menu` VALUES (1090, '文件管ç†', '', 2, 0, 2, 'file', 'upload', 'infra/file/index', 0, '', '2021-03-12 20:16:20', '1', '2021-03-13 11:07:05', b'0');
531 INSERT INTO `sys_menu` VALUES (1091, '文件查询', 'infra:file:query', 3, 1, 1090, '', '', '', 0, '', '2021-03-12 20:16:20', '', '2021-03-12 20:16:20', b'0'); 538 INSERT INTO `sys_menu` VALUES (1091, '文件查询', 'infra:file:query', 3, 1, 1090, '', '', '', 0, '', '2021-03-12 20:16:20', '', '2021-03-12 20:16:20', b'0');
532 INSERT INTO `sys_menu` VALUES (1092, '文件删除', 'infra:file:delete', 3, 4, 1090, '', '', '', 0, '', '2021-03-12 20:16:20', '', '2021-03-12 20:16:20', b'0'); 539 INSERT INTO `sys_menu` VALUES (1092, '文件删除', 'infra:file:delete', 3, 4, 1090, '', '', '', 0, '', '2021-03-12 20:16:20', '', '2021-03-12 20:16:20', b'0');
  540 +INSERT INTO `sys_menu` VALUES (1093, '短信管ç†', '', 1, 11, 1, 'sms', 'validCode', NULL, 0, '1', '2021-04-05 01:10:16', '1', '2021-04-05 01:11:38', b'0');
  541 +INSERT INTO `sys_menu` VALUES (1094, '短信渠é“', '', 2, 0, 1093, 'sms-channel', '', 'system/sms/smsChannel', 0, '', '2021-04-01 11:07:15', '1', '2021-04-05 20:32:53', b'0');
  542 +INSERT INTO `sys_menu` VALUES (1095, 'çŸ­ä¿¡æ¸ é“æŸ¥è¯¢', 'system:sms-channel:query', 3, 1, 1094, '', '', '', 0, '', '2021-04-01 11:07:15', '', '2021-04-01 11:07:15', b'0');
  543 +INSERT INTO `sys_menu` VALUES (1096, '短信渠é“创建', 'system:sms-channel:create', 3, 2, 1094, '', '', '', 0, '', '2021-04-01 11:07:15', '', '2021-04-01 11:07:15', b'0');
  544 +INSERT INTO `sys_menu` VALUES (1097, 'çŸ­ä¿¡æ¸ é“æ›´æ–°', 'system:sms-channel:update', 3, 3, 1094, '', '', '', 0, '', '2021-04-01 11:07:15', '', '2021-04-01 11:07:15', b'0');
  545 +INSERT INTO `sys_menu` VALUES (1098, '短信渠é“删除', 'system:sms-channel:delete', 3, 4, 1094, '', '', '', 0, '', '2021-04-01 11:07:15', '', '2021-04-01 11:07:15', b'0');
533 COMMIT; 546 COMMIT;
534 547
535 -- ---------------------------- 548 -- ----------------------------
@@ -589,7 +602,7 @@ CREATE TABLE `sys_operate_log` ( @@ -589,7 +602,7 @@ CREATE TABLE `sys_operate_log` (
589 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', 602 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´',
590 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤', 603 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤',
591 PRIMARY KEY (`id`) USING BTREE 604 PRIMARY KEY (`id`) USING BTREE
592 -) ENGINE=InnoDB AUTO_INCREMENT=65 DEFAULT CHARSET=utf8mb4 COMMENT='æ“作日志记录'; 605 +) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8mb4 COMMENT='æ“作日志记录';
593 606
594 -- ---------------------------- 607 -- ----------------------------
595 -- Records of sys_operate_log 608 -- Records of sys_operate_log
@@ -835,6 +848,118 @@ INSERT INTO `sys_role_menu` VALUES (238, 101, 1065, &#39;&#39;, &#39;2021-01-21 03:23:27&#39;, &#39; @@ -835,6 +848,118 @@ INSERT INTO `sys_role_menu` VALUES (238, 101, 1065, &#39;&#39;, &#39;2021-01-21 03:23:27&#39;, &#39;
835 COMMIT; 848 COMMIT;
836 849
837 -- ---------------------------- 850 -- ----------------------------
  851 +-- Table structure for sys_sms_channel
  852 +-- ----------------------------
  853 +DROP TABLE IF EXISTS `sys_sms_channel`;
  854 +CREATE TABLE `sys_sms_channel` (
  855 + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ç¼–å·',
  856 + `signature` varchar(10) NOT NULL COMMENT '短信签å',
  857 + `code` varchar(63) NOT NULL COMMENT '渠é“ç¼–ç ',
  858 + `status` tinyint(4) NOT NULL COMMENT 'å¼€å¯çжæ€',
  859 + `remark` varchar(255) DEFAULT NULL COMMENT '备注',
  860 + `api_key` varchar(63) NOT NULL COMMENT '短信 API 的账å·',
  861 + `api_secret` varchar(63) DEFAULT NULL COMMENT '短信 API 的秘钥',
  862 + `callback_url` varchar(255) DEFAULT NULL COMMENT '短信å‘é€å›žè°ƒ URL',
  863 + `creator` varchar(64) DEFAULT '' COMMENT '创建者',
  864 + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  865 + `updater` varchar(64) DEFAULT '' COMMENT '更新者',
  866 + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´',
  867 + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤',
  868 + PRIMARY KEY (`id`) USING BTREE
  869 +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COMMENT='短信渠é“';
  870 +
  871 +-- ----------------------------
  872 +-- Records of sys_sms_channel
  873 +-- ----------------------------
  874 +BEGIN;
  875 +INSERT INTO `sys_sms_channel` VALUES (1, '芋é“', 'YUN_PIAN', 0, '呵呵呵哒', '1555a14277cb8a608cf45a9e6a80d510', NULL, 'http://java.nat300.top/api/system/sms/callback/sms/yunpian', '', '2021-03-31 06:12:20', '1', '2021-04-05 21:02:38', b'0');
  876 +INSERT INTO `sys_sms_channel` VALUES (2, 'Ballcat', 'ALIYUN', 0, '啦啦啦', 'LTAI5tCnKso2uG3kJ5gRav88', 'fGJ5SNXL7P1NHNRmJ7DJaMJGPyE55C', NULL, '', '2021-03-31 11:53:10', '1', '2021-04-05 21:02:44', b'0');
  877 +INSERT INTO `sys_sms_channel` VALUES (3, '测试', 'YUN_PIAN', 0, '哈哈哈', '23132', NULL, 'http://www.baidu.com', '1', '2021-04-05 21:10:34', '1', '2021-04-05 21:10:34', b'0');
  878 +COMMIT;
  879 +
  880 +-- ----------------------------
  881 +-- Table structure for sys_sms_log
  882 +-- ----------------------------
  883 +DROP TABLE IF EXISTS `sys_sms_log`;
  884 +CREATE TABLE `sys_sms_log` (
  885 + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ç¼–å·',
  886 + `channel_id` bigint(20) NOT NULL COMMENT '短信渠é“ç¼–å·',
  887 + `channel_code` varchar(63) NOT NULL COMMENT '短信渠é“ç¼–ç ',
  888 + `template_id` bigint(20) NOT NULL COMMENT '模æ¿ç¼–å·',
  889 + `template_code` varchar(63) NOT NULL COMMENT '模æ¿ç¼–ç ',
  890 + `template_type` tinyint(4) NOT NULL COMMENT '短信类型',
  891 + `template_content` varchar(255) NOT NULL COMMENT '短信内容',
  892 + `template_params` varchar(255) NOT NULL COMMENT 'çŸ­ä¿¡å‚æ•°',
  893 + `api_template_id` varchar(63) NOT NULL COMMENT '短信 API 的模æ¿ç¼–å·',
  894 + `mobile` varchar(11) NOT NULL COMMENT '手机å·',
  895 + `user_id` bigint(20) DEFAULT '0' COMMENT '用户编å·',
  896 + `user_type` tinyint(4) DEFAULT '0' COMMENT '用户类型',
  897 + `send_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT 'å‘é€çжæ€',
  898 + `send_time` datetime DEFAULT NULL COMMENT 'å‘逿—¶é—´',
  899 + `send_code` int(11) DEFAULT NULL COMMENT 'å‘é€ç»“果的编ç ',
  900 + `send_msg` varchar(255) DEFAULT NULL COMMENT 'å‘é€ç»“果的æç¤º',
  901 + `api_send_code` varchar(63) DEFAULT NULL COMMENT '短信 API å‘é€ç»“果的编ç ',
  902 + `api_send_msg` varchar(255) DEFAULT NULL COMMENT '短信 API å‘é€å¤±è´¥çš„æç¤º',
  903 + `api_request_id` varchar(255) DEFAULT NULL COMMENT '短信 API å‘é€è¿”回的唯一请求 ID',
  904 + `api_serial_no` varchar(255) DEFAULT NULL COMMENT '短信 API å‘é€è¿”回的åºå·',
  905 + `receive_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '接收状æ€',
  906 + `receive_time` datetime DEFAULT NULL COMMENT '接收时间',
  907 + `api_receive_code` varchar(63) DEFAULT NULL COMMENT 'API 接收结果的编ç ',
  908 + `api_receive_msg` varchar(255) DEFAULT NULL COMMENT 'API 接收结果的说明',
  909 + `creator` varchar(64) DEFAULT '' COMMENT '创建者',
  910 + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  911 + `updater` varchar(64) DEFAULT '' COMMENT '更新者',
  912 + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´',
  913 + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤',
  914 + PRIMARY KEY (`id`) USING BTREE
  915 +) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8mb4 COMMENT='短信moan';
  916 +
  917 +-- ----------------------------
  918 +-- Records of sys_sms_log
  919 +-- ----------------------------
  920 +BEGIN;
  921 +INSERT INTO `sys_sms_log` VALUES (15, 1, 'YUN_PIAN', 2, 'test_01', 1, '正在进行登录æ“作登陆,您的验è¯ç æ˜¯1234', '{\"code\":\"1234\",\"operation\":\"登陆\"}', '4383920', '15601691399', 1, 2, 10, '2021-04-04 23:24:13', 0, 'æˆåŠŸ', '0', 'å‘逿ˆåŠŸ', NULL, '62922707786', 10, '2021-04-04 23:24:26', 'DELIVRD', 'DELIVRD', NULL, '2021-04-04 23:23:29', NULL, '2021-04-04 23:25:17', b'0');
  922 +INSERT INTO `sys_sms_log` VALUES (16, 2, 'ALIYUN', 3, 'test_02', 1, '您的验è¯ç 1234,该验è¯ç 5分钟内有效,请勿泄æ¼äºŽä»–人ï¼', '{\"code\":\"1234\"}', 'SMS_207945135', '15601691399', 1, 2, 20, '2021-04-05 00:08:39', 999, '未知错误,需è¦è§£æž', 'SDK.InvalidAccessKeySecret', 'SDK.InvalidAccessKeySecret : Specified Access Key Secret is not valid.\r\nRequestId : 2EA9C6A5-579F-4D21-B7D3-0AD3BA4F7741', '2EA9C6A5-579F-4D21-B7D3-0AD3BA4F7741', NULL, 0, NULL, NULL, NULL, NULL, '2021-04-05 00:08:39', NULL, '2021-04-05 00:08:39', b'0');
  923 +INSERT INTO `sys_sms_log` VALUES (17, 2, 'ALIYUN', 3, 'test_02', 1, '您的验è¯ç 1234,该验è¯ç 5分钟内有效,请勿泄æ¼äºŽä»–人ï¼', '{\"code\":\"1234\"}', 'SMS_207945135', '15601691399', 1, 2, 20, '2021-04-05 00:09:43', 999, '未知错误,需è¦è§£æž', 'SDK.InvalidAccessKeySecret', 'SDK.InvalidAccessKeySecret : Specified Access Key Secret is not valid.\r\nRequestId : BF766164-9C03-44FD-B6D3-ADA74118E432', 'BF766164-9C03-44FD-B6D3-ADA74118E432', NULL, 0, NULL, NULL, NULL, NULL, '2021-04-05 00:09:43', NULL, '2021-04-05 00:09:43', b'0');
  924 +INSERT INTO `sys_sms_log` VALUES (18, 2, 'ALIYUN', 3, 'test_02', 1, '您的验è¯ç 1234,该验è¯ç 5分钟内有效,请勿泄æ¼äºŽä»–人ï¼', '{\"code\":\"1234\"}', 'SMS_207945135', '15601691399', 1, 2, 20, '2021-04-05 00:11:13', 999, '未知错误,需è¦è§£æž', 'SDK.InvalidAccessKeySecret', 'SDK.InvalidAccessKeySecret : Specified Access Key Secret is not valid.\r\nRequestId : 2D7C0ABC-7538-45B4-BFEF-B610D591CE3D', '2D7C0ABC-7538-45B4-BFEF-B610D591CE3D', NULL, 0, NULL, NULL, NULL, NULL, '2021-04-05 00:11:12', NULL, '2021-04-05 00:11:13', b'0');
  925 +INSERT INTO `sys_sms_log` VALUES (19, 2, 'ALIYUN', 3, 'test_02', 1, '您的验è¯ç 1234,该验è¯ç 5分钟内有效,请勿泄æ¼äºŽä»–人ï¼', '{\"code\":\"1234\"}', 'SMS_207945135', '15601691399', 1, 2, 20, '2021-04-05 00:12:21', 999, '未知错误,需è¦è§£æž', 'SDK.InvalidAccessKeySecret', 'SDK.InvalidAccessKeySecret : Specified Access Key Secret is not valid.\r\nRequestId : 0A86DC5C-2985-474F-B076-748C9F2C5D3F', '0A86DC5C-2985-474F-B076-748C9F2C5D3F', NULL, 0, NULL, NULL, NULL, NULL, '2021-04-05 00:12:01', NULL, '2021-04-05 00:12:21', b'0');
  926 +INSERT INTO `sys_sms_log` VALUES (20, 1, 'YUN_PIAN', 2, 'test_01', 1, '正在进行登录æ“作登陆,您的验è¯ç æ˜¯1234', '{\"code\":\"1234\",\"operation\":\"登陆\"}', '4383920', '15601691399', 1, 2, 10, '2021-04-05 00:14:36', 0, 'æˆåŠŸ', '0', 'å‘逿ˆåŠŸ', NULL, '62923244790', 0, NULL, NULL, NULL, NULL, '2021-04-05 00:13:42', NULL, '2021-04-05 00:14:36', b'0');
  927 +INSERT INTO `sys_sms_log` VALUES (21, 2, 'ALIYUN', 3, 'test_02', 1, '您的验è¯ç 1234,该验è¯ç 5分钟内有效,请勿泄æ¼äºŽä»–人ï¼', '{\"code\":\"1234\"}', 'SMS_207945135', '15601691399', 1, 2, 20, '2021-04-05 00:19:43', 999, '未知错误,需è¦è§£æž', 'SDK.InvalidAccessKeySecret', 'SDK.InvalidAccessKeySecret : Specified Access Key Secret is not valid.\r\nRequestId : 3837C6D3-B96F-428C-BBB2-86135D4B5B99', '3837C6D3-B96F-428C-BBB2-86135D4B5B99', NULL, 0, NULL, NULL, NULL, NULL, '2021-04-05 00:15:06', NULL, '2021-04-05 00:19:44', b'0');
  928 +COMMIT;
  929 +
  930 +-- ----------------------------
  931 +-- Table structure for sys_sms_template
  932 +-- ----------------------------
  933 +DROP TABLE IF EXISTS `sys_sms_template`;
  934 +CREATE TABLE `sys_sms_template` (
  935 + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ç¼–å·',
  936 + `type` tinyint(4) NOT NULL COMMENT '短信签å',
  937 + `status` tinyint(4) NOT NULL COMMENT 'å¼€å¯çжæ€',
  938 + `code` varchar(63) NOT NULL COMMENT '模æ¿ç¼–ç ',
  939 + `name` varchar(63) NOT NULL COMMENT '模æ¿åç§°',
  940 + `content` varchar(255) NOT NULL COMMENT '模æ¿å†…容',
  941 + `params` varchar(255) NOT NULL COMMENT '傿•°æ•°ç»„',
  942 + `remark` varchar(255) DEFAULT NULL COMMENT '备注',
  943 + `api_template_id` varchar(63) NOT NULL COMMENT '短信 API 的模æ¿ç¼–å·',
  944 + `channel_id` bigint(20) NOT NULL COMMENT '短信渠é“ç¼–å·',
  945 + `channel_code` varchar(63) NOT NULL COMMENT '短信渠é“ç¼–ç ',
  946 + `creator` varchar(64) DEFAULT '' COMMENT '创建者',
  947 + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  948 + `updater` varchar(64) DEFAULT '' COMMENT '更新者',
  949 + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´',
  950 + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤',
  951 + PRIMARY KEY (`id`) USING BTREE
  952 +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COMMENT='短信模æ¿';
  953 +
  954 +-- ----------------------------
  955 +-- Records of sys_sms_template
  956 +-- ----------------------------
  957 +BEGIN;
  958 +INSERT INTO `sys_sms_template` VALUES (2, 1, 0, 'test_01', '测试验è¯ç çŸ­ä¿¡', '正在进行登录æ“作{operation},您的验è¯ç æ˜¯{code}', '[\"operation\",\"code\"]', NULL, '4383920', 1, 'YUN_PIAN', '', '2021-03-31 10:49:38', '', '2021-03-31 12:01:38', b'0');
  959 +INSERT INTO `sys_sms_template` VALUES (3, 1, 0, 'test_02', '公告通知', '您的验è¯ç {code},该验è¯ç 5分钟内有效,请勿泄æ¼äºŽä»–人ï¼', '[\"code\"]', NULL, 'SMS_207945135', 2, 'ALIYUN', '', '2021-03-31 11:56:30', '', '2021-03-31 11:56:30', b'0');
  960 +COMMIT;
  961 +
  962 +-- ----------------------------
838 -- Table structure for sys_user 963 -- Table structure for sys_user
839 -- ---------------------------- 964 -- ----------------------------
840 DROP TABLE IF EXISTS `sys_user`; 965 DROP TABLE IF EXISTS `sys_user`;
@@ -865,8 +990,8 @@ CREATE TABLE `sys_user` ( @@ -865,8 +990,8 @@ CREATE TABLE `sys_user` (
865 -- Records of sys_user 990 -- Records of sys_user
866 -- ---------------------------- 991 -- ----------------------------
867 BEGIN; 992 BEGIN;
868 -INSERT INTO `sys_user` VALUES (1, 'admin', '$2a$10$0acJOIk2D25/oC87nyclE..0lzeu9DtQ/n3geP4fkun/zIVRhHJIO', '芋é“', '管ç†å‘˜', 103, '[1]', 'aoteman@126.com', '15612345678', 1, 'http://127.0.0.1:8080/api/system/file/get/add5ec1891a7d97d2cc1d60847e16294.jpg', 0, '127.0.0.1', '2021-01-05 17:03:47', 'admin', '2021-01-05 17:03:47', '1', '2021-03-21 18:16:16', b'0');  
869 -INSERT INTO `sys_user` VALUES (2, 'ry', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', 'è‹¥ä¾', '测试员', 105, '[2]', 'ry@qq.com', '15666666666', 1, '', 0, '127.0.0.1', '2021-01-05 17:03:47', 'admin', '2021-01-05 17:03:47', '', '2021-01-05 17:03:47', b'0'); 993 +INSERT INTO `sys_user` VALUES (1, 'admin', '$2a$10$0acJOIk2D25/oC87nyclE..0lzeu9DtQ/n3geP4fkun/zIVRhHJIO', 'èŠ‹é“æºç ', '管ç†å‘˜', 103, '[1]', 'aoteman@126.com', '15612345678', 1, 'http://api-dashboard.yudao.iocoder.cn/api/infra/file/get/5e8609290e915c4fa8b08e67.jpg', 0, '127.0.0.1', '2021-01-05 17:03:47', 'admin', '2021-01-05 17:03:47', '1', '2021-04-05 02:16:10', b'0');
  994 +INSERT INTO `sys_user` VALUES (2, 'ry', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', 'è‹¥ä¾', '测试员', 105, '[2]', 'ry@qq.com', '15666666666', 1, '', 0, '127.0.0.1', '2021-01-05 17:03:47', 'admin', '2021-01-05 17:03:47', '', '2021-04-01 04:50:36', b'1');
870 INSERT INTO `sys_user` VALUES (100, 'yudao', '$2a$10$11U48RhyJ5pSBYWSn12AD./ld671.ycSzJHbyrtpeoMeYiw31eo8a', '芋é“', 'ä¸è¦å“我', 100, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 1, '', NULL, '', '2021-01-07 09:07:17', '1', '2021-03-14 22:35:17', b'0'); 995 INSERT INTO `sys_user` VALUES (100, 'yudao', '$2a$10$11U48RhyJ5pSBYWSn12AD./ld671.ycSzJHbyrtpeoMeYiw31eo8a', '芋é“', 'ä¸è¦å“我', 100, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 1, '', NULL, '', '2021-01-07 09:07:17', '1', '2021-03-14 22:35:17', b'0');
871 INSERT INTO `sys_user` VALUES (103, 'yuanma', '', 'æºç ', NULL, 100, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '', NULL, '', '2021-01-13 23:50:35', '', '2021-01-13 23:50:35', b'0'); 996 INSERT INTO `sys_user` VALUES (103, 'yuanma', '', 'æºç ', NULL, 100, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '', NULL, '', '2021-01-13 23:50:35', '', '2021-01-13 23:50:35', b'0');
872 INSERT INTO `sys_user` VALUES (104, 'test', '$2a$10$.TOFpaIiI3PzEwkGrNq0Eu6Cc3rOqJMxTb1DqeSEM8StxaGPBRKoi', '测试å·', NULL, 100, '[]', '', '15601691200', 1, '', 0, '', NULL, '', '2021-01-21 02:13:53', '1', '2021-03-14 22:36:38', b'0'); 997 INSERT INTO `sys_user` VALUES (104, 'test', '$2a$10$.TOFpaIiI3PzEwkGrNq0Eu6Cc3rOqJMxTb1DqeSEM8StxaGPBRKoi', '测试å·', NULL, 100, '[]', '', '15601691200', 1, '', 0, '', NULL, '', '2021-01-21 02:13:53', '1', '2021-03-14 22:36:38', b'0');
@@ -924,11 +1049,17 @@ CREATE TABLE `sys_user_session` ( @@ -924,11 +1049,17 @@ CREATE TABLE `sys_user_session` (
924 -- Records of sys_user_session 1049 -- Records of sys_user_session
925 -- ---------------------------- 1050 -- ----------------------------
926 BEGIN; 1051 BEGIN;
  1052 +INSERT INTO `sys_user_session` VALUES ('04c6624c7bf14b1ba1a01cb976a9d876', 1, '2021-04-05 21:40:12', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-05 20:21:09', NULL, '2021-04-01 12:25:35', b'1');
  1053 +INSERT INTO `sys_user_session` VALUES ('0e235ce5ae7342a09b372a00bd7d1b41', 1, '2021-04-05 01:43:22', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-05 00:51:03', NULL, '2021-04-01 04:18:06', b'1');
  1054 +INSERT INTO `sys_user_session` VALUES ('0e6943f8ca9b4215a014843eb489ccc7', 1, '2021-04-05 22:53:22', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-05 21:43:59', NULL, '2021-04-05 22:23:22', b'0');
  1055 +INSERT INTO `sys_user_session` VALUES ('40d532d8900c43b791266429a7911751', 1, '2021-04-05 22:11:34', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-05 21:41:34', NULL, '2021-04-01 12:28:20', b'1');
927 INSERT INTO `sys_user_session` VALUES ('505b4e7d8b0d4b40aa23bf540da81234', 1, '2021-03-14 01:25:13', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-14 00:31:43', NULL, '2021-03-13 07:35:26', b'1'); 1056 INSERT INTO `sys_user_session` VALUES ('505b4e7d8b0d4b40aa23bf540da81234', 1, '2021-03-14 01:25:13', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-14 00:31:43', NULL, '2021-03-13 07:35:26', b'1');
928 INSERT INTO `sys_user_session` VALUES ('5a7248bf87d14e7e9f0578b05969986c', 1, '2021-03-13 10:42:50', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-13 09:37:36', NULL, '2021-03-12 19:53:07', b'1'); 1057 INSERT INTO `sys_user_session` VALUES ('5a7248bf87d14e7e9f0578b05969986c', 1, '2021-03-13 10:42:50', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-13 09:37:36', NULL, '2021-03-12 19:53:07', b'1');
  1058 +INSERT INTO `sys_user_session` VALUES ('8b3eac5e4a104a4191c8070e03d553ea', 1, '2021-04-05 02:45:12', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-05 02:15:12', NULL, '2021-04-01 11:05:25', b'1');
929 INSERT INTO `sys_user_session` VALUES ('9ae27346d8b7491aad1385f51e8aa196', 1, '2021-03-13 14:02:12', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-13 10:43:06', NULL, '2021-03-13 06:40:35', b'1'); 1059 INSERT INTO `sys_user_session` VALUES ('9ae27346d8b7491aad1385f51e8aa196', 1, '2021-03-13 14:02:12', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-13 10:43:06', NULL, '2021-03-13 06:40:35', b'1');
930 INSERT INTO `sys_user_session` VALUES ('ae9ee7452ee54e4b983d658188c15c4d', 1, '2021-03-14 21:32:57', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-14 20:25:00', NULL, '2021-03-13 15:19:10', b'1'); 1060 INSERT INTO `sys_user_session` VALUES ('ae9ee7452ee54e4b983d658188c15c4d', 1, '2021-03-14 21:32:57', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-14 20:25:00', NULL, '2021-03-13 15:19:10', b'1');
931 -INSERT INTO `sys_user_session` VALUES ('d0adf48f82914212b947e5ab04d9fb65', 1, '2021-03-21 19:16:28', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-03-21 18:13:37', NULL, '2021-03-21 18:46:28', b'0'); 1061 +INSERT INTO `sys_user_session` VALUES ('d0adf48f82914212b947e5ab04d9fb65', 1, '2021-03-21 19:16:28', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-03-21 18:13:37', NULL, '2021-03-15 05:53:20', b'1');
  1062 +INSERT INTO `sys_user_session` VALUES ('e80c2400724042a2ab73732166cde8fc', 1, '2021-03-21 21:17:12', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-03-21 20:47:12', NULL, '2021-03-15 08:18:56', b'1');
932 INSERT INTO `sys_user_session` VALUES ('e8872f5192584440a548641b83c877ef', 1, '2021-03-21 18:36:01', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-03-21 17:51:48', NULL, '2021-03-15 03:54:20', b'1'); 1063 INSERT INTO `sys_user_session` VALUES ('e8872f5192584440a548641b83c877ef', 1, '2021-03-21 18:36:01', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-03-21 17:51:48', NULL, '2021-03-15 03:54:20', b'1');
933 INSERT INTO `sys_user_session` VALUES ('f1ab99b09b5a475795579ff99d60ac78', 1, '2021-03-14 23:04:31', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-14 21:12:44', NULL, '2021-03-15 03:32:38', b'1'); 1064 INSERT INTO `sys_user_session` VALUES ('f1ab99b09b5a475795579ff99d60ac78', 1, '2021-03-14 23:04:31', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-14 21:12:44', NULL, '2021-03-15 03:32:38', b'1');
934 INSERT INTO `sys_user_session` VALUES ('f853b50d064340a581e9a49bba9411fc', 1, '2021-03-10 01:55:41', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-10 01:11:53', NULL, '2021-03-12 18:37:05', b'1'); 1065 INSERT INTO `sys_user_session` VALUES ('f853b50d064340a581e9a49bba9411fc', 1, '2021-03-10 01:55:41', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-10 01:11:53', NULL, '2021-03-12 18:37:05', b'1');
@@ -964,7 +1095,7 @@ CREATE TABLE `tool_codegen_column` ( @@ -964,7 +1095,7 @@ CREATE TABLE `tool_codegen_column` (
964 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', 1095 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´',
965 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤', 1096 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤',
966 PRIMARY KEY (`id`) USING BTREE 1097 PRIMARY KEY (`id`) USING BTREE
967 -) ENGINE=InnoDB AUTO_INCREMENT=389 DEFAULT CHARSET=utf8mb4 COMMENT='代ç ç”Ÿæˆè¡¨å­—段定义'; 1098 +) ENGINE=InnoDB AUTO_INCREMENT=418 DEFAULT CHARSET=utf8mb4 COMMENT='代ç ç”Ÿæˆè¡¨å­—段定义';
968 1099
969 -- ---------------------------- 1100 -- ----------------------------
970 -- Records of tool_codegen_column 1101 -- Records of tool_codegen_column
@@ -1126,6 +1257,35 @@ INSERT INTO `tool_codegen_column` VALUES (385, 33, &#39;create_time&#39;, &#39;datetime&#39;, &#39;å @@ -1126,6 +1257,35 @@ INSERT INTO `tool_codegen_column` VALUES (385, 33, &#39;create_time&#39;, &#39;datetime&#39;, &#39;å
1126 INSERT INTO `tool_codegen_column` VALUES (386, 33, 'updater', 'varchar(64)', '更新者', b'1', b'0', '0', 6, 'String', 'updater', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0'); 1257 INSERT INTO `tool_codegen_column` VALUES (386, 33, 'updater', 'varchar(64)', '更新者', b'1', b'0', '0', 6, 'String', 'updater', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0');
1127 INSERT INTO `tool_codegen_column` VALUES (387, 33, 'update_time', 'datetime', 'æ›´æ–°æ—¶é—´', b'0', b'0', '0', 7, 'Date', 'updateTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'0', 'datetime', '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0'); 1258 INSERT INTO `tool_codegen_column` VALUES (387, 33, 'update_time', 'datetime', 'æ›´æ–°æ—¶é—´', b'0', b'0', '0', 7, 'Date', 'updateTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'0', 'datetime', '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0');
1128 INSERT INTO `tool_codegen_column` VALUES (388, 33, 'deleted', 'bit(1)', '是å¦åˆ é™¤', b'0', b'0', '0', 8, 'Boolean', 'deleted', '', NULL, b'0', b'0', b'0', '=', b'0', 'radio', '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0'); 1259 INSERT INTO `tool_codegen_column` VALUES (388, 33, 'deleted', 'bit(1)', '是å¦åˆ é™¤', b'0', b'0', '0', 8, 'Boolean', 'deleted', '', NULL, b'0', b'0', b'0', '=', b'0', 'radio', '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0');
  1260 +INSERT INTO `tool_codegen_column` VALUES (389, 34, 'id', 'bigint(20)', 'ç¼–å·', b'0', b'1', '1', 1, 'Long', 'id', '', '1024', b'0', b'1', b'0', '=', b'1', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0');
  1261 +INSERT INTO `tool_codegen_column` VALUES (390, 34, 'signature', 'varchar(8)', '短信签å', b'0', b'0', '0', 2, 'String', 'signature', '', 'èŠ‹é“æºç ', b'1', b'1', b'1', '=', b'1', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0');
  1262 +INSERT INTO `tool_codegen_column` VALUES (391, 34, 'code', 'varchar(63)', '渠é“ç¼–ç ', b'0', b'0', '0', 3, 'String', 'code', 'sys_sms_channel_code', 'YUN_PIAN', b'1', b'0', b'0', '=', b'1', 'select', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0');
  1263 +INSERT INTO `tool_codegen_column` VALUES (392, 34, 'status', 'tinyint(4)', 'å¯ç”¨çжæ€', b'0', b'0', '0', 4, 'Integer', 'status', 'sys_common_status', '1', b'1', b'1', b'1', '=', b'1', 'radio', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0');
  1264 +INSERT INTO `tool_codegen_column` VALUES (393, 34, 'remark', 'varchar(255)', '备注', b'1', b'0', '0', 5, 'String', 'remark', '', '好åƒï¼', b'1', b'1', b'0', '=', b'1', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0');
  1265 +INSERT INTO `tool_codegen_column` VALUES (394, 34, 'api_key', 'varchar(63)', '短信 API 的账å·', b'0', b'0', '0', 6, 'String', 'apiKey', '', 'yudao', b'1', b'1', b'0', '=', b'1', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0');
  1266 +INSERT INTO `tool_codegen_column` VALUES (395, 34, 'api_secret', 'varchar(63)', '短信 API 的秘钥', b'1', b'0', '0', 7, 'String', 'apiSecret', '', 'yuanma', b'1', b'1', b'0', '=', b'1', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0');
  1267 +INSERT INTO `tool_codegen_column` VALUES (396, 34, 'callback_url', 'varchar(255)', '短信å‘é€å›žè°ƒ URL', b'1', b'0', '0', 8, 'String', 'callbackUrl', '', 'http://www.iocoder.cn', b'1', b'1', b'0', '=', b'1', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0');
  1268 +INSERT INTO `tool_codegen_column` VALUES (397, 34, 'creator', 'varchar(64)', '创建者', b'1', b'0', '0', 9, 'String', 'creator', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0');
  1269 +INSERT INTO `tool_codegen_column` VALUES (398, 34, 'create_time', 'datetime', '创建时间', b'0', b'0', '0', 10, 'Date', 'createTime', '', NULL, b'0', b'0', b'1', 'BETWEEN', b'1', 'datetime', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0');
  1270 +INSERT INTO `tool_codegen_column` VALUES (399, 34, 'updater', 'varchar(64)', '更新者', b'1', b'0', '0', 11, 'String', 'updater', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0');
  1271 +INSERT INTO `tool_codegen_column` VALUES (400, 34, 'update_time', 'datetime', 'æ›´æ–°æ—¶é—´', b'0', b'0', '0', 12, 'Date', 'updateTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'0', 'datetime', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0');
  1272 +INSERT INTO `tool_codegen_column` VALUES (401, 34, 'deleted', 'bit(1)', '是å¦åˆ é™¤', b'0', b'0', '0', 13, 'Boolean', 'deleted', '', NULL, b'0', b'0', b'0', '=', b'0', 'radio', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0');
  1273 +INSERT INTO `tool_codegen_column` VALUES (402, 35, 'id', 'bigint(20)', 'ç¼–å·', b'0', b'1', '1', 1, 'Long', 'id', '', '1024', b'0', b'1', b'0', '=', b'1', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0');
  1274 +INSERT INTO `tool_codegen_column` VALUES (403, 35, 'type', 'tinyint(4)', '短信签å', b'0', b'0', '0', 2, 'Integer', 'type', 'sys_sms_template_type', '1', b'1', b'1', b'1', '=', b'1', 'select', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0');
  1275 +INSERT INTO `tool_codegen_column` VALUES (404, 35, 'status', 'tinyint(4)', 'å¼€å¯çжæ€', b'0', b'0', '0', 3, 'Integer', 'status', 'sys_common_status', '1', b'1', b'1', b'1', '=', b'1', 'radio', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0');
  1276 +INSERT INTO `tool_codegen_column` VALUES (405, 35, 'code', 'varchar(63)', '模æ¿ç¼–ç ', b'0', b'0', '0', 4, 'String', 'code', '', 'test_01', b'1', b'1', b'1', 'LIKE', b'1', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0');
  1277 +INSERT INTO `tool_codegen_column` VALUES (406, 35, 'name', 'varchar(63)', '模æ¿åç§°', b'0', b'0', '0', 5, 'String', 'name', '', 'yudao', b'1', b'1', b'0', 'LIKE', b'1', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0');
  1278 +INSERT INTO `tool_codegen_column` VALUES (407, 35, 'content', 'varchar(255)', '模æ¿å†…容', b'0', b'0', '0', 6, 'String', 'content', '', '你好,{name}。你长的太{like}啦ï¼', b'1', b'1', b'1', 'LIKE', b'1', 'editor', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0');
  1279 +INSERT INTO `tool_codegen_column` VALUES (408, 35, 'params', 'varchar(255)', '傿•°æ•°ç»„', b'0', b'0', '0', 7, 'String', 'params', '', 'name,code', b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0');
  1280 +INSERT INTO `tool_codegen_column` VALUES (409, 35, 'remark', 'varchar(255)', '备注', b'1', b'0', '0', 8, 'String', 'remark', '', '哈哈哈', b'1', b'1', b'0', '=', b'1', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0');
  1281 +INSERT INTO `tool_codegen_column` VALUES (410, 35, 'api_template_id', 'varchar(63)', '短信 API 的模æ¿ç¼–å·', b'0', b'0', '0', 9, 'String', 'apiTemplateId', '', '4383920', b'1', b'1', b'1', 'LIKE', b'1', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0');
  1282 +INSERT INTO `tool_codegen_column` VALUES (411, 35, 'channel_id', 'bigint(20)', '短信渠é“ç¼–å·', b'0', b'0', '0', 10, 'Long', 'channelId', '', '10', b'1', b'1', b'1', '=', b'1', 'select', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0');
  1283 +INSERT INTO `tool_codegen_column` VALUES (412, 35, 'channel_code', 'varchar(63)', '短信渠é“ç¼–ç ', b'0', b'0', '0', 11, 'String', 'channelCode', 'sys_sms_channel_code', 'ALIYUN', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0');
  1284 +INSERT INTO `tool_codegen_column` VALUES (413, 35, 'creator', 'varchar(64)', '创建者', b'1', b'0', '0', 12, 'String', 'creator', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0');
  1285 +INSERT INTO `tool_codegen_column` VALUES (414, 35, 'create_time', 'datetime', '创建时间', b'0', b'0', '0', 13, 'Date', 'createTime', '', NULL, b'0', b'0', b'1', 'BETWEEN', b'1', 'datetime', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0');
  1286 +INSERT INTO `tool_codegen_column` VALUES (415, 35, 'updater', 'varchar(64)', '更新者', b'1', b'0', '0', 14, 'String', 'updater', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0');
  1287 +INSERT INTO `tool_codegen_column` VALUES (416, 35, 'update_time', 'datetime', 'æ›´æ–°æ—¶é—´', b'0', b'0', '0', 15, 'Date', 'updateTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'0', 'datetime', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0');
  1288 +INSERT INTO `tool_codegen_column` VALUES (417, 35, 'deleted', 'bit(1)', '是å¦åˆ é™¤', b'0', b'0', '0', 16, 'Boolean', 'deleted', '', NULL, b'0', b'0', b'0', '=', b'0', 'radio', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0');
1129 COMMIT; 1289 COMMIT;
1130 1290
1131 -- ---------------------------- 1291 -- ----------------------------
@@ -1151,7 +1311,7 @@ CREATE TABLE `tool_codegen_table` ( @@ -1151,7 +1311,7 @@ CREATE TABLE `tool_codegen_table` (
1151 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', 1311 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´',
1152 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤', 1312 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤',
1153 PRIMARY KEY (`id`) USING BTREE 1313 PRIMARY KEY (`id`) USING BTREE
1154 -) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8mb4 COMMENT='代ç ç”Ÿæˆè¡¨å®šä¹‰'; 1314 +) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8mb4 COMMENT='代ç ç”Ÿæˆè¡¨å®šä¹‰';
1155 1315
1156 -- ---------------------------- 1316 -- ----------------------------
1157 -- Records of tool_codegen_table 1317 -- Records of tool_codegen_table
@@ -1169,6 +1329,8 @@ INSERT INTO `tool_codegen_table` VALUES (28, 1, &#39;sys_dict_type&#39;, &#39;å­—å…¸ç±»åž‹è¡ @@ -1169,6 +1329,8 @@ INSERT INTO `tool_codegen_table` VALUES (28, 1, &#39;sys_dict_type&#39;, &#39;字典类型è¡
1169 INSERT INTO `tool_codegen_table` VALUES (29, 1, 'sys_dict_type', '字典类型表', NULL, 'system', 'dict', 'SysDictType', '字典类型', '芋艿', 1, NULL, '', '2021-03-06 03:52:57', '', '2021-03-06 04:03:52', b'0'); 1329 INSERT INTO `tool_codegen_table` VALUES (29, 1, 'sys_dict_type', '字典类型表', NULL, 'system', 'dict', 'SysDictType', '字典类型', '芋艿', 1, NULL, '', '2021-03-06 03:52:57', '', '2021-03-06 04:03:52', b'0');
1170 INSERT INTO `tool_codegen_table` VALUES (30, 1, 'sys_dict_data', '字典数æ®è¡¨', NULL, 'system', 'type', 'SysDictData', '字典数æ®', 'èŠ‹é“æºç ', 1, NULL, '', '2021-03-06 06:48:28', '', '2021-03-06 06:50:47', b'0'); 1330 INSERT INTO `tool_codegen_table` VALUES (30, 1, 'sys_dict_data', '字典数æ®è¡¨', NULL, 'system', 'type', 'SysDictData', '字典数æ®', 'èŠ‹é“æºç ', 1, NULL, '', '2021-03-06 06:48:28', '', '2021-03-06 06:50:47', b'0');
1171 INSERT INTO `tool_codegen_table` VALUES (33, 1, 'inf_file', '文件表', NULL, 'infra', 'file', 'InfFile', '文件', '芋艿', 1, 2, '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0'); 1331 INSERT INTO `tool_codegen_table` VALUES (33, 1, 'inf_file', '文件表', NULL, 'infra', 'file', 'InfFile', '文件', '芋艿', 1, 2, '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0');
  1332 +INSERT INTO `tool_codegen_table` VALUES (34, 1, 'sys_sms_channel', '短信渠é“', NULL, 'system', 'sms', 'SysSmsChannel', '短信渠é“', 'èŠ‹é“æºç ', 1, 1093, '1', '2021-04-03 13:39:06', '1', '2021-04-05 20:52:09', b'0');
  1333 +INSERT INTO `tool_codegen_table` VALUES (35, 1, 'sys_sms_template', '短信模æ¿', NULL, 'system', 'sms', 'SysSmsTemplate', '短信模æ¿', 'èŠ‹é“æºç ', 1, 1093, '1', '2021-04-03 13:58:55', '1', '2021-04-05 22:23:38', b'0');
1172 COMMIT; 1334 COMMIT;
1173 1335
1174 -- ---------------------------- 1336 -- ----------------------------
src/main/java/cn/iocoder/dashboard/common/core/KeyValue.java 0 → 100644
  1 +package cn.iocoder.dashboard.common.core;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Data;
  5 +import lombok.NoArgsConstructor;
  6 +
  7 +/**
  8 + * Key Value 的键值对
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +@Data
  13 +@NoArgsConstructor
  14 +@AllArgsConstructor
  15 +public class KeyValue<K, V> {
  16 +
  17 + private K key;
  18 + private V value;
  19 +
  20 +}
src/main/java/cn/iocoder/dashboard/common/enums/DefaultBitFieldEnum.java 0 → 100644
  1 +package cn.iocoder.dashboard.common.enums;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * 通用状态枚举
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +@Getter
  12 +@AllArgsConstructor
  13 +public enum DefaultBitFieldEnum {
  14 +
  15 + NO(0, "否"),
  16 + YES(1, "是");
  17 +
  18 + /**
  19 + * 状态值
  20 + */
  21 + private final Integer val;
  22 + /**
  23 + * 状态名
  24 + */
  25 + private final String name;
  26 +
  27 +}
src/main/java/cn/iocoder/dashboard/common/exception/ErrorCode.java
1 package cn.iocoder.dashboard.common.exception; 1 package cn.iocoder.dashboard.common.exception;
2 2
  3 +import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants;
3 import cn.iocoder.dashboard.common.exception.enums.ServiceErrorCodeRange; 4 import cn.iocoder.dashboard.common.exception.enums.ServiceErrorCodeRange;
4 import lombok.Data; 5 import lombok.Data;
5 6
6 /** 7 /**
7 * 错误码对象 8 * 错误码对象
8 * 9 *
9 - * 全局错误码,占用 [0, 999],参见 {@link GlobalException} 10 + * 全局错误码,占用 [0, 999], 参见 {@link GlobalErrorCodeConstants}
10 * 业务异常错误码,占用 [1 000 000 000, +∞),参见 {@link ServiceErrorCodeRange} 11 * 业务异常错误码,占用 [1 000 000 000, +∞),参见 {@link ServiceErrorCodeRange}
11 * 12 *
12 * TODO 错误码设计成对象的原因,为未来的 i18 国际化做准备 13 * TODO 错误码设计成对象的原因,为未来的 i18 国际化做准备
@@ -21,11 +22,11 @@ public class ErrorCode { @@ -21,11 +22,11 @@ public class ErrorCode {
21 /** 22 /**
22 * 错误提示 23 * 错误提示
23 */ 24 */
24 - private final String message; 25 + private final String msg;
25 26
26 public ErrorCode(Integer code, String message) { 27 public ErrorCode(Integer code, String message) {
27 this.code = code; 28 this.code = code;
28 - this.message = message; 29 + this.msg = message;
29 } 30 }
30 31
31 } 32 }
src/main/java/cn/iocoder/dashboard/common/exception/GlobalException.java deleted
1 -package cn.iocoder.dashboard.common.exception;  
2 -  
3 -import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants;  
4 -import lombok.Data;  
5 -import lombok.EqualsAndHashCode;  
6 -  
7 -/**  
8 - * 全局异常 Exception  
9 - */  
10 -@Data  
11 -@EqualsAndHashCode(callSuper = true)  
12 -public class GlobalException extends RuntimeException {  
13 -  
14 - /**  
15 - * 全局错误码  
16 - *  
17 - * @see GlobalErrorCodeConstants  
18 - */  
19 - private Integer code;  
20 - /**  
21 - * 错误提示  
22 - */  
23 - private String message;  
24 -  
25 - /**  
26 - * 空构造方法,避免反序列化问题  
27 - */  
28 - public GlobalException() {  
29 - }  
30 -  
31 - public GlobalException(ErrorCode errorCode) {  
32 - this.code = errorCode.getCode();  
33 - this.message = errorCode.getMessage();  
34 - }  
35 -  
36 - public GlobalException(Integer code, String message) {  
37 - this.code = code;  
38 - this.message = message;  
39 - }  
40 -  
41 -}  
src/main/java/cn/iocoder/dashboard/common/exception/ServiceException.java
@@ -30,7 +30,7 @@ public final class ServiceException extends RuntimeException { @@ -30,7 +30,7 @@ public final class ServiceException extends RuntimeException {
30 30
31 public ServiceException(ErrorCode errorCode) { 31 public ServiceException(ErrorCode errorCode) {
32 this.code = errorCode.getCode(); 32 this.code = errorCode.getCode();
33 - this.message = errorCode.getMessage(); 33 + this.message = errorCode.getMsg();
34 } 34 }
35 35
36 public ServiceException(Integer code, String message) { 36 public ServiceException(Integer code, String message) {
src/main/java/cn/iocoder/dashboard/common/exception/util/ServiceExceptionUtil.java
@@ -47,12 +47,12 @@ public class ServiceExceptionUtil { @@ -47,12 +47,12 @@ public class ServiceExceptionUtil {
47 // ========== 和 ServiceException 的集成 ========== 47 // ========== 和 ServiceException 的集成 ==========
48 48
49 public static ServiceException exception(ErrorCode errorCode) { 49 public static ServiceException exception(ErrorCode errorCode) {
50 - String messagePattern = MESSAGES.getOrDefault(errorCode.getCode(), errorCode.getMessage()); 50 + String messagePattern = MESSAGES.getOrDefault(errorCode.getCode(), errorCode.getMsg());
51 return exception0(errorCode.getCode(), messagePattern); 51 return exception0(errorCode.getCode(), messagePattern);
52 } 52 }
53 53
54 public static ServiceException exception(ErrorCode errorCode, Object... params) { 54 public static ServiceException exception(ErrorCode errorCode, Object... params) {
55 - String messagePattern = MESSAGES.getOrDefault(errorCode.getCode(), errorCode.getMessage()); 55 + String messagePattern = MESSAGES.getOrDefault(errorCode.getCode(), errorCode.getMsg());
56 return exception0(errorCode.getCode(), messagePattern, params); 56 return exception0(errorCode.getCode(), messagePattern, params);
57 } 57 }
58 58
src/main/java/cn/iocoder/dashboard/common/pojo/CommonResult.java
1 package cn.iocoder.dashboard.common.pojo; 1 package cn.iocoder.dashboard.common.pojo;
2 2
3 import cn.iocoder.dashboard.common.exception.ErrorCode; 3 import cn.iocoder.dashboard.common.exception.ErrorCode;
4 -import cn.iocoder.dashboard.common.exception.GlobalException;  
5 import cn.iocoder.dashboard.common.exception.ServiceException; 4 import cn.iocoder.dashboard.common.exception.ServiceException;
6 import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; 5 import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants;
7 import com.fasterxml.jackson.annotation.JsonIgnore; 6 import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -9,6 +8,7 @@ import lombok.Data; @@ -9,6 +8,7 @@ import lombok.Data;
9 import org.springframework.util.Assert; 8 import org.springframework.util.Assert;
10 9
11 import java.io.Serializable; 10 import java.io.Serializable;
  11 +import java.util.Objects;
12 12
13 /** 13 /**
14 * 通用返回 14 * 通用返回
@@ -16,7 +16,7 @@ import java.io.Serializable; @@ -16,7 +16,7 @@ import java.io.Serializable;
16 * @param <T> 数据泛型 16 * @param <T> 数据泛型
17 */ 17 */
18 @Data 18 @Data
19 -public final class CommonResult<T> implements Serializable { 19 +public class CommonResult<T> implements Serializable {
20 20
21 /** 21 /**
22 * 错误码 22 * 错误码
@@ -31,7 +31,7 @@ public final class CommonResult&lt;T&gt; implements Serializable { @@ -31,7 +31,7 @@ public final class CommonResult&lt;T&gt; implements Serializable {
31 /** 31 /**
32 * 错误提示,用户可阅读 32 * 错误提示,用户可阅读
33 * 33 *
34 - * @see ErrorCode#getMessage() () 34 + * @see ErrorCode#getMsg() ()
35 */ 35 */
36 private String msg; 36 private String msg;
37 37
@@ -57,7 +57,7 @@ public final class CommonResult&lt;T&gt; implements Serializable { @@ -57,7 +57,7 @@ public final class CommonResult&lt;T&gt; implements Serializable {
57 } 57 }
58 58
59 public static <T> CommonResult<T> error(ErrorCode errorCode) { 59 public static <T> CommonResult<T> error(ErrorCode errorCode) {
60 - return error(errorCode.getCode(), errorCode.getMessage()); 60 + return error(errorCode.getCode(), errorCode.getMsg());
61 } 61 }
62 62
63 public static <T> CommonResult<T> success(T data) { 63 public static <T> CommonResult<T> success(T data) {
@@ -68,9 +68,13 @@ public final class CommonResult&lt;T&gt; implements Serializable { @@ -68,9 +68,13 @@ public final class CommonResult&lt;T&gt; implements Serializable {
68 return result; 68 return result;
69 } 69 }
70 70
  71 + public static boolean isSuccess(Integer code) {
  72 + return Objects.equals(code, GlobalErrorCodeConstants.SUCCESS.getCode());
  73 + }
  74 +
71 @JsonIgnore // 避免 jackson 序列化 75 @JsonIgnore // 避免 jackson 序列化
72 public boolean isSuccess() { 76 public boolean isSuccess() {
73 - return GlobalErrorCodeConstants.SUCCESS.getCode().equals(code); 77 + return isSuccess(code);
74 } 78 }
75 79
76 @JsonIgnore // 避免 jackson 序列化 80 @JsonIgnore // 避免 jackson 序列化
@@ -81,16 +85,12 @@ public final class CommonResult&lt;T&gt; implements Serializable { @@ -81,16 +85,12 @@ public final class CommonResult&lt;T&gt; implements Serializable {
81 // ========= 和 Exception 异常体系集成 ========= 85 // ========= 和 Exception 异常体系集成 =========
82 86
83 /** 87 /**
84 - * 判断是否有异常。如果有,则抛出 {@link GlobalException} 或 {@link ServiceException} 异常 88 + * 判断是否有异常。如果有,则抛出 {@link ServiceException} 异常
85 */ 89 */
86 - public void checkError() throws GlobalException, ServiceException { 90 + public void checkError() throws ServiceException {
87 if (isSuccess()) { 91 if (isSuccess()) {
88 return; 92 return;
89 } 93 }
90 - // 全局异常  
91 - if (GlobalErrorCodeConstants.isMatch(code)) {  
92 - throw new GlobalException(code, msg);  
93 - }  
94 // 业务异常 94 // 业务异常
95 throw new ServiceException(code, msg); 95 throw new ServiceException(code, msg);
96 } 96 }
@@ -99,8 +99,4 @@ public final class CommonResult&lt;T&gt; implements Serializable { @@ -99,8 +99,4 @@ public final class CommonResult&lt;T&gt; implements Serializable {
99 return error(serviceException.getCode(), serviceException.getMessage()); 99 return error(serviceException.getCode(), serviceException.getMessage());
100 } 100 }
101 101
102 - public static <T> CommonResult<T> error(GlobalException globalException) {  
103 - return error(globalException.getCode(), globalException.getMessage());  
104 - }  
105 -  
106 } 102 }
src/main/java/cn/iocoder/dashboard/framework/excel/core/convert/DictConvert.java
@@ -13,6 +13,11 @@ import com.alibaba.excel.metadata.GlobalConfiguration; @@ -13,6 +13,11 @@ import com.alibaba.excel.metadata.GlobalConfiguration;
13 import com.alibaba.excel.metadata.property.ExcelContentProperty; 13 import com.alibaba.excel.metadata.property.ExcelContentProperty;
14 import lombok.extern.slf4j.Slf4j; 14 import lombok.extern.slf4j.Slf4j;
15 15
  16 +/**
  17 + * Excel {@link SysDictDataDO} 数据字典转换器
  18 + *
  19 + * @author 芋道源码
  20 + */
16 @Slf4j 21 @Slf4j
17 public class DictConvert implements Converter<Object> { 22 public class DictConvert implements Converter<Object> {
18 23
src/main/java/cn/iocoder/dashboard/framework/excel/core/convert/JsonConvert.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.excel.core.convert;
  2 +
  3 +import cn.iocoder.dashboard.util.json.JsonUtils;
  4 +import com.alibaba.excel.converters.Converter;
  5 +import com.alibaba.excel.enums.CellDataTypeEnum;
  6 +import com.alibaba.excel.metadata.CellData;
  7 +import com.alibaba.excel.metadata.GlobalConfiguration;
  8 +import com.alibaba.excel.metadata.property.ExcelContentProperty;
  9 +
  10 +/**
  11 + * Excel Json 转换器
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +public class JsonConvert implements Converter<Object> {
  16 +
  17 + @Override
  18 + public Class<?> supportJavaTypeKey() {
  19 + throw new UnsupportedOperationException("暂不支持,也不需要");
  20 + }
  21 +
  22 + @Override
  23 + public CellDataTypeEnum supportExcelTypeKey() {
  24 + throw new UnsupportedOperationException("暂不支持,也不需要");
  25 + }
  26 +
  27 + @Override
  28 + public Object convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
  29 + throw new UnsupportedOperationException("暂不支持,也不需要");
  30 + }
  31 +
  32 + @Override
  33 + public CellData<String> convertToExcelData(Object value, ExcelContentProperty contentProperty,
  34 + GlobalConfiguration globalConfiguration) {
  35 + // 生成 Excel 小表格
  36 + return new CellData<>(JsonUtils.toJsonString(value));
  37 + }
  38 +
  39 +}
src/main/java/cn/iocoder/dashboard/framework/redis/config/RedisConfig.java
@@ -48,8 +48,8 @@ public class RedisConfig { @@ -48,8 +48,8 @@ public class RedisConfig {
48 * 创建 Redis Pub/Sub 广播消费的容器 48 * 创建 Redis Pub/Sub 广播消费的容器
49 */ 49 */
50 @Bean 50 @Bean
51 - public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory factory,  
52 - List<AbstractChannelMessageListener<?>> listeners) { 51 + public RedisMessageListenerContainer redisMessageListenerContainer(
  52 + RedisConnectionFactory factory, List<AbstractChannelMessageListener<?>> listeners) {
53 // 创建 RedisMessageListenerContainer 对象 53 // 创建 RedisMessageListenerContainer 对象
54 RedisMessageListenerContainer container = new RedisMessageListenerContainer(); 54 RedisMessageListenerContainer container = new RedisMessageListenerContainer();
55 // 设置 RedisConnection 工厂。 55 // 设置 RedisConnection 工厂。
@@ -69,8 +69,8 @@ public class RedisConfig { @@ -69,8 +69,8 @@ public class RedisConfig {
69 * Redis Stream 的 xreadgroup 命令:https://www.geek-book.com/src/docs/redis/redis/redis.io/commands/xreadgroup.html 69 * Redis Stream 的 xreadgroup 命令:https://www.geek-book.com/src/docs/redis/redis/redis.io/commands/xreadgroup.html
70 */ 70 */
71 @Bean(initMethod = "start", destroyMethod = "stop") 71 @Bean(initMethod = "start", destroyMethod = "stop")
72 - public StreamMessageListenerContainer<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer(RedisTemplate<String, Object> redisTemplate,  
73 - List<AbstractStreamMessageListener<?>> listeners) { 72 + public StreamMessageListenerContainer<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer(
  73 + RedisTemplate<String, Object> redisTemplate, List<AbstractStreamMessageListener<?>> listeners) {
74 // 第一步,创建 StreamMessageListenerContainer 容器 74 // 第一步,创建 StreamMessageListenerContainer 容器
75 // 创建 options 配置 75 // 创建 options 配置
76 StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, String>> containerOptions = 76 StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, String>> containerOptions =
src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityConfiguration.java
@@ -128,13 +128,13 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @@ -128,13 +128,13 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
128 // 设置每个请求的权限 128 // 设置每个请求的权限
129 .authorizeRequests() 129 .authorizeRequests()
130 // 登陆的接口,可匿名访问 130 // 登陆的接口,可匿名访问
131 - .antMatchers(webProperties.getApiPrefix() + "/login").anonymous() 131 + .antMatchers(api("/login")).anonymous()
132 // 通用的接口,可匿名访问 132 // 通用的接口,可匿名访问
133 - .antMatchers( webProperties.getApiPrefix() + "/system/captcha/**").anonymous() 133 + .antMatchers(api("/system/captcha/**")).anonymous()
134 // 静态资源,可匿名访问 134 // 静态资源,可匿名访问
135 .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll() 135 .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll()
136 // 文件的获取接口,可匿名访问 136 // 文件的获取接口,可匿名访问
137 - .antMatchers(webProperties.getApiPrefix() + "/infra/file/get/**").anonymous() 137 + .antMatchers(api("/infra/file/get/**")).anonymous()
138 // Swagger 接口文档 138 // Swagger 接口文档
139 .antMatchers("/swagger-ui.html").anonymous() 139 .antMatchers("/swagger-ui.html").anonymous()
140 .antMatchers("/swagger-resources/**").anonymous() 140 .antMatchers("/swagger-resources/**").anonymous()
@@ -148,13 +148,19 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @@ -148,13 +148,19 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
148 .antMatchers("/actuator/**").anonymous() 148 .antMatchers("/actuator/**").anonymous()
149 // Druid 监控 149 // Druid 监控
150 .antMatchers("/druid/**").anonymous() 150 .antMatchers("/druid/**").anonymous()
  151 + // 短信回调 API
  152 + .antMatchers(api("/system/sms/callback/**")).anonymous()
151 // 除上面外的所有请求全部需要鉴权认证 153 // 除上面外的所有请求全部需要鉴权认证
152 .anyRequest().authenticated() 154 .anyRequest().authenticated()
153 .and() 155 .and()
154 .headers().frameOptions().disable(); 156 .headers().frameOptions().disable();
155 - httpSecurity.logout().logoutUrl(webProperties.getApiPrefix() + "/logout").logoutSuccessHandler(logoutSuccessHandler); 157 + httpSecurity.logout().logoutUrl(api("/logout")).logoutSuccessHandler(logoutSuccessHandler);
156 // 添加 JWT Filter 158 // 添加 JWT Filter
157 httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); 159 httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
158 } 160 }
159 161
  162 + private String api(String url) {
  163 + return webProperties.getApiPrefix() + url;
  164 + }
  165 +
160 } 166 }
src/main/java/cn/iocoder/dashboard/framework/sms/config/SmsConfiguration.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.config;
  2 +
  3 +import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory;
  4 +import cn.iocoder.dashboard.framework.sms.core.client.impl.SmsClientFactoryImpl;
  5 +import org.springframework.context.annotation.Bean;
  6 +import org.springframework.context.annotation.Configuration;
  7 +
  8 +/**
  9 + * 短信配置类
  10 + *
  11 + * @author 芋道源码
  12 + */
  13 +@Configuration
  14 +public class SmsConfiguration {
  15 +
  16 + @Bean
  17 + public SmsClientFactory smsClientFactory() {
  18 + return new SmsClientFactoryImpl();
  19 + }
  20 +
  21 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClient.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.client;
  2 +
  3 +import cn.iocoder.dashboard.common.core.KeyValue;
  4 +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO;
  5 +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO;
  6 +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsTemplateRespDTO;
  7 +
  8 +import java.util.List;
  9 +
  10 +/**
  11 + * 短信客户端接口
  12 + *
  13 + * @author zzf
  14 + * @date 2021/1/25 14:14
  15 + */
  16 +public interface SmsClient {
  17 +
  18 + /**
  19 + * 获得渠道编号
  20 + *
  21 + * @return 渠道编号
  22 + */
  23 + Long getId();
  24 +
  25 + /**
  26 + * 发送消息
  27 + *
  28 + * @param logId 日志编号
  29 + * @param mobile 手机号
  30 + * @param apiTemplateId 短信 API 的模板编号
  31 + * @param templateParams 短信模板参数。通过 List 数组,保证参数的顺序
  32 + * @return 短信发送结果
  33 + */
  34 + SmsCommonResult<SmsSendRespDTO> sendSms(Long logId, String mobile, String apiTemplateId,
  35 + List<KeyValue<String, Object>> templateParams);
  36 +
  37 + /**
  38 + * 解析接收短信的接收结果
  39 + *
  40 + * @param text 结果
  41 + * @return 结果内容
  42 + * @throws Throwable 当解析 text 发生异常时,则会抛出异常
  43 + */
  44 + List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) throws Throwable;
  45 +
  46 + /**
  47 + * 查询指定的短信模板
  48 + *
  49 + * @param apiTemplateId 短信 API 的模板编号
  50 + * @return 短信模板
  51 + */
  52 + SmsCommonResult<SmsTemplateRespDTO> getSmsTemplate(String apiTemplateId);
  53 +
  54 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClientFactory.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.client;
  2 +
  3 +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
  4 +
  5 +/**
  6 + * 短信客户端工厂接口
  7 + *
  8 + * @author zzf
  9 + * @date 2021/1/28 14:01
  10 + */
  11 +public interface SmsClientFactory {
  12 +
  13 + /**
  14 + * 获得短信 Client
  15 + *
  16 + * @param channelId 渠道编号
  17 + * @return 短信 Client
  18 + */
  19 + SmsClient getSmsClient(Long channelId);
  20 +
  21 + /**
  22 + * 获得短信 Client
  23 + *
  24 + * @param channelCode 渠道编码
  25 + * @return 短信 Client
  26 + */
  27 + SmsClient getSmsClient(String channelCode);
  28 +
  29 + /**
  30 + * 创建短信 Client
  31 + *
  32 + * @param properties 配置对象
  33 + */
  34 + void createOrUpdateSmsClient(SmsChannelProperties properties);
  35 +
  36 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsCodeMapping.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.client;
  2 +
  3 +import cn.iocoder.dashboard.common.exception.ErrorCode;
  4 +import cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
  5 +
  6 +import java.util.function.Function;
  7 +
  8 +/**
  9 + * 将 API 的错误码,转换为通用的错误码
  10 + *
  11 + * @see SmsCommonResult
  12 + * @see SmsFrameworkErrorCodeConstants
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +public interface SmsCodeMapping extends Function<String, ErrorCode> {
  17 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsCommonResult.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.client;
  2 +
  3 +import cn.hutool.core.exceptions.ExceptionUtil;
  4 +import cn.hutool.core.lang.Assert;
  5 +import cn.iocoder.dashboard.common.exception.ErrorCode;
  6 +import cn.iocoder.dashboard.common.pojo.CommonResult;
  7 +import cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
  8 +import lombok.Data;
  9 +import lombok.EqualsAndHashCode;
  10 +import lombok.ToString;
  11 +
  12 +/**
  13 + * 短信的 CommonResult 拓展类
  14 + *
  15 + * 考虑到不同的平台,返回的 code 和 msg 是不同的,所以统一额外返回 {@link #apiCode} 和 {@link #apiMsg} 字段
  16 + *
  17 + * 另外,一些短信平台(例如说阿里云、腾讯云)会返回一个请求编号,用于排查请求失败的问题,我们设置到 {@link #apiRequestId} 字段
  18 + *
  19 + * @author 芋道源码
  20 + */
  21 +@Data
  22 +@EqualsAndHashCode(callSuper = true)
  23 +@ToString(callSuper = true)
  24 +public class SmsCommonResult<T> extends CommonResult<T> {
  25 +
  26 + /**
  27 + * API 返回错误码
  28 + *
  29 + * 由于第三方的错误码可能是字符串,所以使用 String 类型
  30 + */
  31 + private String apiCode;
  32 + /**
  33 + * API 返回提示
  34 + */
  35 + private String apiMsg;
  36 +
  37 + /**
  38 + * API 请求编号
  39 + */
  40 + private String apiRequestId;
  41 +
  42 + private SmsCommonResult() {
  43 + }
  44 +
  45 + public static <T> SmsCommonResult<T> build(String apiCode, String apiMsg, String apiRequestId,
  46 + T data, SmsCodeMapping codeMapping) {
  47 + Assert.notNull(codeMapping, "参数 codeMapping 不能为空");
  48 + SmsCommonResult<T> result = new SmsCommonResult<T>().setApiCode(apiCode).setApiMsg(apiMsg).setApiRequestId(apiRequestId);
  49 + result.setData(data);
  50 + // 翻译错误码
  51 + if (codeMapping != null) {
  52 + ErrorCode errorCode = codeMapping.apply(apiCode);
  53 + if (errorCode == null) {
  54 + errorCode = SmsFrameworkErrorCodeConstants.SMS_UNKNOWN;
  55 + }
  56 + result.setCode(errorCode.getCode()).setMsg(errorCode.getMsg());
  57 + }
  58 + return result;
  59 + }
  60 +
  61 + public static <T> SmsCommonResult<T> error(Throwable ex) {
  62 + SmsCommonResult<T> result = new SmsCommonResult<>();
  63 + result.setCode(SmsFrameworkErrorCodeConstants.EXCEPTION.getCode());
  64 + result.setMsg(ExceptionUtil.getRootCauseMessage(ex));
  65 + return result;
  66 + }
  67 +
  68 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsReceiveRespDTO.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.client.dto;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.Date;
  6 +
  7 +/**
  8 + * 消息接收 Response DTO
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +@Data
  13 +public class SmsReceiveRespDTO {
  14 +
  15 + /**
  16 + * 是否接收成功
  17 + */
  18 + private Boolean success;
  19 + /**
  20 + * API 接收结果的编码
  21 + */
  22 + private String errorCode;
  23 + /**
  24 + * API 接收结果的说明
  25 + */
  26 + private String errorMsg;
  27 +
  28 + /**
  29 + * 手机号
  30 + */
  31 + private String mobile;
  32 + /**
  33 + * 用户接收时间
  34 + */
  35 + private Date receiveTime;
  36 +
  37 + /**
  38 + * 短信 API 发送返回的序号
  39 + */
  40 + private String serialNo;
  41 + /**
  42 + * 短信日志编号
  43 + *
  44 + * 对应 SysSmsLogDO 的编号
  45 + */
  46 + private Long logId;
  47 +
  48 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsSendRespDTO.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.client.dto;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * 短信发送 Response DTO
  7 + *
  8 + * @author 芋道源码
  9 + */
  10 +@Data
  11 +public class SmsSendRespDTO {
  12 +
  13 + /**
  14 + * 短信 API 发送返回的序号
  15 + */
  16 + private String serialNo;
  17 +
  18 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsTemplateRespDTO.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.client.dto;
  2 +
  3 +import cn.iocoder.dashboard.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
  4 +import lombok.Data;
  5 +
  6 +/**
  7 + * 短信模板 Response DTO
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +@Data
  12 +public class SmsTemplateRespDTO {
  13 +
  14 + /**
  15 + * 模板编号
  16 + */
  17 + private String id;
  18 + /**
  19 + * 短信内容
  20 + */
  21 + private String content;
  22 + /**
  23 + * 审核状态
  24 + *
  25 + * 枚举 {@link SmsTemplateAuditStatusEnum}
  26 + */
  27 + private Integer auditStatus;
  28 + /**
  29 + * 审核未通过的理由
  30 + */
  31 + private String auditReason;
  32 +
  33 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/AbstractSmsClient.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.client.impl;
  2 +
  3 +import cn.iocoder.dashboard.common.core.KeyValue;
  4 +import cn.iocoder.dashboard.framework.sms.core.client.SmsClient;
  5 +import cn.iocoder.dashboard.framework.sms.core.client.SmsCodeMapping;
  6 +import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult;
  7 +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO;
  8 +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO;
  9 +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsTemplateRespDTO;
  10 +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
  11 +import lombok.extern.slf4j.Slf4j;
  12 +
  13 +import java.util.List;
  14 +
  15 +/**
  16 + * 短信客户端抽象类
  17 + *
  18 + * @author zzf
  19 + * @date 2021/2/1 9:28
  20 + */
  21 +@Slf4j
  22 +public abstract class AbstractSmsClient implements SmsClient {
  23 +
  24 + /**
  25 + * 短信渠道配置
  26 + */
  27 + protected volatile SmsChannelProperties properties;
  28 + /**
  29 + * 错误码枚举类
  30 + */
  31 + protected final SmsCodeMapping codeMapping;
  32 +
  33 + /**
  34 + * 短信客户端有参构造函数
  35 + *
  36 + * @param properties 短信配置
  37 + */
  38 + public AbstractSmsClient(SmsChannelProperties properties, SmsCodeMapping codeMapping) {
  39 + this.properties = properties;
  40 + this.codeMapping = codeMapping;
  41 + }
  42 +
  43 + /**
  44 + * 初始化
  45 + */
  46 + public final void init() {
  47 + doInit();
  48 + log.info("[init][配置({}) 初始化完成]", properties);
  49 + }
  50 +
  51 + public final void refresh(SmsChannelProperties properties) {
  52 + // 判断是否更新
  53 + if (properties.equals(this.properties)) {
  54 + return;
  55 + }
  56 + log.info("[refresh][配置({})发生变化,重新初始化]", properties);
  57 + this.properties = properties;
  58 + // 初始化
  59 + this.init();
  60 + }
  61 +
  62 + /**
  63 + * 自定义初始化
  64 + */
  65 + protected abstract void doInit();
  66 +
  67 + @Override
  68 + public Long getId() {
  69 + return properties.getId();
  70 + }
  71 +
  72 + @Override
  73 + public final SmsCommonResult<SmsSendRespDTO> sendSms(Long logId, String mobile,
  74 + String apiTemplateId, List<KeyValue<String, Object>> templateParams) {
  75 + // 执行短信发送
  76 + SmsCommonResult<SmsSendRespDTO> result;
  77 + try {
  78 + result = doSendSms(logId, mobile, apiTemplateId, templateParams);
  79 + } catch (Throwable ex) {
  80 + // 打印异常日志
  81 + log.error("[sendSms][发送短信异常,sendLogId({}) mobile({}) apiTemplateId({}) templateParams({})]",
  82 + logId, mobile, apiTemplateId, templateParams, ex);
  83 + // 封装返回
  84 + return SmsCommonResult.error(ex);
  85 + }
  86 + return result;
  87 + }
  88 +
  89 + protected abstract SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile,
  90 + String apiTemplateId, List<KeyValue<String, Object>> templateParams)
  91 + throws Throwable;
  92 +
  93 + @Override
  94 + public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) throws Throwable {
  95 + try {
  96 + return doParseSmsReceiveStatus(text);
  97 + } catch (Throwable ex) {
  98 + log.error("[parseSmsReceiveStatus][text({}) 解析发生异常]", text, ex);
  99 + throw ex;
  100 + }
  101 + }
  102 +
  103 + protected abstract List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable;
  104 +
  105 + @Override
  106 + public SmsCommonResult<SmsTemplateRespDTO> getSmsTemplate(String apiTemplateId) {
  107 + // 执行短信发送
  108 + SmsCommonResult<SmsTemplateRespDTO> result;
  109 + try {
  110 + result = doGetSmsTemplate(apiTemplateId);
  111 + } catch (Throwable ex) {
  112 + // 打印异常日志
  113 + log.error("[getSmsTemplate][获得短信模板({}) 发生异常]", apiTemplateId, ex);
  114 + // 封装返回
  115 + return SmsCommonResult.error(ex);
  116 + }
  117 + return result;
  118 + }
  119 +
  120 + protected abstract SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) throws Throwable;
  121 +
  122 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/SmsClientFactoryImpl.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.client.impl;
  2 +
  3 +import cn.iocoder.dashboard.framework.sms.core.client.SmsClient;
  4 +import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory;
  5 +import cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun.AliyunSmsClient;
  6 +import cn.iocoder.dashboard.framework.sms.core.client.impl.debug.DebugDingTalkSmsClient;
  7 +import cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian.YunpianSmsClient;
  8 +import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;
  9 +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
  10 +import lombok.extern.slf4j.Slf4j;
  11 +import org.springframework.util.Assert;
  12 +import org.springframework.validation.annotation.Validated;
  13 +
  14 +import java.util.Arrays;
  15 +import java.util.concurrent.ConcurrentHashMap;
  16 +import java.util.concurrent.ConcurrentMap;
  17 +
  18 +/**
  19 + * 短信客户端工厂接口
  20 + *
  21 + * @author zzf
  22 + */
  23 +@Validated
  24 +@Slf4j
  25 +public class SmsClientFactoryImpl implements SmsClientFactory {
  26 +
  27 + /**
  28 + * 短信客户端 Map
  29 + * key:渠道编号,使用 {@link SmsChannelProperties#getId()}
  30 + */
  31 + private final ConcurrentMap<Long, AbstractSmsClient> channelIdClients = new ConcurrentHashMap<>();
  32 +
  33 + /**
  34 + * 短信客户端 Map
  35 + * key:渠道编码,使用 {@link SmsChannelProperties#getCode()} ()}
  36 + *
  37 + * 注意,一些场景下,需要获得某个渠道类型的客户端,所以需要使用它。
  38 + * 例如说,解析短信接收结果,是相对通用的,不需要使用某个渠道编号的 {@link #channelIdClients}
  39 + */
  40 + private final ConcurrentMap<String, AbstractSmsClient> channelCodeClients = new ConcurrentHashMap<>();
  41 +
  42 + public SmsClientFactoryImpl() {
  43 + // 初始化 channelCodeClients 集合
  44 + Arrays.stream(SmsChannelEnum.values()).forEach(channel -> {
  45 + // 创建一个空的 SmsChannelProperties 对象
  46 + SmsChannelProperties properties = new SmsChannelProperties().setCode(channel.getCode())
  47 + .setApiKey("default").setApiSecret("default");
  48 + // 创建 Sms 客户端
  49 + AbstractSmsClient smsClient = createSmsClient(properties);
  50 + channelCodeClients.put(channel.getCode(), smsClient);
  51 + });
  52 + }
  53 +
  54 + @Override
  55 + public SmsClient getSmsClient(Long channelId) {
  56 + return channelIdClients.get(channelId);
  57 + }
  58 +
  59 + @Override
  60 + public SmsClient getSmsClient(String channelCode) {
  61 + return channelCodeClients.get(channelCode);
  62 + }
  63 +
  64 + @Override
  65 + public void createOrUpdateSmsClient(SmsChannelProperties properties) {
  66 + AbstractSmsClient client = channelIdClients.get(properties.getId());
  67 + if (client == null) {
  68 + client = this.createSmsClient(properties);
  69 + client.init();
  70 + channelIdClients.put(client.getId(), client);
  71 + } else {
  72 + client.refresh(properties);
  73 + }
  74 + }
  75 +
  76 + private AbstractSmsClient createSmsClient(SmsChannelProperties properties) {
  77 + SmsChannelEnum channelEnum = SmsChannelEnum.getByCode(properties.getCode());
  78 + Assert.notNull(channelEnum, String.format("渠道类型(%s) 为空", channelEnum));
  79 + // 创建客户端
  80 + switch (channelEnum) {
  81 + case ALIYUN: return new AliyunSmsClient(properties);
  82 + case YUN_PIAN: return new YunpianSmsClient(properties);
  83 + case DEBUG_DING_TALK: return new DebugDingTalkSmsClient(properties);
  84 + }
  85 + // 创建失败,错误日志 + 抛出异常
  86 + log.error("[createSmsClient][配置({}) 找不到合适的客户端实现]", properties);
  87 + throw new IllegalArgumentException(String.format("配置(%s) 找不到合适的客户端实现", properties));
  88 + }
  89 +
  90 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun;
  2 +
  3 +import cn.hutool.core.lang.Assert;
  4 +import cn.hutool.core.util.ReflectUtil;
  5 +import cn.hutool.core.util.StrUtil;
  6 +import cn.iocoder.dashboard.common.core.KeyValue;
  7 +import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult;
  8 +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO;
  9 +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO;
  10 +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsTemplateRespDTO;
  11 +import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient;
  12 +import cn.iocoder.dashboard.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
  13 +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
  14 +import cn.iocoder.dashboard.util.collection.MapUtils;
  15 +import cn.iocoder.dashboard.util.json.JsonUtils;
  16 +import com.aliyuncs.AcsRequest;
  17 +import com.aliyuncs.AcsResponse;
  18 +import com.aliyuncs.DefaultAcsClient;
  19 +import com.aliyuncs.IAcsClient;
  20 +import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateRequest;
  21 +import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
  22 +import com.aliyuncs.exceptions.ClientException;
  23 +import com.aliyuncs.profile.DefaultProfile;
  24 +import com.aliyuncs.profile.IClientProfile;
  25 +import com.fasterxml.jackson.annotation.JsonFormat;
  26 +import com.fasterxml.jackson.annotation.JsonProperty;
  27 +import com.google.common.annotations.VisibleForTesting;
  28 +import lombok.Data;
  29 +import lombok.extern.slf4j.Slf4j;
  30 +
  31 +import java.util.Date;
  32 +import java.util.List;
  33 +import java.util.Objects;
  34 +import java.util.function.Function;
  35 +import java.util.stream.Collectors;
  36 +
  37 +import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  38 +import static cn.iocoder.dashboard.util.date.DateUtils.TIME_ZONE_DEFAULT;
  39 +
  40 +/**
  41 + * 阿里短信客户端的实现类
  42 + *
  43 + * @author zzf
  44 + * @date 2021/1/25 14:17
  45 + */
  46 +@Slf4j
  47 +public class AliyunSmsClient extends AbstractSmsClient {
  48 +
  49 + /**
  50 + * REGION, 使用杭州
  51 + */
  52 + private static final String ENDPOINT = "cn-hangzhou";
  53 +
  54 + /**
  55 + * 阿里云客户端
  56 + */
  57 + private volatile IAcsClient client;
  58 +
  59 + public AliyunSmsClient(SmsChannelProperties properties) {
  60 + super(properties, new AliyunSmsCodeMapping());
  61 + Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
  62 + Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
  63 + }
  64 +
  65 + @Override
  66 + protected void doInit() {
  67 + IClientProfile profile = DefaultProfile.getProfile(ENDPOINT, properties.getApiKey(), properties.getApiSecret());
  68 + client = new DefaultAcsClient(profile);
  69 + }
  70 +
  71 + @Override
  72 + protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile,
  73 + String apiTemplateId, List<KeyValue<String, Object>> templateParams) {
  74 + // 构建参数
  75 + SendSmsRequest request = new SendSmsRequest();
  76 + request.setPhoneNumbers(mobile);
  77 + request.setSignName(properties.getSignature());
  78 + request.setTemplateCode(apiTemplateId);
  79 + request.setTemplateParam(JsonUtils.toJsonString(MapUtils.convertMap(templateParams)));
  80 + request.setOutId(String.valueOf(sendLogId));
  81 + // 执行请求
  82 + return invoke(request, response -> new SmsSendRespDTO().setSerialNo(response.getBizId()));
  83 + }
  84 +
  85 + @Override
  86 + protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable {
  87 + List<SmsReceiveStatus> statuses = JsonUtils.parseArray(text, SmsReceiveStatus.class);
  88 + return statuses.stream().map(status -> {
  89 + SmsReceiveRespDTO resp = new SmsReceiveRespDTO();
  90 + resp.setSuccess(status.getSuccess());
  91 + resp.setErrorCode(status.getErrCode()).setErrorMsg(status.getErrMsg());
  92 + resp.setMobile(status.getPhoneNumber()).setReceiveTime(status.getReportTime());
  93 + resp.setSerialNo(status.getBizId()).setLogId(Long.valueOf(status.getOutId()));
  94 + return resp;
  95 + }).collect(Collectors.toList());
  96 + }
  97 +
  98 + @Override
  99 + protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) {
  100 + // 构建参数
  101 + QuerySmsTemplateRequest request = new QuerySmsTemplateRequest();
  102 + request.setTemplateCode(apiTemplateId);
  103 + // 执行请求
  104 + return invoke(request, response -> {
  105 + SmsTemplateRespDTO data = new SmsTemplateRespDTO();
  106 + data.setId(response.getTemplateCode()).setContent(response.getTemplateContent());
  107 + data.setAuditStatus(convertSmsTemplateAuditStatus(response.getTemplateStatus())).setAuditReason(response.getReason());
  108 + return data;
  109 + });
  110 + }
  111 +
  112 + @VisibleForTesting
  113 + Integer convertSmsTemplateAuditStatus(Integer templateStatus) {
  114 + switch (templateStatus) {
  115 + case 0: return SmsTemplateAuditStatusEnum.CHECKING.getStatus();
  116 + case 1: return SmsTemplateAuditStatusEnum.SUCCESS.getStatus();
  117 + case 2: return SmsTemplateAuditStatusEnum.FAIL.getStatus();
  118 + default: throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus));
  119 + }
  120 + }
  121 +
  122 + @VisibleForTesting
  123 + <T extends AcsResponse, R> SmsCommonResult<R> invoke(AcsRequest<T> request, Function<T, R> responseConsumer) {
  124 + try {
  125 + // 执行发送. 由于阿里云 sms 短信没有统一的 Response,但是有统一的 code、message、requestId 属性,所以只好反射
  126 + T sendResult = client.getAcsResponse(request);
  127 + String code = (String) ReflectUtil.getFieldValue(sendResult, "code");
  128 + String message = (String) ReflectUtil.getFieldValue(sendResult, "message");
  129 + String requestId = (String) ReflectUtil.getFieldValue(sendResult, "requestId");
  130 + // 解析结果
  131 + R data = null;
  132 + if (Objects.equals(code, "OK")) { // 请求成功的情况下
  133 + data = responseConsumer.apply(sendResult);
  134 + }
  135 + // 拼接结果
  136 + return SmsCommonResult.build(code, message, requestId, data, codeMapping);
  137 + } catch (ClientException ex) {
  138 + return SmsCommonResult.build(ex.getErrCode(), formatResultMsg(ex), ex.getRequestId(), null, codeMapping);
  139 + }
  140 + }
  141 +
  142 + private static String formatResultMsg(ClientException ex) {
  143 + if (StrUtil.isEmpty(ex.getErrorDescription())) {
  144 + return ex.getErrMsg();
  145 + }
  146 + return ex.getErrMsg() + " => " + ex.getErrorDescription();
  147 + }
  148 +
  149 + /**
  150 + * 短信接收状态
  151 + *
  152 + * 参见 https://help.aliyun.com/document_detail/101867.html 文档
  153 + *
  154 + * @author 芋道源码
  155 + */
  156 + @Data
  157 + public static class SmsReceiveStatus {
  158 +
  159 + /**
  160 + * 手机号
  161 + */
  162 + @JsonProperty("phone_number")
  163 + private String phoneNumber;
  164 + /**
  165 + * 发送时间
  166 + */
  167 + @JsonProperty("send_time")
  168 + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
  169 + private Date sendTime;
  170 + /**
  171 + * 状态报告时间
  172 + */
  173 + @JsonProperty("report_time")
  174 + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
  175 + private Date reportTime;
  176 + /**
  177 + * 是否接收成功
  178 + */
  179 + private Boolean success;
  180 + /**
  181 + * 状态报告说明
  182 + */
  183 + @JsonProperty("err_msg")
  184 + private String errMsg;
  185 + /**
  186 + * 状态报告编码
  187 + */
  188 + @JsonProperty("err_code")
  189 + private String errCode;
  190 + /**
  191 + * 发送序列号
  192 + */
  193 + @JsonProperty("biz_id")
  194 + private String bizId;
  195 + /**
  196 + * 用户序列号
  197 + *
  198 + * 这里我们传递的是 SysSmsLogDO 的日志编号
  199 + */
  200 + @JsonProperty("out_id")
  201 + private String outId;
  202 + /**
  203 + * 短信长度,例如说 1、2、3
  204 + *
  205 + * 140 字节算一条短信,短信长度超过 140 字节时会拆分成多条短信发送
  206 + */
  207 + @JsonProperty("sms_size")
  208 + private Integer smsSize;
  209 +
  210 + }
  211 +
  212 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsCodeMapping.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun;
  2 +
  3 +import cn.iocoder.dashboard.common.exception.ErrorCode;
  4 +import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants;
  5 +import cn.iocoder.dashboard.framework.sms.core.client.SmsCodeMapping;
  6 +
  7 +import static cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants.*;
  8 +
  9 +/**
  10 + * 阿里云的 SmsCodeMapping 实现类
  11 + *
  12 + * 参见 https://help.aliyun.com/document_detail/101346.htm 文档
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +public class AliyunSmsCodeMapping implements SmsCodeMapping {
  17 +
  18 + @Override
  19 + public ErrorCode apply(String apiCode) {
  20 + switch (apiCode) {
  21 + case "OK": return GlobalErrorCodeConstants.SUCCESS;
  22 + case "isv.ACCOUNT_NOT_EXISTS":
  23 + case "isv.ACCOUNT_ABNORMAL":
  24 + case "MissingAccessKeyId": return SMS_ACCOUNT_INVALID;
  25 + case "isp.RAM_PERMISSION_DENY": return SMS_PERMISSION_DENY;
  26 + case "isv.INVALID_JSON_PARAM":
  27 + case "isv.INVALID_PARAMETERS": return SMS_API_PARAM_ERROR;
  28 + case "isv.BUSINESS_LIMIT_CONTROL": return SMS_SEND_BUSINESS_LIMIT_CONTROL;
  29 + case "isv.DAY_LIMIT_CONTROL": return SMS_SEND_DAY_LIMIT_CONTROL;
  30 + case "isv.SMS_CONTENT_ILLEGAL": return SMS_SEND_CONTENT_INVALID;
  31 + case "isv.SMS_TEMPLATE_ILLEGAL": return SMS_TEMPLATE_INVALID;
  32 + case "isv.SMS_SIGNATURE_ILLEGAL":
  33 + case "isv.SIGN_NAME_ILLEGAL":
  34 + case "isv.SMS_SIGN_ILLEGAL": return SMS_SIGN_INVALID;
  35 + case "isv.AMOUNT_NOT_ENOUGH":
  36 + case "isv.OUT_OF_SERVICE": return SMS_ACCOUNT_MONEY_NOT_ENOUGH;
  37 + case "isv.MOBILE_NUMBER_ILLEGAL": return SMS_MOBILE_INVALID;
  38 + case "isv.TEMPLATE_MISSING_PARAMETERS": return SMS_TEMPLATE_PARAM_ERROR;
  39 + }
  40 + return SMS_UNKNOWN;
  41 + }
  42 +
  43 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/debug/DebugDingTalkCodeMapping.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.client.impl.debug;
  2 +
  3 +import cn.iocoder.dashboard.common.exception.ErrorCode;
  4 +import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants;
  5 +import cn.iocoder.dashboard.framework.sms.core.client.SmsCodeMapping;
  6 +
  7 +import java.util.Objects;
  8 +
  9 +import static cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants.SMS_UNKNOWN;
  10 +
  11 +/**
  12 + * 钉钉的 SmsCodeMapping 实现类
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +public class DebugDingTalkCodeMapping implements SmsCodeMapping {
  17 +
  18 + @Override
  19 + public ErrorCode apply(String apiCode) {
  20 + return Objects.equals(apiCode, "0") ? GlobalErrorCodeConstants.SUCCESS : SMS_UNKNOWN;
  21 + }
  22 +
  23 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/debug/DebugDingTalkSmsClient.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.client.impl.debug;
  2 +
  3 +import cn.hutool.core.codec.Base64;
  4 +import cn.hutool.core.lang.Assert;
  5 +import cn.hutool.core.map.MapUtil;
  6 +import cn.hutool.core.util.StrUtil;
  7 +import cn.hutool.crypto.digest.DigestUtil;
  8 +import cn.hutool.crypto.digest.HmacAlgorithm;
  9 +import cn.hutool.http.HttpUtil;
  10 +import cn.iocoder.dashboard.common.core.KeyValue;
  11 +import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult;
  12 +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO;
  13 +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO;
  14 +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsTemplateRespDTO;
  15 +import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient;
  16 +import cn.iocoder.dashboard.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
  17 +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
  18 +import cn.iocoder.dashboard.util.collection.MapUtils;
  19 +import cn.iocoder.dashboard.util.json.JsonUtils;
  20 +
  21 +import java.util.HashMap;
  22 +import java.util.List;
  23 +import java.util.Map;
  24 +
  25 +/**
  26 + * 基于钉钉 WebHook 实现的调试的短信客户端实现类
  27 + *
  28 + * 考虑到省钱,我们使用钉钉 WebHook 模拟发送短信,方便调试。
  29 + *
  30 + * @author 芋道源码
  31 + */
  32 +public class DebugDingTalkSmsClient extends AbstractSmsClient {
  33 +
  34 + public DebugDingTalkSmsClient(SmsChannelProperties properties) {
  35 + super(properties, new DebugDingTalkCodeMapping());
  36 + Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
  37 + Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
  38 + }
  39 +
  40 + @Override
  41 + protected void doInit() {
  42 + }
  43 +
  44 + @Override
  45 + protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile,
  46 + String apiTemplateId, List<KeyValue<String, Object>> templateParams) throws Throwable {
  47 + // 构建请求
  48 + String url = buildUrl("robot/send");
  49 + Map<String, Object> params = new HashMap<>();
  50 + params.put("msgtype", "text");
  51 + String content = String.format("【模拟短信】\n手机号:%s\n短信日志编号:%d\n模板参数:%s",
  52 + mobile, sendLogId, MapUtils.convertMap(templateParams));
  53 + params.put("text", MapUtil.builder().put("content", content).build());
  54 + // 执行请求
  55 + String responseText = HttpUtil.post(url, JsonUtils.toJsonString(params));
  56 + // 解析结果
  57 + Map<?, ?> responseObj = JsonUtils.parseObject(responseText, Map.class);
  58 + return SmsCommonResult.build(MapUtil.getStr(responseObj, "errcode"), MapUtil.getStr(responseObj, "errorMsg"),
  59 + null, new SmsSendRespDTO().setSerialNo(StrUtil.uuid()), codeMapping);
  60 + }
  61 +
  62 + /**
  63 + * 构建请求地址
  64 + *
  65 + * 参见 https://developers.dingtalk.com/document/app/custom-robot-access/title-nfv-794-g71 文档
  66 + *
  67 + * @param path 请求路径
  68 + * @return 请求地址
  69 + */
  70 + @SuppressWarnings("SameParameterValue")
  71 + private String buildUrl(String path) {
  72 + // 生成 timestamp
  73 + long timestamp = System.currentTimeMillis();
  74 + // 生成 sign
  75 + String secret = properties.getApiSecret();
  76 + String stringToSign = timestamp + "\n" + secret;
  77 + byte[] signData = DigestUtil.hmac(HmacAlgorithm.HmacSHA256, StrUtil.bytes(secret)).digest(stringToSign);
  78 + String sign = Base64.encode(signData);
  79 + // 构建最终 URL
  80 + return String.format("https://oapi.dingtalk.com/%s?access_token=%s&timestamp=%d&sign=%s",
  81 + path, properties.getApiKey(), timestamp, sign);
  82 + }
  83 +
  84 + @Override
  85 + protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable {
  86 + throw new UnsupportedOperationException("模拟短信客户端,暂时无需解析回调");
  87 + }
  88 +
  89 + @Override
  90 + protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) {
  91 + SmsTemplateRespDTO data = new SmsTemplateRespDTO().setId(apiTemplateId).setContent("")
  92 + .setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason("");
  93 + return SmsCommonResult.build("0", "success", null, data, codeMapping);
  94 + }
  95 +
  96 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClient.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
  5 +import cn.hutool.core.util.StrUtil;
  6 +import cn.hutool.core.util.URLUtil;
  7 +import cn.iocoder.dashboard.common.core.KeyValue;
  8 +import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult;
  9 +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO;
  10 +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO;
  11 +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsTemplateRespDTO;
  12 +import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient;
  13 +import cn.iocoder.dashboard.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
  14 +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
  15 +import cn.iocoder.dashboard.util.json.JsonUtils;
  16 +import com.fasterxml.jackson.annotation.JsonFormat;
  17 +import com.fasterxml.jackson.annotation.JsonProperty;
  18 +import com.google.common.annotations.VisibleForTesting;
  19 +import com.yunpian.sdk.YunpianClient;
  20 +import com.yunpian.sdk.constant.YunpianConstant;
  21 +import com.yunpian.sdk.model.Result;
  22 +import com.yunpian.sdk.model.Template;
  23 +import lombok.Data;
  24 +import lombok.extern.slf4j.Slf4j;
  25 +
  26 +import java.util.*;
  27 +import java.util.function.Function;
  28 +import java.util.function.Supplier;
  29 +import java.util.stream.Collectors;
  30 +
  31 +import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  32 +import static cn.iocoder.dashboard.util.date.DateUtils.TIME_ZONE_DEFAULT;
  33 +
  34 +/**
  35 + * 云片短信客户端的实现类
  36 + *
  37 + * @author zzf
  38 + * @date 9:48 2021/3/5
  39 + */
  40 +@Slf4j
  41 +public class YunpianSmsClient extends AbstractSmsClient {
  42 +
  43 + /**
  44 + * 云信短信客户端
  45 + */
  46 + private volatile YunpianClient client;
  47 +
  48 + public YunpianSmsClient(SmsChannelProperties properties) {
  49 + super(properties, new YunpianSmsCodeMapping());
  50 + Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
  51 + }
  52 +
  53 + @Override
  54 + public void doInit() {
  55 + YunpianClient oldClient = client;
  56 + // 初始化新的客户端
  57 + YunpianClient newClient = new YunpianClient(properties.getApiKey());
  58 + newClient.init();
  59 + this.client = newClient;
  60 + // 销毁老的客户端
  61 + if (oldClient != null) {
  62 + oldClient.close();
  63 + }
  64 + }
  65 +
  66 + @Override
  67 + protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile,
  68 + String apiTemplateId, List<KeyValue<String, Object>> templateParams) throws Throwable {
  69 + return invoke(() -> {
  70 + Map<String, String> request = new HashMap<>();
  71 + request.put(YunpianConstant.MOBILE, mobile);
  72 + request.put(YunpianConstant.TPL_ID, apiTemplateId);
  73 + request.put(YunpianConstant.TPL_VALUE, formatTplValue(templateParams));
  74 + request.put(YunpianConstant.UID, String.valueOf(sendLogId));
  75 + request.put(YunpianConstant.CALLBACK_URL, properties.getCallbackUrl());
  76 + return client.sms().tpl_single_send(request);
  77 + }, response -> new SmsSendRespDTO().setSerialNo(String.valueOf(response.getSid())));
  78 + }
  79 +
  80 + private static String formatTplValue(List<KeyValue<String, Object>> templateParams) {
  81 + if (CollUtil.isEmpty(templateParams)) {
  82 + return "";
  83 + }
  84 + // 参考 https://www.yunpian.com/official/document/sms/zh_cn/introduction_demos_encode_sample 格式化
  85 + StringJoiner joiner = new StringJoiner("&");
  86 + templateParams.forEach(param -> joiner.add(String.format("#%s#=%s", param.getKey(), URLUtil.encode(String.valueOf(param.getValue())))));
  87 + return joiner.toString();
  88 + }
  89 +
  90 + @Override
  91 + protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable {
  92 + List<SmsReceiveStatus> statuses = JsonUtils.parseArray(text, SmsReceiveStatus.class);
  93 + return statuses.stream().map(status -> {
  94 + SmsReceiveRespDTO resp = new SmsReceiveRespDTO();
  95 + resp.setSuccess(Objects.equals(status.getReportStatus(), "SUCCESS"));
  96 + resp.setErrorCode(status.getErrorMsg()).setErrorMsg(status.getErrorDetail());
  97 + resp.setMobile(status.getMobile()).setReceiveTime(status.getUserReceiveTime());
  98 + resp.setSerialNo(String.valueOf(status.getSid())).setLogId(status.getUid());
  99 + return resp;
  100 + }).collect(Collectors.toList());
  101 + }
  102 +
  103 + @Override
  104 + protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) throws Throwable {
  105 + return invoke(() -> {
  106 + Map<String, String> request = new HashMap<>();
  107 + request.put(YunpianConstant.APIKEY, properties.getApiKey());
  108 + request.put(YunpianConstant.TPL_ID, apiTemplateId);
  109 + return client.tpl().get(request);
  110 + }, response -> {
  111 + Template template = response.get(0);
  112 + return new SmsTemplateRespDTO().setId(String.valueOf(template.getTpl_id())).setContent(template.getTpl_content())
  113 + .setAuditStatus(convertSmsTemplateAuditStatus(template.getCheck_status())).setAuditReason(template.getReason());
  114 + });
  115 + }
  116 +
  117 + @VisibleForTesting
  118 + Integer convertSmsTemplateAuditStatus(String checkStatus) {
  119 + switch (checkStatus) {
  120 + case "CHECKING": return SmsTemplateAuditStatusEnum.CHECKING.getStatus();
  121 + case "SUCCESS": return SmsTemplateAuditStatusEnum.SUCCESS.getStatus();
  122 + case "FAIL": return SmsTemplateAuditStatusEnum.FAIL.getStatus();
  123 + default: throw new IllegalArgumentException(String.format("未知审核状态(%s)", checkStatus));
  124 + }
  125 + }
  126 +
  127 + @VisibleForTesting
  128 + <T, R> SmsCommonResult<R> invoke(Supplier<Result<T>> requestConsumer, Function<T, R> responseConsumer) throws Throwable {
  129 + // 执行请求
  130 + Result<T> result = requestConsumer.get();
  131 + if (result.getThrowable() != null) {
  132 + throw result.getThrowable();
  133 + }
  134 + // 解析结果
  135 + R data = null;
  136 + if (result.getData() != null) {
  137 + data = responseConsumer.apply(result.getData());
  138 + }
  139 + // 拼接结果
  140 + return SmsCommonResult.build(String.valueOf(result.getCode()), formatResultMsg(result), null, data, codeMapping);
  141 + }
  142 +
  143 + private static String formatResultMsg(Result<?> sendResult) {
  144 + if (StrUtil.isEmpty(sendResult.getDetail())) {
  145 + return sendResult.getMsg();
  146 + }
  147 + return sendResult.getMsg() + " => " + sendResult.getDetail();
  148 + }
  149 +
  150 + /**
  151 + * 短信接收状态
  152 + *
  153 + * 参见 https://www.yunpian.com/official/document/sms/zh_cn/domestic_push_report 文档
  154 + *
  155 + * @author 芋道源码
  156 + */
  157 + @Data
  158 + public static class SmsReceiveStatus {
  159 +
  160 + /**
  161 + * 接收状态
  162 + *
  163 + * 目前仅有 SUCCESS / FAIL,所以使用 Boolean 接收
  164 + */
  165 + @JsonProperty("report_status")
  166 + private String reportStatus;
  167 + /**
  168 + * 接收手机号
  169 + */
  170 + private String mobile;
  171 + /**
  172 + * 运营商返回的代码,如:"DB:0103"
  173 + *
  174 + * 由于不同运营商信息不同,此字段仅供参考;
  175 + */
  176 + @JsonProperty("error_msg")
  177 + private String errorMsg;
  178 + /**
  179 + * 运营商反馈代码的中文解释
  180 + *
  181 + * 默认不推送此字段,如需推送,请联系客服
  182 + */
  183 + @JsonProperty("error_detail")
  184 + private String errorDetail;
  185 + /**
  186 + * 短信编号
  187 + */
  188 + private Long sid;
  189 + /**
  190 + * 用户自定义 id
  191 + *
  192 + * 这里我们传递的是 SysSmsLogDO 的日志编号
  193 + */
  194 + private Long uid;
  195 + /**
  196 + * 用户接收时间
  197 + */
  198 + @JsonProperty("user_receive_time")
  199 + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
  200 + private Date userReceiveTime;
  201 +
  202 + }
  203 +
  204 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsCodeMapping.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian;
  2 +
  3 +import cn.iocoder.dashboard.common.exception.ErrorCode;
  4 +import cn.iocoder.dashboard.framework.sms.core.client.SmsCodeMapping;
  5 +
  6 +import static cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants.SUCCESS;
  7 +import static cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants.*;
  8 +import static com.yunpian.sdk.constant.Code.*;
  9 +
  10 +/**
  11 + * 云片的 SmsCodeMapping 实现类
  12 + *
  13 + * 参见 https://www.yunpian.com/official/document/sms/zh_CN/returnvalue_common 文档
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +public class YunpianSmsCodeMapping implements SmsCodeMapping {
  18 +
  19 + @Override
  20 + public ErrorCode apply(String apiCode) {
  21 + int code = Integer.parseInt(apiCode);
  22 + switch (code) {
  23 + case OK: return SUCCESS;
  24 + case ARGUMENT_MISSING: return SMS_API_PARAM_ERROR;
  25 + case BAD_ARGUMENT_FORMAT: return SMS_TEMPLATE_PARAM_ERROR;
  26 + case TPL_NOT_FOUND:
  27 + case TPL_NOT_VALID: return SMS_TEMPLATE_INVALID;
  28 + case MONEY_NOT_ENOUGH: return SMS_ACCOUNT_MONEY_NOT_ENOUGH;
  29 + case BLACK_WORD: return SMS_SEND_CONTENT_INVALID;
  30 + case DUP_IN_SHORT_TIME:
  31 + case TOO_MANY_TIME_IN_5:
  32 + case DAY_LIMIT_PER_MOBILE:
  33 + case HOUR_LIMIT_PER_MOBILE: return SMS_SEND_BUSINESS_LIMIT_CONTROL;
  34 + case BLACK_PHONE_FILTER: return SMS_MOBILE_BLACK;
  35 + case SIGN_NOT_MATCH:
  36 + case BAD_SIGN_FORMAT:
  37 + case SIGN_NOT_VALID: return SMS_SIGN_INVALID;
  38 + case BAD_API_KEY: return SMS_ACCOUNT_INVALID;
  39 + case API_NOT_ALLOWED: return SMS_PERMISSION_DENY;
  40 + case IP_NOT_ALLOWED: return SMS_IP_DENY;
  41 + }
  42 + return SMS_UNKNOWN;
  43 + }
  44 +
  45 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsChannelEnum.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.enums;
  2 +
  3 +import cn.hutool.core.util.ArrayUtil;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Getter;
  6 +
  7 +/**
  8 + * 短信渠道枚举
  9 + *
  10 + * @author zzf
  11 + * @date 2021/1/25 10:56
  12 + */
  13 +@Getter
  14 +@AllArgsConstructor
  15 +public enum SmsChannelEnum {
  16 +
  17 + DEBUG_DING_TALK("DEBUG_DING_TALK", "调试(钉钉)"),
  18 + YUN_PIAN("YUN_PIAN", "云片"),
  19 + ALIYUN("ALIYUN", "阿里云"),
  20 +// TENCENT("TENCENT", "腾讯云"),
  21 +// HUA_WEI("HUA_WEI", "华为云"),
  22 + ;
  23 +
  24 + /**
  25 + * 编码
  26 + */
  27 + private final String code;
  28 + /**
  29 + * 名字
  30 + */
  31 + private final String name;
  32 +
  33 + public static SmsChannelEnum getByCode(String code) {
  34 + return ArrayUtil.firstMatch(o -> o.getCode().equals(code), values());
  35 + }
  36 +
  37 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsFrameworkErrorCodeConstants.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.enums;
  2 +
  3 +import cn.iocoder.dashboard.common.exception.ErrorCode;
  4 +
  5 +/**
  6 + * 短信框架的错误码枚举
  7 + *
  8 + * 短信框架,使用 2-001-000-000 段
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +public interface SmsFrameworkErrorCodeConstants {
  13 +
  14 + ErrorCode SMS_UNKNOWN = new ErrorCode(2001000000, "未知错误,需要解析");
  15 +
  16 + // ========== 权限 / 限流等相关 2001000100 ==========
  17 +
  18 + ErrorCode SMS_PERMISSION_DENY = new ErrorCode(2001000100, "没有发送短信的权限");
  19 + // 云片:可以配置 IP 白名单,只有在白名单中才可以发送短信
  20 + ErrorCode SMS_IP_DENY = new ErrorCode(2001000100, "IP 不允许发送短信");
  21 +
  22 + // 阿里云:将短信发送频率限制在正常的业务限流范围内。默认短信验证码:使用同一签名,对同一个手机号验证码,支持 1 条 / 分钟,5 条 / 小时,累计 10 条 / 天。
  23 + ErrorCode SMS_SEND_BUSINESS_LIMIT_CONTROL = new ErrorCode(2001000102, "指定手机的发送限流");
  24 + // 阿里云:已经达到您在控制台设置的短信日发送量限额值。在国内消息设置 > 安全设置,修改发送总量阈值。
  25 + ErrorCode SMS_SEND_DAY_LIMIT_CONTROL = new ErrorCode(2001000103, "每天的发送限流");
  26 +
  27 + ErrorCode SMS_SEND_CONTENT_INVALID = new ErrorCode(2001000104, "短信内容有敏感词");
  28 +
  29 + // ========== 模板相关 2001000200 ==========
  30 + ErrorCode SMS_TEMPLATE_INVALID = new ErrorCode(2001000200, "短信模板不合法"); // 包括短信模板不存在
  31 + ErrorCode SMS_TEMPLATE_PARAM_ERROR = new ErrorCode(2001000201, "模板参数不正确");
  32 +
  33 + // ========== 签名相关 2001000300 ==========
  34 + ErrorCode SMS_SIGN_INVALID = new ErrorCode(2001000300, "短信签名不可用");
  35 +
  36 + // ========== 账户相关 2001000400 ==========
  37 + ErrorCode SMS_ACCOUNT_MONEY_NOT_ENOUGH = new ErrorCode(2001000400, "账户余额不足");
  38 + ErrorCode SMS_ACCOUNT_INVALID = new ErrorCode(2001000401, "apiKey 不存在");
  39 +
  40 + // ========== 其它相关 2001000900 开头 ==========
  41 + ErrorCode SMS_API_PARAM_ERROR = new ErrorCode(2001000900, "请求参数缺失");
  42 + ErrorCode SMS_MOBILE_INVALID = new ErrorCode(2001000901, "手机格式不正确");
  43 + ErrorCode SMS_MOBILE_BLACK = new ErrorCode(2001000902, "手机号在黑名单中");
  44 +
  45 + ErrorCode EXCEPTION = new ErrorCode(2001000999, "调用异常");
  46 +
  47 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsTemplateAuditStatusEnum.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.enums;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * 短信模板的审核状态枚举
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +@AllArgsConstructor
  12 +@Getter
  13 +public enum SmsTemplateAuditStatusEnum {
  14 +
  15 + CHECKING(1),
  16 + SUCCESS(2),
  17 + FAIL(3);
  18 +
  19 + private final Integer status;
  20 +
  21 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/property/SmsChannelProperties.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.property;
  2 +
  3 +import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;
  4 +import lombok.Data;
  5 +import org.springframework.validation.annotation.Validated;
  6 +
  7 +import javax.validation.constraints.NotEmpty;
  8 +import javax.validation.constraints.NotNull;
  9 +
  10 +/**
  11 + * 短信渠道配置类
  12 + *
  13 + * @author zzf
  14 + * @date 2021/1/25 17:01
  15 + */
  16 +@Data
  17 +@Validated
  18 +public class SmsChannelProperties {
  19 +
  20 + /**
  21 + * 渠道编号
  22 + */
  23 + @NotNull(message = "短信渠道 ID 不能为空")
  24 + private Long id;
  25 + /**
  26 + * 短信签名
  27 + */
  28 + @NotEmpty(message = "短信签名不能为空")
  29 + private String signature;
  30 + /**
  31 + * 渠道编码
  32 + *
  33 + * 枚举 {@link SmsChannelEnum}
  34 + */
  35 + @NotEmpty(message = "渠道编码不能为空")
  36 + private String code;
  37 + /**
  38 + * 短信 API 的账号
  39 + */
  40 + @NotEmpty(message = "短信 API 的账号不能为空")
  41 + private String apiKey;
  42 + /**
  43 + * 短信 API 的秘钥
  44 + */
  45 + @NotEmpty(message = "短信 API 的秘钥不能为空")
  46 + private String apiSecret;
  47 + /**
  48 + * 短信发送回调 URL
  49 + */
  50 + private String callbackUrl;
  51 +
  52 +}
src/main/java/cn/iocoder/dashboard/framework/web/core/handler/GlobalExceptionHandler.java
@@ -3,7 +3,6 @@ package cn.iocoder.dashboard.framework.web.core.handler; @@ -3,7 +3,6 @@ package cn.iocoder.dashboard.framework.web.core.handler;
3 import cn.hutool.core.exceptions.ExceptionUtil; 3 import cn.hutool.core.exceptions.ExceptionUtil;
4 import cn.hutool.core.map.MapUtil; 4 import cn.hutool.core.map.MapUtil;
5 import cn.hutool.extra.servlet.ServletUtil; 5 import cn.hutool.extra.servlet.ServletUtil;
6 -import cn.iocoder.dashboard.common.exception.GlobalException;  
7 import cn.iocoder.dashboard.common.exception.ServiceException; 6 import cn.iocoder.dashboard.common.exception.ServiceException;
8 import cn.iocoder.dashboard.common.pojo.CommonResult; 7 import cn.iocoder.dashboard.common.pojo.CommonResult;
9 import cn.iocoder.dashboard.framework.logger.apilog.core.service.ApiErrorLogFrameworkService; 8 import cn.iocoder.dashboard.framework.logger.apilog.core.service.ApiErrorLogFrameworkService;
@@ -96,9 +95,6 @@ public class GlobalExceptionHandler { @@ -96,9 +95,6 @@ public class GlobalExceptionHandler {
96 if (ex instanceof AccessDeniedException) { 95 if (ex instanceof AccessDeniedException) {
97 return accessDeniedExceptionHandler(request, (AccessDeniedException) ex); 96 return accessDeniedExceptionHandler(request, (AccessDeniedException) ex);
98 } 97 }
99 - if (ex instanceof GlobalException) {  
100 - return globalExceptionHandler(request, (GlobalException) ex);  
101 - }  
102 return defaultExceptionHandler(request, ex); 98 return defaultExceptionHandler(request, ex);
103 } 99 }
104 100
@@ -223,25 +219,6 @@ public class GlobalExceptionHandler { @@ -223,25 +219,6 @@ public class GlobalExceptionHandler {
223 } 219 }
224 220
225 /** 221 /**
226 - * 处理全局异常 ServiceException  
227 - *  
228 - * 例如说,Dubbo 请求超时,调用的 Dubbo 服务系统异常  
229 - */  
230 - @ExceptionHandler(value = GlobalException.class)  
231 - public CommonResult<?> globalExceptionHandler(HttpServletRequest req, GlobalException ex) {  
232 - // 系统异常时,才打印异常日志  
233 - if (INTERNAL_SERVER_ERROR.getCode().equals(ex.getCode())) {  
234 - // 插入异常日志  
235 - this.createExceptionLog(req, ex);  
236 - // 普通全局异常,打印 info 日志即可  
237 - } else {  
238 - log.info("[globalExceptionHandler]", ex);  
239 - }  
240 - // 返回 ERROR CommonResult  
241 - return CommonResult.error(ex);  
242 - }  
243 -  
244 - /**  
245 * 处理系统异常,兜底处理所有的一切 222 * 处理系统异常,兜底处理所有的一切
246 */ 223 */
247 @ExceptionHandler(value = Exception.class) 224 @ExceptionHandler(value = Exception.class)
@@ -250,7 +227,7 @@ public class GlobalExceptionHandler { @@ -250,7 +227,7 @@ public class GlobalExceptionHandler {
250 // 插入异常日志 227 // 插入异常日志
251 this.createExceptionLog(req, ex); 228 this.createExceptionLog(req, ex);
252 // 返回 ERROR CommonResult 229 // 返回 ERROR CommonResult
253 - return CommonResult.error(INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMessage()); 230 + return CommonResult.error(INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg());
254 } 231 }
255 232
256 private void createExceptionLog(HttpServletRequest req, Throwable e) { 233 private void createExceptionLog(HttpServletRequest req, Throwable e) {
src/main/java/cn/iocoder/dashboard/modules/system/controller/dict/SysDictDataController.java
@@ -61,7 +61,7 @@ public class SysDictDataController { @@ -61,7 +61,7 @@ public class SysDictDataController {
61 @GetMapping("/list-all-simple") 61 @GetMapping("/list-all-simple")
62 @ApiOperation(value = "获得全部字典数据列表", notes = "一般用于管理后台缓存字典数据在本地") 62 @ApiOperation(value = "获得全部字典数据列表", notes = "一般用于管理后台缓存字典数据在本地")
63 // 无需添加权限认证,因为前端全局都需要 63 // 无需添加权限认证,因为前端全局都需要
64 - public CommonResult<List<SysDictDataSimpleVO>> getSimpleDictDatas() { 64 + public CommonResult<List<SysDictDataSimpleRespVO>> getSimpleDictDatas() {
65 List<SysDictDataDO> list = dictDataService.getDictDatas(); 65 List<SysDictDataDO> list = dictDataService.getDictDatas();
66 return success(SysDictDataConvert.INSTANCE.convertList(list)); 66 return success(SysDictDataConvert.INSTANCE.convertList(list));
67 } 67 }
src/main/java/cn/iocoder/dashboard/modules/system/controller/dict/SysDictTypeController.java
@@ -41,7 +41,7 @@ public class SysDictTypeController { @@ -41,7 +41,7 @@ public class SysDictTypeController {
41 return success(dictTypeId); 41 return success(dictTypeId);
42 } 42 }
43 43
44 - @PostMapping("update") 44 + @PutMapping("/update")
45 @ApiOperation("修改字典类型") 45 @ApiOperation("修改字典类型")
46 @PreAuthorize("@ss.hasPermission('system:dict:update')") 46 @PreAuthorize("@ss.hasPermission('system:dict:update')")
47 public CommonResult<Boolean> updateDictType(@Valid @RequestBody SysDictTypeUpdateReqVO reqVO) { 47 public CommonResult<Boolean> updateDictType(@Valid @RequestBody SysDictTypeUpdateReqVO reqVO) {
src/main/java/cn/iocoder/dashboard/modules/system/controller/dict/vo/data/SysDictDataSimpleVO.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/controller/dict/vo/data/SysDictDataSimpleRespVO.java
@@ -4,9 +4,9 @@ import io.swagger.annotations.ApiModel; @@ -4,9 +4,9 @@ import io.swagger.annotations.ApiModel;
4 import io.swagger.annotations.ApiModelProperty; 4 import io.swagger.annotations.ApiModelProperty;
5 import lombok.Data; 5 import lombok.Data;
6 6
7 -@ApiModel("数据字典精简 VO") 7 +@ApiModel("数据字典精简 Response VO")
8 @Data 8 @Data
9 -public class SysDictDataSimpleVO { 9 +public class SysDictDataSimpleRespVO {
10 10
11 @ApiModelProperty(value = "字典类型", required = true, example = "gender") 11 @ApiModelProperty(value = "字典类型", required = true, example = "gender")
12 private String dictType; 12 private String dictType;
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SmsCallbackController.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms;
  2 +
  3 +import cn.hutool.core.util.URLUtil;
  4 +import cn.hutool.extra.servlet.ServletUtil;
  5 +import cn.iocoder.dashboard.common.pojo.CommonResult;
  6 +import cn.iocoder.dashboard.framework.logger.operatelog.core.annotations.OperateLog;
  7 +import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;
  8 +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService;
  9 +import io.swagger.annotations.Api;
  10 +import io.swagger.annotations.ApiImplicitParam;
  11 +import io.swagger.annotations.ApiOperation;
  12 +import org.springframework.web.bind.annotation.PostMapping;
  13 +import org.springframework.web.bind.annotation.RequestMapping;
  14 +import org.springframework.web.bind.annotation.RequestParam;
  15 +import org.springframework.web.bind.annotation.RestController;
  16 +
  17 +import javax.annotation.Resource;
  18 +import javax.servlet.http.HttpServletRequest;
  19 +
  20 +import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
  21 +
  22 +@Api(tags = "短信回调")
  23 +@RestController
  24 +@RequestMapping("/system/sms/callback")
  25 +public class SmsCallbackController {
  26 +
  27 + @Resource
  28 + private SysSmsService smsService;
  29 +
  30 + @PostMapping("/sms/yunpian")
  31 + @ApiOperation(value = "云片短信的回调", notes = "参见 https://www.yunpian.com/official/document/sms/zh_cn/domestic_push_report 文档")
  32 + @ApiImplicitParam(name = "sms_status", value = "发送状态", required = true, example = "[{具体内容}]", dataTypeClass = Long.class)
  33 + @OperateLog(enable = false)
  34 + public String receiveYunpianSmsStatus(@RequestParam("sms_status") String smsStatus) throws Throwable {
  35 + String text = URLUtil.decode(smsStatus); // decode 解码参数,因为它被 encode
  36 + smsService.receiveSmsStatus(SmsChannelEnum.YUN_PIAN.getCode(), text);
  37 + return "SUCCESS"; // 约定返回 SUCCESS 为成功
  38 + }
  39 +
  40 + @PostMapping("/sms/aliyun")
  41 + @ApiOperation(value = "阿里云短信的回调", notes = "参见 https://help.aliyun.com/document_detail/120998.html 文档")
  42 + @OperateLog(enable = false)
  43 + public CommonResult<Boolean> receiveAliyunSmsStatus(HttpServletRequest request) throws Throwable {
  44 + String text = ServletUtil.getBody(request);
  45 + smsService.receiveSmsStatus(SmsChannelEnum.ALIYUN.getCode(), text);
  46 + return success(true);
  47 + }
  48 +
  49 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SysSmsChannelController.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms;
  2 +
  3 +import cn.iocoder.dashboard.common.pojo.CommonResult;
  4 +import cn.iocoder.dashboard.common.pojo.PageResult;
  5 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.*;
  6 +import cn.iocoder.dashboard.modules.system.convert.sms.SysSmsChannelConvert;
  7 +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO;
  8 +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService;
  9 +import io.swagger.annotations.Api;
  10 +import io.swagger.annotations.ApiImplicitParam;
  11 +import io.swagger.annotations.ApiOperation;
  12 +import org.springframework.security.access.prepost.PreAuthorize;
  13 +import org.springframework.web.bind.annotation.*;
  14 +
  15 +import javax.annotation.Resource;
  16 +import javax.validation.Valid;
  17 +import java.util.Comparator;
  18 +import java.util.List;
  19 +
  20 +import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
  21 +
  22 +@Api(tags = "短信渠道")
  23 +@RestController
  24 +@RequestMapping("system/sms-channel")
  25 +public class SysSmsChannelController {
  26 +
  27 + @Resource
  28 + private SysSmsChannelService smsChannelService;
  29 +
  30 + @PostMapping("/create")
  31 + @ApiOperation("创建短信渠道")
  32 + @PreAuthorize("@ss.hasPermission('system:sms-channel:create')")
  33 + public CommonResult<Long> createSmsChannel(@Valid @RequestBody SysSmsChannelCreateReqVO createReqVO) {
  34 + return success(smsChannelService.createSmsChannel(createReqVO));
  35 + }
  36 +
  37 + @PutMapping("/update")
  38 + @ApiOperation("更新短信渠道")
  39 + @PreAuthorize("@ss.hasPermission('system:sms-channel:update')")
  40 + public CommonResult<Boolean> updateSmsChannel(@Valid @RequestBody SysSmsChannelUpdateReqVO updateReqVO) {
  41 + smsChannelService.updateSmsChannel(updateReqVO);
  42 + return success(true);
  43 + }
  44 +
  45 + @DeleteMapping("/delete")
  46 + @ApiOperation("删除短信渠道")
  47 + @ApiImplicitParam(name = "id", value = "编号", required = true)
  48 + @PreAuthorize("@ss.hasPermission('system:sms-channel:delete')")
  49 + public CommonResult<Boolean> deleteSmsChannel(@RequestParam("id") Long id) {
  50 + smsChannelService.deleteSmsChannel(id);
  51 + return success(true);
  52 + }
  53 +
  54 + @GetMapping("/get")
  55 + @ApiOperation("获得短信渠道")
  56 + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
  57 + @PreAuthorize("@ss.hasPermission('system:sms-channel:query')")
  58 + public CommonResult<SysSmsChannelRespVO> getSmsChannel(@RequestParam("id") Long id) {
  59 + SysSmsChannelDO smsChannel = smsChannelService.getSmsChannel(id);
  60 + return success(SysSmsChannelConvert.INSTANCE.convert(smsChannel));
  61 + }
  62 +
  63 + @GetMapping("/page")
  64 + @ApiOperation("获得短信渠道分页")
  65 + @PreAuthorize("@ss.hasPermission('system:sms-channel:query')")
  66 + public CommonResult<PageResult<SysSmsChannelRespVO>> getSmsChannelPage(@Valid SysSmsChannelPageReqVO pageVO) {
  67 + PageResult<SysSmsChannelDO> pageResult = smsChannelService.getSmsChannelPage(pageVO);
  68 + return success(SysSmsChannelConvert.INSTANCE.convertPage(pageResult));
  69 + }
  70 +
  71 + @GetMapping("/list-all-simple")
  72 + @ApiOperation(value = "获得短信渠道精简列表", notes = "包含被禁用的短信渠道")
  73 + public CommonResult<List<SysSmsChannelSimpleRespVO>> getSimpleSmsChannels() {
  74 + List<SysSmsChannelDO> list = smsChannelService.getSmsChannelList();
  75 + // 排序后,返回给前端
  76 + list.sort(Comparator.comparing(SysSmsChannelDO::getId));
  77 + return success(SysSmsChannelConvert.INSTANCE.convertList03(list));
  78 + }
  79 +
  80 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SysSmsLogController.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms;
  2 +
  3 +import cn.iocoder.dashboard.common.pojo.CommonResult;
  4 +import cn.iocoder.dashboard.common.pojo.PageResult;
  5 +import cn.iocoder.dashboard.framework.excel.core.util.ExcelUtils;
  6 +import cn.iocoder.dashboard.framework.logger.operatelog.core.annotations.OperateLog;
  7 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogExcelVO;
  8 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogExportReqVO;
  9 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogPageReqVO;
  10 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogRespVO;
  11 +import cn.iocoder.dashboard.modules.system.convert.sms.SysSmsLogConvert;
  12 +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsLogDO;
  13 +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsLogService;
  14 +import io.swagger.annotations.Api;
  15 +import io.swagger.annotations.ApiOperation;
  16 +import org.springframework.security.access.prepost.PreAuthorize;
  17 +import org.springframework.validation.annotation.Validated;
  18 +import org.springframework.web.bind.annotation.GetMapping;
  19 +import org.springframework.web.bind.annotation.RequestMapping;
  20 +import org.springframework.web.bind.annotation.RestController;
  21 +
  22 +import javax.annotation.Resource;
  23 +import javax.servlet.http.HttpServletResponse;
  24 +import javax.validation.Valid;
  25 +import java.io.IOException;
  26 +import java.util.List;
  27 +
  28 +import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
  29 +import static cn.iocoder.dashboard.framework.logger.operatelog.core.enums.OperateTypeEnum.EXPORT;
  30 +
  31 +@Api(tags = "短信日志")
  32 +@RestController
  33 +@RequestMapping("/system/sms-log")
  34 +@Validated
  35 +public class SysSmsLogController {
  36 +
  37 + @Resource
  38 + private SysSmsLogService smsLogService;
  39 +
  40 + @GetMapping("/page")
  41 + @ApiOperation("获得短信日志分页")
  42 + @PreAuthorize("@ss.hasPermission('system:sms-log:query')")
  43 + public CommonResult<PageResult<SysSmsLogRespVO>> getSmsLogPage(@Valid SysSmsLogPageReqVO pageVO) {
  44 + PageResult<SysSmsLogDO> pageResult = smsLogService.getSmsLogPage(pageVO);
  45 + return success(SysSmsLogConvert.INSTANCE.convertPage(pageResult));
  46 + }
  47 +
  48 + @GetMapping("/export-excel")
  49 + @ApiOperation("导出短信日志 Excel")
  50 + @PreAuthorize("@ss.hasPermission('system:sms-log:export')")
  51 + @OperateLog(type = EXPORT)
  52 + public void exportSmsLogExcel(@Valid SysSmsLogExportReqVO exportReqVO,
  53 + HttpServletResponse response) throws IOException {
  54 + List<SysSmsLogDO> list = smsLogService.getSmsLogList(exportReqVO);
  55 + // 导出 Excel
  56 + List<SysSmsLogExcelVO> datas = SysSmsLogConvert.INSTANCE.convertList02(list);
  57 + ExcelUtils.write(response, "短信日志.xls", "数据", SysSmsLogExcelVO.class, datas);
  58 + }
  59 +
  60 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SysSmsTemplateController.http 0 → 100644
  1 +### 请求 /menu/list 接口 => 成功
  2 +POST {{baseUrl}}/system/sms-template/send-sms
  3 +Authorization: Bearer {{token}}
  4 +Content-Type: application/json
  5 +
  6 +{
  7 + "code": "test_01",
  8 + "params": {
  9 + "key01": "value01",
  10 + "key02": "value02"
  11 + }
  12 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SysSmsTemplateController.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms;
  2 +
  3 +import cn.iocoder.dashboard.common.pojo.CommonResult;
  4 +import cn.iocoder.dashboard.common.pojo.PageResult;
  5 +import cn.iocoder.dashboard.framework.excel.core.util.ExcelUtils;
  6 +import cn.iocoder.dashboard.framework.logger.operatelog.core.annotations.OperateLog;
  7 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.*;
  8 +import cn.iocoder.dashboard.modules.system.convert.sms.SysSmsTemplateConvert;
  9 +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
  10 +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService;
  11 +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsTemplateService;
  12 +import io.swagger.annotations.Api;
  13 +import io.swagger.annotations.ApiImplicitParam;
  14 +import io.swagger.annotations.ApiOperation;
  15 +import org.springframework.security.access.prepost.PreAuthorize;
  16 +import org.springframework.web.bind.annotation.*;
  17 +
  18 +import javax.annotation.Resource;
  19 +import javax.servlet.http.HttpServletResponse;
  20 +import javax.validation.Valid;
  21 +import java.io.IOException;
  22 +import java.util.List;
  23 +
  24 +import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
  25 +import static cn.iocoder.dashboard.framework.logger.operatelog.core.enums.OperateTypeEnum.EXPORT;
  26 +
  27 +@Api("短信模板")
  28 +@RestController
  29 +@RequestMapping("/system/sms-template")
  30 +public class SysSmsTemplateController {
  31 +
  32 + @Resource
  33 + private SysSmsTemplateService smsTemplateService;
  34 + @Resource
  35 + private SysSmsService smsService;
  36 +
  37 + @PostMapping("/create")
  38 + @ApiOperation("创建短信模板")
  39 + @PreAuthorize("@ss.hasPermission('system:sms-template:create')")
  40 + public CommonResult<Long> createSmsTemplate(@Valid @RequestBody SysSmsTemplateCreateReqVO createReqVO) {
  41 + return success(smsTemplateService.createSmsTemplate(createReqVO));
  42 + }
  43 +
  44 + @PutMapping("/update")
  45 + @ApiOperation("更新短信模板")
  46 + @PreAuthorize("@ss.hasPermission('system:sms-template:update')")
  47 + public CommonResult<Boolean> updateSmsTemplate(@Valid @RequestBody SysSmsTemplateUpdateReqVO updateReqVO) {
  48 + smsTemplateService.updateSmsTemplate(updateReqVO);
  49 + return success(true);
  50 + }
  51 +
  52 + @DeleteMapping("/delete")
  53 + @ApiOperation("删除短信模板")
  54 + @ApiImplicitParam(name = "id", value = "编号", required = true)
  55 + @PreAuthorize("@ss.hasPermission('system:sms-template:delete')")
  56 + public CommonResult<Boolean> deleteSmsTemplate(@RequestParam("id") Long id) {
  57 + smsTemplateService.deleteSmsTemplate(id);
  58 + return success(true);
  59 + }
  60 +
  61 + @GetMapping("/get")
  62 + @ApiOperation("获得短信模板")
  63 + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
  64 + @PreAuthorize("@ss.hasPermission('system:sms-template:query')")
  65 + public CommonResult<SysSmsTemplateRespVO> getSmsTemplate(@RequestParam("id") Long id) {
  66 + SysSmsTemplateDO smsTemplate = smsTemplateService.getSmsTemplate(id);
  67 + return success(SysSmsTemplateConvert.INSTANCE.convert(smsTemplate));
  68 + }
  69 +
  70 + @GetMapping("/page")
  71 + @ApiOperation("获得短信模板分页")
  72 + @PreAuthorize("@ss.hasPermission('system:sms-template:query')")
  73 + public CommonResult<PageResult<SysSmsTemplateRespVO>> getSmsTemplatePage(@Valid SysSmsTemplatePageReqVO pageVO) {
  74 + PageResult<SysSmsTemplateDO> pageResult = smsTemplateService.getSmsTemplatePage(pageVO);
  75 + return success(SysSmsTemplateConvert.INSTANCE.convertPage(pageResult));
  76 + }
  77 +
  78 + @GetMapping("/export-excel")
  79 + @ApiOperation("导出短信模板 Excel")
  80 + @PreAuthorize("@ss.hasPermission('system:sms-template:export')")
  81 + @OperateLog(type = EXPORT)
  82 + public void exportSmsTemplateExcel(@Valid SysSmsTemplateExportReqVO exportReqVO,
  83 + HttpServletResponse response) throws IOException {
  84 + List<SysSmsTemplateDO> list = smsTemplateService.getSmsTemplateList(exportReqVO);
  85 + // 导出 Excel
  86 + List<SysSmsTemplateExcelVO> datas = SysSmsTemplateConvert.INSTANCE.convertList02(list);
  87 + ExcelUtils.write(response, "短信模板.xls", "数据", SysSmsTemplateExcelVO.class, datas);
  88 + }
  89 +
  90 + @PostMapping("/send-sms")
  91 + @ApiOperation("发送短信")
  92 + @PreAuthorize("@ss.hasPermission('system:sms-template:send-sms')")
  93 + public CommonResult<Long> sendSms(@Valid @RequestBody SysSmsTemplateSendReqVO sendReqVO) {
  94 + return success(smsService.sendSingleSms(sendReqVO.getMobile(), null, null,
  95 + sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams()));
  96 + }
  97 +
  98 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/channel/SysSmsChannelBaseVO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms.vo.channel;
  2 +
  3 +import io.swagger.annotations.ApiModelProperty;
  4 +import lombok.Data;
  5 +import org.hibernate.validator.constraints.URL;
  6 +
  7 +import javax.validation.constraints.NotNull;
  8 +
  9 +/**
  10 +* 短信渠道 Base VO,提供给添加、修改、详细的子 VO 使用
  11 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
  12 +*/
  13 +@Data
  14 +public class SysSmsChannelBaseVO {
  15 +
  16 + @ApiModelProperty(value = "短信签名", required = true, example = "芋道源码")
  17 + @NotNull(message = "短信签名不能为空")
  18 + private String signature;
  19 +
  20 + @ApiModelProperty(value = "启用状态", required = true, example = "1")
  21 + @NotNull(message = "启用状态不能为空")
  22 + private Integer status;
  23 +
  24 + @ApiModelProperty(value = "备注", example = "好吃!")
  25 + private String remark;
  26 +
  27 + @ApiModelProperty(value = "短信 API 的账号", required = true, example = "yudao")
  28 + @NotNull(message = "短信 API 的账号不能为空")
  29 + private String apiKey;
  30 +
  31 + @ApiModelProperty(value = "短信 API 的秘钥", example = "yuanma")
  32 + private String apiSecret;
  33 +
  34 + @ApiModelProperty(value = "短信发送回调 URL", example = "http://www.iocoder.cn")
  35 + @URL(message = "回调 URL 格式不正确")
  36 + private String callbackUrl;
  37 +
  38 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/channel/SysSmsChannelCreateReqVO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms.vo.channel;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import javax.validation.constraints.NotNull;
  10 +
  11 +@ApiModel("短信渠道创建 Request VO")
  12 +@Data
  13 +@EqualsAndHashCode(callSuper = true)
  14 +@ToString(callSuper = true)
  15 +public class SysSmsChannelCreateReqVO extends SysSmsChannelBaseVO {
  16 +
  17 + @ApiModelProperty(value = "渠道编码", required = true, example = "YUN_PIAN", notes = "参见 SmsChannelEnum 枚举类")
  18 + @NotNull(message = "渠道编码不能为空")
  19 + private String code;
  20 +
  21 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/channel/SysSmsChannelPageReqVO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms.vo.channel;
  2 +
  3 +import cn.iocoder.dashboard.common.pojo.PageParam;
  4 +import io.swagger.annotations.ApiModel;
  5 +import io.swagger.annotations.ApiModelProperty;
  6 +import lombok.Data;
  7 +import lombok.EqualsAndHashCode;
  8 +import lombok.ToString;
  9 +import org.springframework.format.annotation.DateTimeFormat;
  10 +
  11 +import java.util.Date;
  12 +
  13 +import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  14 +
  15 +@ApiModel("短信渠道分页 Request VO")
  16 +@Data
  17 +@EqualsAndHashCode(callSuper = true)
  18 +@ToString(callSuper = true)
  19 +public class SysSmsChannelPageReqVO extends PageParam {
  20 +
  21 + @ApiModelProperty(value = "任务状态", example = "1")
  22 + private Integer status;
  23 +
  24 + @ApiModelProperty(value = "短信签名", example = "芋道源码", notes = "模糊匹配")
  25 + private String signature;
  26 +
  27 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  28 + @ApiModelProperty(value = "开始创建时间")
  29 + private Date beginCreateTime;
  30 +
  31 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  32 + @ApiModelProperty(value = "结束创建时间")
  33 + private Date endCreateTime;
  34 +
  35 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/channel/SysSmsChannelRespVO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms.vo.channel;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import java.util.Date;
  10 +
  11 +@ApiModel("短信渠道 Response VO")
  12 +@Data
  13 +@EqualsAndHashCode(callSuper = true)
  14 +@ToString(callSuper = true)
  15 +public class SysSmsChannelRespVO extends SysSmsChannelBaseVO {
  16 +
  17 + @ApiModelProperty(value = "编号", required = true, example = "1024")
  18 + private Long id;
  19 +
  20 + @ApiModelProperty(value = "渠道编码", required = true, example = "YUN_PIAN", notes = "参见 SmsChannelEnum 枚举类")
  21 + private String code;
  22 +
  23 + @ApiModelProperty(value = "创建时间", required = true)
  24 + private Date createTime;
  25 +
  26 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/channel/SysSmsChannelSimpleRespVO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms.vo.channel;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +
  7 +import javax.validation.constraints.NotNull;
  8 +
  9 +@ApiModel("短信渠道精简 Response VO")
  10 +@Data
  11 +public class SysSmsChannelSimpleRespVO {
  12 +
  13 + @ApiModelProperty(value = "编号", required = true, example = "1024")
  14 + @NotNull(message = "编号不能为空")
  15 + private Long id;
  16 +
  17 + @ApiModelProperty(value = "短信签名", required = true, example = "芋道源码")
  18 + @NotNull(message = "短信签名不能为空")
  19 + private String signature;
  20 +
  21 + @ApiModelProperty(value = "渠道编码", required = true, example = "YUN_PIAN", notes = "参见 SmsChannelEnum 枚举类")
  22 + private String code;
  23 +
  24 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/channel/SysSmsChannelUpdateReqVO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms.vo.channel;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import javax.validation.constraints.NotNull;
  10 +
  11 +@ApiModel("短信渠道更新 Request VO")
  12 +@Data
  13 +@EqualsAndHashCode(callSuper = true)
  14 +@ToString(callSuper = true)
  15 +public class SysSmsChannelUpdateReqVO extends SysSmsChannelBaseVO {
  16 +
  17 + @ApiModelProperty(value = "编号", required = true, example = "1024")
  18 + @NotNull(message = "编号不能为空")
  19 + private Long id;
  20 +
  21 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/log/SysSmsLogExcelVO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms.vo.log;
  2 +
  3 +import cn.iocoder.dashboard.framework.excel.core.annotations.DictFormat;
  4 +import cn.iocoder.dashboard.framework.excel.core.convert.DictConvert;
  5 +import cn.iocoder.dashboard.framework.excel.core.convert.JsonConvert;
  6 +import com.alibaba.excel.annotation.ExcelProperty;
  7 +import lombok.Data;
  8 +
  9 +import java.util.Date;
  10 +import java.util.Map;
  11 +
  12 +import static cn.iocoder.dashboard.modules.system.enums.dict.SysDictTypeEnum.*;
  13 +
  14 +/**
  15 + * 短信日志 Excel VO
  16 + *
  17 + * @author 芋道源码
  18 + */
  19 +@Data
  20 +public class SysSmsLogExcelVO {
  21 +
  22 + @ExcelProperty("编号")
  23 + private Long id;
  24 +
  25 + @ExcelProperty("短信渠道编号")
  26 + private Long channelId;
  27 +
  28 + @ExcelProperty("短信渠道编码")
  29 + private String channelCode;
  30 +
  31 + @ExcelProperty("模板编号")
  32 + private Long templateId;
  33 +
  34 + @ExcelProperty("模板编码")
  35 + private String templateCode;
  36 +
  37 + @ExcelProperty(value = "短信类型", converter = DictConvert.class)
  38 + @DictFormat(SYS_SMS_TEMPLATE_TYPE)
  39 + private Integer templateType;
  40 +
  41 + @ExcelProperty("短信内容")
  42 + private String templateContent;
  43 +
  44 + @ExcelProperty(value = "短信参数", converter = JsonConvert.class)
  45 + private Map<String, Object> templateParams;
  46 +
  47 + @ExcelProperty("短信 API 的模板编号")
  48 + private String apiTemplateId;
  49 +
  50 + @ExcelProperty("手机号")
  51 + private String mobile;
  52 +
  53 + @ExcelProperty("用户编号")
  54 + private Long userId;
  55 +
  56 + @ExcelProperty(value = "用户类型", converter = DictConvert.class)
  57 + @DictFormat(USER_TYPE)
  58 + private Integer userType;
  59 +
  60 + @ExcelProperty(value = "发送状态", converter = DictConvert.class)
  61 + @DictFormat(SYS_SMS_SEND_STATUS)
  62 + private Integer sendStatus;
  63 +
  64 + @ExcelProperty("发送时间")
  65 + private Date sendTime;
  66 +
  67 + @ExcelProperty("发送结果的编码")
  68 + private Integer sendCode;
  69 +
  70 + @ExcelProperty("发送结果的提示")
  71 + private String sendMsg;
  72 +
  73 + @ExcelProperty("短信 API 发送结果的编码")
  74 + private String apiSendCode;
  75 +
  76 + @ExcelProperty("短信 API 发送失败的提示")
  77 + private String apiSendMsg;
  78 +
  79 + @ExcelProperty("短信 API 发送返回的唯一请求 ID")
  80 + private String apiRequestId;
  81 +
  82 + @ExcelProperty("短信 API 发送返回的序号")
  83 + private String apiSerialNo;
  84 +
  85 + @ExcelProperty(value = "接收状态", converter = DictConvert.class)
  86 + @DictFormat(SYS_SMS_RECEIVE_STATUS)
  87 + private Integer receiveStatus;
  88 +
  89 + @ExcelProperty("接收时间")
  90 + private Date receiveTime;
  91 +
  92 + @ExcelProperty("API 接收结果的编码")
  93 + private String apiReceiveCode;
  94 +
  95 + @ExcelProperty("API 接收结果的说明")
  96 + private String apiReceiveMsg;
  97 +
  98 + @ExcelProperty("创建时间")
  99 + private Date createTime;
  100 +
  101 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/log/SysSmsLogExportReqVO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms.vo.log;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import org.springframework.format.annotation.DateTimeFormat;
  7 +
  8 +import java.util.Date;
  9 +
  10 +import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  11 +
  12 +@ApiModel(value = "短信日志 Excel 导出 Request VO", description = "参数和 SysSmsLogPageReqVO 是一致的")
  13 +@Data
  14 +public class SysSmsLogExportReqVO {
  15 +
  16 + @ApiModelProperty(value = "短信渠道编号", example = "10")
  17 + private Long channelId;
  18 +
  19 + @ApiModelProperty(value = "模板编号", example = "20")
  20 + private Long templateId;
  21 +
  22 + @ApiModelProperty(value = "手机号", example = "15601691300")
  23 + private String mobile;
  24 +
  25 + @ApiModelProperty(value = "发送状态", example = "1")
  26 + private Integer sendStatus;
  27 +
  28 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  29 + @ApiModelProperty(value = "开始发送时间")
  30 + private Date beginSendTime;
  31 +
  32 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  33 + @ApiModelProperty(value = "结束发送时间")
  34 + private Date endSendTime;
  35 +
  36 + @ApiModelProperty(value = "接收状态", example = "0")
  37 + private Integer receiveStatus;
  38 +
  39 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  40 + @ApiModelProperty(value = "开始接收时间")
  41 + private Date beginReceiveTime;
  42 +
  43 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  44 + @ApiModelProperty(value = "结束接收时间")
  45 + private Date endReceiveTime;
  46 +
  47 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/log/SysSmsLogPageReqVO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms.vo.log;
  2 +
  3 +import cn.iocoder.dashboard.common.pojo.PageParam;
  4 +import io.swagger.annotations.ApiModel;
  5 +import io.swagger.annotations.ApiModelProperty;
  6 +import lombok.Data;
  7 +import lombok.EqualsAndHashCode;
  8 +import lombok.ToString;
  9 +import org.springframework.format.annotation.DateTimeFormat;
  10 +
  11 +import java.util.Date;
  12 +
  13 +import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  14 +
  15 +@ApiModel("短信日志分页 Request VO")
  16 +@Data
  17 +@EqualsAndHashCode(callSuper = true)
  18 +@ToString(callSuper = true)
  19 +public class SysSmsLogPageReqVO extends PageParam {
  20 +
  21 + @ApiModelProperty(value = "短信渠道编号", example = "10")
  22 + private Long channelId;
  23 +
  24 + @ApiModelProperty(value = "模板编号", example = "20")
  25 + private Long templateId;
  26 +
  27 + @ApiModelProperty(value = "手机号", example = "15601691300")
  28 + private String mobile;
  29 +
  30 + @ApiModelProperty(value = "发送状态", example = "1", notes = "参见 SysSmsSendStatusEnum 枚举类")
  31 + private Integer sendStatus;
  32 +
  33 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  34 + @ApiModelProperty(value = "开始发送时间")
  35 + private Date beginSendTime;
  36 +
  37 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  38 + @ApiModelProperty(value = "结束发送时间")
  39 + private Date endSendTime;
  40 +
  41 + @ApiModelProperty(value = "接收状态", example = "0", notes = "参见 SysSmsReceiveStatusEnum 枚举类")
  42 + private Integer receiveStatus;
  43 +
  44 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  45 + @ApiModelProperty(value = "开始接收时间")
  46 + private Date beginReceiveTime;
  47 +
  48 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  49 + @ApiModelProperty(value = "结束接收时间")
  50 + private Date endReceiveTime;
  51 +
  52 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/log/SysSmsLogRespVO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms.vo.log;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +
  7 +import java.util.Date;
  8 +import java.util.Map;
  9 +
  10 +@ApiModel("短信日志 Response VO")
  11 +@Data
  12 +public class SysSmsLogRespVO {
  13 +
  14 + @ApiModelProperty(value = "编号", required = true, example = "1024")
  15 + private Long id;
  16 +
  17 + @ApiModelProperty(value = "短信渠道编号", required = true, example = "10")
  18 + private Long channelId;
  19 +
  20 + @ApiModelProperty(value = "短信渠道编码", required = true, example = "ALIYUN")
  21 + private String channelCode;
  22 +
  23 + @ApiModelProperty(value = "模板编号", required = true, example = "20")
  24 + private Long templateId;
  25 +
  26 + @ApiModelProperty(value = "模板编码", required = true, example = "test-01")
  27 + private String templateCode;
  28 +
  29 + @ApiModelProperty(value = "短信类型", required = true, example = "1")
  30 + private Integer templateType;
  31 +
  32 + @ApiModelProperty(value = "短信内容", required = true, example = "你好,你的验证码是 1024")
  33 + private String templateContent;
  34 +
  35 + @ApiModelProperty(value = "短信参数", required = true, example = "name,code")
  36 + private Map<String, Object> templateParams;
  37 +
  38 + @ApiModelProperty(value = "短信 API 的模板编号", required = true, example = "SMS_207945135")
  39 + private String apiTemplateId;
  40 +
  41 + @ApiModelProperty(value = "手机号", required = true, example = "15601691300")
  42 + private String mobile;
  43 +
  44 + @ApiModelProperty(value = "用户编号", example = "10")
  45 + private Long userId;
  46 +
  47 + @ApiModelProperty(value = "用户类型", example = "1")
  48 + private Integer userType;
  49 +
  50 + @ApiModelProperty(value = "发送状态", required = true, example = "1")
  51 + private Integer sendStatus;
  52 +
  53 + @ApiModelProperty(value = "发送时间")
  54 + private Date sendTime;
  55 +
  56 + @ApiModelProperty(value = "发送结果的编码", example = "0")
  57 + private Integer sendCode;
  58 +
  59 + @ApiModelProperty(value = "发送结果的提示", example = "成功")
  60 + private String sendMsg;
  61 +
  62 + @ApiModelProperty(value = "短信 API 发送结果的编码", example = "SUCCESS")
  63 + private String apiSendCode;
  64 +
  65 + @ApiModelProperty(value = "短信 API 发送失败的提示", example = "成功")
  66 + private String apiSendMsg;
  67 +
  68 + @ApiModelProperty(value = "短信 API 发送返回的唯一请求 ID", example = "3837C6D3-B96F-428C-BBB2-86135D4B5B99")
  69 + private String apiRequestId;
  70 +
  71 + @ApiModelProperty(value = "短信 API 发送返回的序号", example = "62923244790")
  72 + private String apiSerialNo;
  73 +
  74 + @ApiModelProperty(value = "接收状态", required = true, example = "0")
  75 + private Integer receiveStatus;
  76 +
  77 + @ApiModelProperty(value = "接收时间")
  78 + private Date receiveTime;
  79 +
  80 + @ApiModelProperty(value = "API 接收结果的编码", example = "DELIVRD")
  81 + private String apiReceiveCode;
  82 +
  83 + @ApiModelProperty(value = "API 接收结果的说明", example = "用户接收成功")
  84 + private String apiReceiveMsg;
  85 +
  86 + @ApiModelProperty(value = "创建时间", required = true)
  87 + private Date createTime;
  88 +
  89 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/template/SysSmsTemplateBaseVO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms.vo.template;
  2 +
  3 +import io.swagger.annotations.ApiModelProperty;
  4 +import lombok.Data;
  5 +
  6 +import javax.validation.constraints.NotNull;
  7 +
  8 +/**
  9 +* 短信模板 Base VO,提供给添加、修改、详细的子 VO 使用
  10 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
  11 +*/
  12 +@Data
  13 +public class SysSmsTemplateBaseVO {
  14 +
  15 + @ApiModelProperty(value = "短信类型", required = true, example = "1", notes = "参见 SysSmsTemplateTypeEnum 枚举类")
  16 + @NotNull(message = "短信类型不能为空")
  17 + private Integer type;
  18 +
  19 + @ApiModelProperty(value = "开启状态", required = true, example = "1", notes = "参见 CommonStatusEnum 枚举类")
  20 + @NotNull(message = "开启状态不能为空")
  21 + private Integer status;
  22 +
  23 + @ApiModelProperty(value = "模板编码", required = true, example = "test_01")
  24 + @NotNull(message = "模板编码不能为空")
  25 + private String code;
  26 +
  27 + @ApiModelProperty(value = "模板名称", required = true, example = "yudao")
  28 + @NotNull(message = "模板名称不能为空")
  29 + private String name;
  30 +
  31 + @ApiModelProperty(value = "模板内容", required = true, example = "你好,{name}。你长的太{like}啦!")
  32 + @NotNull(message = "模板内容不能为空")
  33 + private String content;
  34 +
  35 + @ApiModelProperty(value = "备注", example = "哈哈哈")
  36 + private String remark;
  37 +
  38 + @ApiModelProperty(value = "短信 API 的模板编号", required = true, example = "4383920")
  39 + @NotNull(message = "短信 API 的模板编号不能为空")
  40 + private String apiTemplateId;
  41 +
  42 + @ApiModelProperty(value = "短信渠道编号", required = true, example = "10")
  43 + @NotNull(message = "短信渠道编号不能为空")
  44 + private Long channelId;
  45 +
  46 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/template/SysSmsTemplateCreateReqVO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms.vo.template;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import lombok.Data;
  5 +import lombok.EqualsAndHashCode;
  6 +import lombok.ToString;
  7 +
  8 +@ApiModel("短信模板创建 Request VO")
  9 +@Data
  10 +@EqualsAndHashCode(callSuper = true)
  11 +@ToString(callSuper = true)
  12 +public class SysSmsTemplateCreateReqVO extends SysSmsTemplateBaseVO {
  13 +
  14 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/template/SysSmsTemplateExcelVO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms.vo.template;
  2 +
  3 +import cn.iocoder.dashboard.framework.excel.core.annotations.DictFormat;
  4 +import cn.iocoder.dashboard.framework.excel.core.convert.DictConvert;
  5 +import com.alibaba.excel.annotation.ExcelProperty;
  6 +import lombok.Data;
  7 +
  8 +import java.util.Date;
  9 +
  10 +import static cn.iocoder.dashboard.modules.system.enums.dict.SysDictTypeEnum.*;
  11 +
  12 +/**
  13 + * 短信模板 Excel VO
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Data
  18 +public class SysSmsTemplateExcelVO {
  19 +
  20 + @ExcelProperty("编号")
  21 + private Long id;
  22 +
  23 + @ExcelProperty(value = "短信签名", converter = DictConvert.class)
  24 + @DictFormat(SYS_SMS_TEMPLATE_TYPE)
  25 + private Integer type;
  26 +
  27 + @ExcelProperty(value = "开启状态", converter = DictConvert.class)
  28 + @DictFormat(SYS_COMMON_STATUS)
  29 + private Integer status;
  30 +
  31 + @ExcelProperty("模板编码")
  32 + private String code;
  33 +
  34 + @ExcelProperty("模板名称")
  35 + private String name;
  36 +
  37 + @ExcelProperty("模板内容")
  38 + private String content;
  39 +
  40 + @ExcelProperty("备注")
  41 + private String remark;
  42 +
  43 + @ExcelProperty("短信 API 的模板编号")
  44 + private String apiTemplateId;
  45 +
  46 + @ExcelProperty("短信渠道编号")
  47 + private Long channelId;
  48 +
  49 + @ExcelProperty(value = "短信渠道编码", converter = DictConvert.class)
  50 + @DictFormat(SYS_SMS_CHANNEL_CODE)
  51 + private String channelCode;
  52 +
  53 + @ExcelProperty("创建时间")
  54 + private Date createTime;
  55 +
  56 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/template/SysSmsTemplateExportReqVO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms.vo.template;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import org.springframework.format.annotation.DateTimeFormat;
  7 +
  8 +import java.util.Date;
  9 +
  10 +import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  11 +
  12 +@ApiModel(value = "短信模板 Excel 导出 Request VO", description = "参数和 SysSmsTemplatePageReqVO 是一致的")
  13 +@Data
  14 +public class SysSmsTemplateExportReqVO {
  15 +
  16 + @ApiModelProperty(value = "短信签名", example = "1")
  17 + private Integer type;
  18 +
  19 + @ApiModelProperty(value = "开启状态", example = "1")
  20 + private Integer status;
  21 +
  22 + @ApiModelProperty(value = "模板编码", example = "test_01", notes = "模糊匹配")
  23 + private String code;
  24 +
  25 + @ApiModelProperty(value = "模板内容", example = "你好,{name}。你长的太{like}啦!", notes = "模糊匹配")
  26 + private String content;
  27 +
  28 + @ApiModelProperty(value = "短信 API 的模板编号", example = "4383920", notes = "模糊匹配")
  29 + private String apiTemplateId;
  30 +
  31 + @ApiModelProperty(value = "短信渠道编号", example = "10")
  32 + private Long channelId;
  33 +
  34 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  35 + @ApiModelProperty(value = "开始创建时间")
  36 + private Date beginCreateTime;
  37 +
  38 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  39 + @ApiModelProperty(value = "结束创建时间")
  40 + private Date endCreateTime;
  41 +
  42 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/template/SysSmsTemplatePageReqVO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms.vo.template;
  2 +
  3 +import cn.iocoder.dashboard.common.pojo.PageParam;
  4 +import io.swagger.annotations.ApiModel;
  5 +import io.swagger.annotations.ApiModelProperty;
  6 +import lombok.Data;
  7 +import lombok.EqualsAndHashCode;
  8 +import lombok.ToString;
  9 +import org.springframework.format.annotation.DateTimeFormat;
  10 +
  11 +import java.util.Date;
  12 +
  13 +import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  14 +
  15 +@ApiModel("短信模板分页 Request VO")
  16 +@Data
  17 +@EqualsAndHashCode(callSuper = true)
  18 +@ToString(callSuper = true)
  19 +public class SysSmsTemplatePageReqVO extends PageParam {
  20 +
  21 + @ApiModelProperty(value = "短信签名", example = "1")
  22 + private Integer type;
  23 +
  24 + @ApiModelProperty(value = "开启状态", example = "1")
  25 + private Integer status;
  26 +
  27 + @ApiModelProperty(value = "模板编码", example = "test_01", notes = "模糊匹配")
  28 + private String code;
  29 +
  30 + @ApiModelProperty(value = "模板内容", example = "你好,{name}。你长的太{like}啦!", notes = "模糊匹配")
  31 + private String content;
  32 +
  33 + @ApiModelProperty(value = "短信 API 的模板编号", example = "4383920", notes = "模糊匹配")
  34 + private String apiTemplateId;
  35 +
  36 + @ApiModelProperty(value = "短信渠道编号", example = "10")
  37 + private Long channelId;
  38 +
  39 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  40 + @ApiModelProperty(value = "开始创建时间")
  41 + private Date beginCreateTime;
  42 +
  43 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  44 + @ApiModelProperty(value = "结束创建时间")
  45 + private Date endCreateTime;
  46 +
  47 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/template/SysSmsTemplateRespVO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms.vo.template;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import java.util.Date;
  10 +import java.util.List;
  11 +
  12 +@ApiModel("短信模板 Response VO")
  13 +@Data
  14 +@EqualsAndHashCode(callSuper = true)
  15 +@ToString(callSuper = true)
  16 +public class SysSmsTemplateRespVO extends SysSmsTemplateBaseVO {
  17 +
  18 + @ApiModelProperty(value = "编号", required = true, example = "1024")
  19 + private Long id;
  20 +
  21 + @ApiModelProperty(value = "短信渠道编码", required = true, example = "ALIYUN")
  22 + private String channelCode;
  23 +
  24 + @ApiModelProperty(value = "参数数组", example = "name,code")
  25 + private List<String> params;
  26 +
  27 + @ApiModelProperty(value = "创建时间", required = true)
  28 + private Date createTime;
  29 +
  30 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/template/SysSmsTemplateSendReqVO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms.vo.template;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +
  7 +import javax.validation.constraints.NotNull;
  8 +import java.util.Map;
  9 +
  10 +@ApiModel("短信模板的发送 Request VO")
  11 +@Data
  12 +public class SysSmsTemplateSendReqVO {
  13 +
  14 + @ApiModelProperty(value = "手机号", required = true, example = "15601691300")
  15 + @NotNull(message = "手机号不能为空")
  16 + private String mobile;
  17 +
  18 + @ApiModelProperty(value = "模板编码", required = true, example = "test_01")
  19 + @NotNull(message = "模板编码不能为空")
  20 + private String templateCode;
  21 +
  22 + @ApiModelProperty(value = "模板参数")
  23 + private Map<String, Object> templateParams;
  24 +
  25 +}
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/template/SysSmsTemplateUpdateReqVO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.controller.sms.vo.template;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import javax.validation.constraints.NotNull;
  10 +
  11 +@ApiModel("短信模板更新 Request VO")
  12 +@Data
  13 +@EqualsAndHashCode(callSuper = true)
  14 +@ToString(callSuper = true)
  15 +public class SysSmsTemplateUpdateReqVO extends SysSmsTemplateBaseVO {
  16 +
  17 + @ApiModelProperty(value = "编号", required = true, example = "1024")
  18 + @NotNull(message = "编号不能为空")
  19 + private Long id;
  20 +
  21 +}
src/main/java/cn/iocoder/dashboard/modules/system/convert/dict/SysDictDataConvert.java
@@ -13,7 +13,7 @@ public interface SysDictDataConvert { @@ -13,7 +13,7 @@ public interface SysDictDataConvert {
13 13
14 SysDictDataConvert INSTANCE = Mappers.getMapper(SysDictDataConvert.class); 14 SysDictDataConvert INSTANCE = Mappers.getMapper(SysDictDataConvert.class);
15 15
16 - List<SysDictDataSimpleVO> convertList(List<SysDictDataDO> list); 16 + List<SysDictDataSimpleRespVO> convertList(List<SysDictDataDO> list);
17 17
18 SysDictDataRespVO convert(SysDictDataDO bean); 18 SysDictDataRespVO convert(SysDictDataDO bean);
19 19
src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SysSmsChannelConvert.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.convert.sms;
  2 +
  3 +import cn.iocoder.dashboard.common.pojo.PageResult;
  4 +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
  5 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelCreateReqVO;
  6 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelRespVO;
  7 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelSimpleRespVO;
  8 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelUpdateReqVO;
  9 +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO;
  10 +import org.mapstruct.Mapper;
  11 +import org.mapstruct.factory.Mappers;
  12 +
  13 +import java.util.List;
  14 +
  15 +/**
  16 + * 短信渠道 Convert
  17 + *
  18 + * @author 芋道源码
  19 + */
  20 +@Mapper
  21 +public interface SysSmsChannelConvert {
  22 +
  23 + SysSmsChannelConvert INSTANCE = Mappers.getMapper(SysSmsChannelConvert.class);
  24 +
  25 + SysSmsChannelDO convert(SysSmsChannelCreateReqVO bean);
  26 +
  27 + SysSmsChannelDO convert(SysSmsChannelUpdateReqVO bean);
  28 +
  29 + SysSmsChannelRespVO convert(SysSmsChannelDO bean);
  30 +
  31 + List<SysSmsChannelRespVO> convertList(List<SysSmsChannelDO> list);
  32 +
  33 + PageResult<SysSmsChannelRespVO> convertPage(PageResult<SysSmsChannelDO> page);
  34 +
  35 + List<SmsChannelProperties> convertList02(List<SysSmsChannelDO> list);
  36 +
  37 + List<SysSmsChannelSimpleRespVO> convertList03(List<SysSmsChannelDO> list);
  38 +
  39 +}
src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SysSmsLogConvert.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.convert.sms;
  2 +
  3 +import cn.iocoder.dashboard.common.pojo.PageResult;
  4 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogExcelVO;
  5 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogRespVO;
  6 +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsLogDO;
  7 +import org.mapstruct.Mapper;
  8 +import org.mapstruct.factory.Mappers;
  9 +
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * 短信日志 Convert
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Mapper
  18 +public interface SysSmsLogConvert {
  19 +
  20 + SysSmsLogConvert INSTANCE = Mappers.getMapper(SysSmsLogConvert.class);
  21 +
  22 + SysSmsLogRespVO convert(SysSmsLogDO bean);
  23 +
  24 + List<SysSmsLogRespVO> convertList(List<SysSmsLogDO> list);
  25 +
  26 + PageResult<SysSmsLogRespVO> convertPage(PageResult<SysSmsLogDO> page);
  27 +
  28 + List<SysSmsLogExcelVO> convertList02(List<SysSmsLogDO> list);
  29 +
  30 +}
src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SysSmsTemplateConvert.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.convert.sms;
  2 +
  3 +import cn.iocoder.dashboard.common.pojo.PageResult;
  4 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplateCreateReqVO;
  5 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplateExcelVO;
  6 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplateRespVO;
  7 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplateUpdateReqVO;
  8 +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
  9 +import org.mapstruct.Mapper;
  10 +import org.mapstruct.factory.Mappers;
  11 +
  12 +import java.util.List;
  13 +
  14 +@Mapper
  15 +public interface SysSmsTemplateConvert {
  16 +
  17 + SysSmsTemplateConvert INSTANCE = Mappers.getMapper(SysSmsTemplateConvert.class);
  18 +
  19 + SysSmsTemplateDO convert(SysSmsTemplateCreateReqVO bean);
  20 +
  21 + SysSmsTemplateDO convert(SysSmsTemplateUpdateReqVO bean);
  22 +
  23 + SysSmsTemplateRespVO convert(SysSmsTemplateDO bean);
  24 +
  25 + List<SysSmsTemplateRespVO> convertList(List<SysSmsTemplateDO> list);
  26 +
  27 + PageResult<SysSmsTemplateRespVO> convertPage(PageResult<SysSmsTemplateDO> page);
  28 +
  29 + List<SysSmsTemplateExcelVO> convertList02(List<SysSmsTemplateDO> list);
  30 +
  31 +}
src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsChannelDO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.dal.dataobject.sms;
  2 +
  3 +import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;
  6 +import com.baomidou.mybatisplus.annotation.TableName;
  7 +import lombok.Data;
  8 +import lombok.EqualsAndHashCode;
  9 +import lombok.ToString;
  10 +
  11 +/**
  12 + * 短信渠道 DO
  13 + *
  14 + * @author zzf
  15 + * @since 2021-01-25
  16 + */
  17 +@TableName(value = "sys_sms_channel", autoResultMap = true)
  18 +@Data
  19 +@EqualsAndHashCode(callSuper = true)
  20 +@ToString(callSuper = true)
  21 +public class SysSmsChannelDO extends BaseDO {
  22 +
  23 + /**
  24 + * 渠道编号
  25 + */
  26 + private Long id;
  27 + /**
  28 + * 短信签名
  29 + */
  30 + private String signature;
  31 + /**
  32 + * 渠道编码
  33 + *
  34 + * 枚举 {@link SmsChannelEnum}
  35 + */
  36 + private String code;
  37 + /**
  38 + * 启用状态
  39 + *
  40 + * 枚举 {@link CommonStatusEnum}
  41 + */
  42 + private Integer status;
  43 + /**
  44 + * 备注
  45 + */
  46 + private String remark;
  47 + /**
  48 + * 短信 API 的账号
  49 + */
  50 + private String apiKey;
  51 + /**
  52 + * 短信 API 的秘钥
  53 + */
  54 + private String apiSecret;
  55 + /**
  56 + * 短信发送回调 URL
  57 + */
  58 + private String callbackUrl;
  59 +
  60 +}
src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsLogDO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.dal.dataobject.sms;
  2 +
  3 +import cn.iocoder.dashboard.common.enums.UserTypeEnum;
  4 +import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
  6 +import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsReceiveStatusEnum;
  7 +import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum;
  8 +import com.baomidou.mybatisplus.annotation.TableField;
  9 +import com.baomidou.mybatisplus.annotation.TableName;
  10 +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
  11 +import lombok.*;
  12 +
  13 +import java.util.Date;
  14 +import java.util.Map;
  15 +
  16 +/**
  17 + * 短信日志 DO
  18 + *
  19 + * @author zzf
  20 + * @since 2021-01-25
  21 + */
  22 +@TableName(value = "sys_sms_log", autoResultMap = true)
  23 +@Data
  24 +@EqualsAndHashCode(callSuper = true)
  25 +@ToString(callSuper = true)
  26 +@Builder
  27 +@AllArgsConstructor
  28 +@NoArgsConstructor
  29 +public class SysSmsLogDO extends BaseDO {
  30 +
  31 + /**
  32 + * 自增编号
  33 + */
  34 + private Long id;
  35 +
  36 + // ========= 渠道相关字段 =========
  37 +
  38 + /**
  39 + * 短信渠道编号
  40 + *
  41 + * 关联 {@link SysSmsChannelDO#getId()}
  42 + */
  43 + private Long channelId;
  44 + /**
  45 + * 短信渠道编码
  46 + *
  47 + * 冗余 {@link SysSmsChannelDO#getCode()}
  48 + */
  49 + private String channelCode;
  50 +
  51 + // ========= 模板相关字段 =========
  52 +
  53 + /**
  54 + * 模板编号
  55 + *
  56 + * 关联 {@link SysSmsTemplateDO#getId()}
  57 + */
  58 + private Long templateId;
  59 + /**
  60 + * 模板编码
  61 + *
  62 + * 冗余 {@link SysSmsTemplateDO#getCode()}
  63 + */
  64 + private String templateCode;
  65 + /**
  66 + * 短信类型
  67 + *
  68 + * 冗余 {@link SysSmsTemplateDO#getType()}
  69 + */
  70 + private Integer templateType;
  71 + /**
  72 + * 基于 {@link SysSmsTemplateDO#getContent()} 格式化后的内容
  73 + */
  74 + private String templateContent;
  75 + /**
  76 + * 基于 {@link SysSmsTemplateDO#getParams()} 输入后的参数
  77 + */
  78 + @TableField(typeHandler = JacksonTypeHandler.class)
  79 + private Map<String, Object> templateParams;
  80 + /**
  81 + * 短信 API 的模板编号
  82 + *
  83 + * 冗余 {@link SysSmsTemplateDO#getApiTemplateId()}
  84 + */
  85 + private String apiTemplateId;
  86 +
  87 + // ========= 手机相关字段 =========
  88 +
  89 + /**
  90 + * 手机号
  91 + */
  92 + private String mobile;
  93 + /**
  94 + * 用户编号
  95 + */
  96 + private Long userId;
  97 + /**
  98 + * 用户类型
  99 + *
  100 + * 枚举 {@link UserTypeEnum}
  101 + */
  102 + private Integer userType;
  103 +
  104 + // ========= 发送相关字段 =========
  105 +
  106 + /**
  107 + * 发送状态
  108 + *
  109 + * 枚举 {@link SysSmsSendStatusEnum}
  110 + */
  111 + private Integer sendStatus;
  112 + /**
  113 + * 发送时间
  114 + */
  115 + private Date sendTime;
  116 + /**
  117 + * 发送结果的编码
  118 + *
  119 + * 枚举 {@link SmsFrameworkErrorCodeConstants}
  120 + */
  121 + private Integer sendCode;
  122 + /**
  123 + * 发送结果的提示
  124 + *
  125 + * 一般情况下,使用 {@link SmsFrameworkErrorCodeConstants}
  126 + * 异常情况下,通过格式化 Exception 的提示存储
  127 + */
  128 + private String sendMsg;
  129 + /**
  130 + * 短信 API 发送结果的编码
  131 + *
  132 + * 由于第三方的错误码可能是字符串,所以使用 String 类型
  133 + */
  134 + private String apiSendCode;
  135 + /**
  136 + * 短信 API 发送失败的提示
  137 + */
  138 + private String apiSendMsg;
  139 + /**
  140 + * 短信 API 发送返回的唯一请求 ID
  141 + *
  142 + * 用于和短信 API 进行定位于排错
  143 + */
  144 + private String apiRequestId;
  145 + /**
  146 + * 短信 API 发送返回的序号
  147 + *
  148 + * 用于和短信 API 平台的发送记录关联
  149 + */
  150 + private String apiSerialNo;
  151 +
  152 + // ========= 接收相关字段 =========
  153 +
  154 + /**
  155 + * 接收状态
  156 + *
  157 + * 枚举 {@link SysSmsReceiveStatusEnum}
  158 + */
  159 + private Integer receiveStatus;
  160 + /**
  161 + * 接收时间
  162 + */
  163 + private Date receiveTime;
  164 + /**
  165 + * 短信 API 接收结果的编码
  166 + */
  167 + private String apiReceiveCode;
  168 + /**
  169 + * 短信 API 接收结果的提示
  170 + */
  171 + private String apiReceiveMsg;
  172 +
  173 +}
src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsTemplateDO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.dal.dataobject.sms;
  2 +
  3 +import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsTemplateTypeEnum;
  6 +import com.baomidou.mybatisplus.annotation.TableField;
  7 +import com.baomidou.mybatisplus.annotation.TableName;
  8 +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
  9 +import lombok.Data;
  10 +import lombok.EqualsAndHashCode;
  11 +import lombok.ToString;
  12 +
  13 +import java.util.List;
  14 +
  15 +/**
  16 + * 短信模板 DO
  17 + *
  18 + * @author zzf
  19 + * @since 2021-01-25
  20 + */
  21 +@TableName(value = "sys_sms_template", autoResultMap = true)
  22 +@Data
  23 +@EqualsAndHashCode(callSuper = true)
  24 +@ToString(callSuper = true)
  25 +public class SysSmsTemplateDO extends BaseDO {
  26 +
  27 + /**
  28 + * 自增编号
  29 + */
  30 + private Long id;
  31 +
  32 + // ========= 模板相关字段 =========
  33 +
  34 + /**
  35 + * 短信类型
  36 + *
  37 + * 枚举 {@link SysSmsTemplateTypeEnum}
  38 + */
  39 + private Integer type;
  40 + /**
  41 + * 启用状态
  42 + *
  43 + * 枚举 {@link CommonStatusEnum}
  44 + */
  45 + private Integer status;
  46 + /**
  47 + * 模板编码,保证唯一
  48 + */
  49 + private String code;
  50 + /**
  51 + * 模板名称
  52 + */
  53 + private String name;
  54 + /**
  55 + * 模板内容
  56 + *
  57 + * 内容的参数,使用 {} 包括,例如说 {name}
  58 + */
  59 + private String content;
  60 + /**
  61 + * 参数数组(自动根据内容生成)
  62 + */
  63 + @TableField(typeHandler = JacksonTypeHandler.class)
  64 + private List<String> params;
  65 + /**
  66 + * 备注
  67 + */
  68 + private String remark;
  69 + /**
  70 + * 短信 API 的模板编号
  71 + */
  72 + private String apiTemplateId;
  73 +
  74 + // ========= 渠道相关字段 =========
  75 +
  76 + /**
  77 + * 短信渠道编号
  78 + *
  79 + * 关联 {@link SysSmsChannelDO#getId()}
  80 + */
  81 + private Long channelId;
  82 + /**
  83 + * 短信渠道编码
  84 + *
  85 + * 冗余 {@link SysSmsChannelDO#getCode()}
  86 + */
  87 + private String channelCode;
  88 +
  89 +}
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsChannelMapper.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.dal.mysql.sms;
  2 +
  3 +import cn.iocoder.dashboard.common.pojo.PageResult;
  4 +import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.dashboard.framework.mybatis.core.query.QueryWrapperX;
  6 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelPageReqVO;
  7 +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO;
  8 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +import org.apache.ibatis.annotations.Select;
  11 +
  12 +import java.util.Date;
  13 +import java.util.List;
  14 +
  15 +@Mapper
  16 +public interface SysSmsChannelMapper extends BaseMapperX<SysSmsChannelDO> {
  17 +
  18 + default PageResult<SysSmsChannelDO> selectPage(SysSmsChannelPageReqVO reqVO) {
  19 + return selectPage(reqVO, new QueryWrapperX<SysSmsChannelDO>()
  20 + .likeIfPresent("signature", reqVO.getSignature())
  21 + .eqIfPresent("status", reqVO.getStatus())
  22 + .betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
  23 + .orderByDesc("id"));
  24 + }
  25 +
  26 + @Select("SELECT id FROM sys_sms_channel WHERE update_time > #{maxUpdateTime} LIMIT 1")
  27 + Long selectExistsByUpdateTimeAfter(Date maxUpdateTime);
  28 +
  29 +}
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsLogMapper.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.dal.mysql.sms;
  2 +
  3 +import cn.iocoder.dashboard.common.pojo.PageResult;
  4 +import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.dashboard.framework.mybatis.core.query.QueryWrapperX;
  6 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogExportReqVO;
  7 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogPageReqVO;
  8 +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsLogDO;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +
  11 +import java.util.List;
  12 +
  13 +@Mapper
  14 +public interface SysSmsLogMapper extends BaseMapperX<SysSmsLogDO> {
  15 +
  16 + default PageResult<SysSmsLogDO> selectPage(SysSmsLogPageReqVO reqVO) {
  17 + return selectPage(reqVO, new QueryWrapperX<SysSmsLogDO>()
  18 + .eqIfPresent("channel_id", reqVO.getChannelId())
  19 + .eqIfPresent("template_id", reqVO.getTemplateId())
  20 + .likeIfPresent("mobile", reqVO.getMobile())
  21 + .eqIfPresent("send_status", reqVO.getSendStatus())
  22 + .betweenIfPresent("send_time", reqVO.getBeginSendTime(), reqVO.getEndSendTime())
  23 + .eqIfPresent("receive_status", reqVO.getReceiveStatus())
  24 + .betweenIfPresent("receive_time", reqVO.getBeginReceiveTime(), reqVO.getEndReceiveTime())
  25 + .orderByDesc("id"));
  26 + }
  27 +
  28 + default List<SysSmsLogDO> selectList(SysSmsLogExportReqVO reqVO) {
  29 + return selectList(new QueryWrapperX<SysSmsLogDO>()
  30 + .eqIfPresent("channel_id", reqVO.getChannelId())
  31 + .eqIfPresent("template_id", reqVO.getTemplateId())
  32 + .likeIfPresent("mobile", reqVO.getMobile())
  33 + .eqIfPresent("send_status", reqVO.getSendStatus())
  34 + .betweenIfPresent("send_time", reqVO.getBeginSendTime(), reqVO.getEndSendTime())
  35 + .eqIfPresent("receive_status", reqVO.getReceiveStatus())
  36 + .betweenIfPresent("receive_time", reqVO.getBeginReceiveTime(), reqVO.getEndReceiveTime())
  37 + .orderByDesc("id"));
  38 + }
  39 +
  40 +}
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsTemplateMapper.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.dal.mysql.sms;
  2 +
  3 +import cn.iocoder.dashboard.common.pojo.PageResult;
  4 +import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.dashboard.framework.mybatis.core.query.QueryWrapperX;
  6 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplateExportReqVO;
  7 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplatePageReqVO;
  8 +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +import org.apache.ibatis.annotations.Select;
  11 +
  12 +import java.util.Date;
  13 +import java.util.List;
  14 +
  15 +@Mapper
  16 +public interface SysSmsTemplateMapper extends BaseMapperX<SysSmsTemplateDO> {
  17 +
  18 + default SysSmsTemplateDO selectByCode(String code) {
  19 + return selectOne("code", code);
  20 + }
  21 +
  22 + default PageResult<SysSmsTemplateDO> selectPage(SysSmsTemplatePageReqVO reqVO) {
  23 + return selectPage(reqVO, new QueryWrapperX<SysSmsTemplateDO>()
  24 + .eqIfPresent("type", reqVO.getType())
  25 + .eqIfPresent("status", reqVO.getStatus())
  26 + .likeIfPresent("code", reqVO.getCode())
  27 + .likeIfPresent("content", reqVO.getContent())
  28 + .likeIfPresent("api_template_id", reqVO.getApiTemplateId())
  29 + .eqIfPresent("channel_id", reqVO.getChannelId())
  30 + .betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
  31 + .orderByDesc("id"));
  32 + }
  33 +
  34 + default List<SysSmsTemplateDO> selectList(SysSmsTemplateExportReqVO reqVO) {
  35 + return selectList(new QueryWrapperX<SysSmsTemplateDO>()
  36 + .eqIfPresent("type", reqVO.getType())
  37 + .eqIfPresent("status", reqVO.getStatus())
  38 + .likeIfPresent("code", reqVO.getCode())
  39 + .likeIfPresent("content", reqVO.getContent())
  40 + .likeIfPresent("api_template_id", reqVO.getApiTemplateId())
  41 + .eqIfPresent("channel_id", reqVO.getChannelId())
  42 + .betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
  43 + .orderByDesc("id"));
  44 + }
  45 +
  46 + default Integer selectCountByChannelId(Long channelId) {
  47 + return selectCount("channel_id", channelId);
  48 + }
  49 +
  50 + @Select("SELECT id FROM sys_sms_template WHERE update_time > #{maxUpdateTime} LIMIT 1")
  51 + Long selectExistsByUpdateTimeAfter(Date maxUpdateTime);
  52 +
  53 +}
src/main/java/cn/iocoder/dashboard/modules/system/dal/redis/RedisKeyConstants.java 0 → 100644
src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java
@@ -30,8 +30,8 @@ public interface SysErrorCodeConstants { @@ -30,8 +30,8 @@ public interface SysErrorCodeConstants {
30 30
31 // ========== 角色模块 1002003000 ========== 31 // ========== 角色模块 1002003000 ==========
32 ErrorCode ROLE_NOT_EXISTS = new ErrorCode(1002003000, "角色不存在"); 32 ErrorCode ROLE_NOT_EXISTS = new ErrorCode(1002003000, "角色不存在");
33 - ErrorCode ROLE_NAME_DUPLICATE = new ErrorCode(1002003001, "已经存在名为【{}}】的角色");  
34 - ErrorCode ROLE_CODE_DUPLICATE = new ErrorCode(1002003002, "已经存在编码为【{}}】的角色"); 33 + ErrorCode ROLE_NAME_DUPLICATE = new ErrorCode(1002003001, "已经存在名为【{}】的角色");
  34 + ErrorCode ROLE_CODE_DUPLICATE = new ErrorCode(1002003002, "已经存在编码为【{}】的角色");
35 ErrorCode ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE = new ErrorCode(1002003004, "不能操作类型为系统内置的角色"); 35 ErrorCode ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE = new ErrorCode(1002003004, "不能操作类型为系统内置的角色");
36 36
37 // ========== 用户模块 1002004000 ========== 37 // ========== 用户模块 1002004000 ==========
@@ -78,4 +78,18 @@ public interface SysErrorCodeConstants { @@ -78,4 +78,18 @@ public interface SysErrorCodeConstants {
78 ErrorCode FILE_UPLOAD_FAILED = new ErrorCode(1002009002, "文件上传失败"); 78 ErrorCode FILE_UPLOAD_FAILED = new ErrorCode(1002009002, "文件上传失败");
79 ErrorCode FILE_IS_EMPTY= new ErrorCode(1002009003, "文件为空"); 79 ErrorCode FILE_IS_EMPTY= new ErrorCode(1002009003, "文件为空");
80 80
  81 + // ========== 短信渠道 1002011000 ==========
  82 + ErrorCode SMS_CHANNEL_NOT_EXISTS = new ErrorCode(1002011000, "短信渠道不存在");
  83 + ErrorCode SMS_CHANNEL_DISABLE = new ErrorCode(1002011001, "短信渠道不处于开启状态,不允许选择");
  84 + ErrorCode SMS_CHANNEL_HAS_CHILDREN = new ErrorCode(1002011002, "无法删除,该短信渠道还有短信模板");
  85 +
  86 + // ========== 短信模板 1002011000 ==========
  87 + ErrorCode SMS_TEMPLATE_NOT_EXISTS = new ErrorCode(1002011000, "短信模板不存在");
  88 + ErrorCode SMS_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1002011001, "已经存在编码为【{}】的短信模板");
  89 +
  90 + // ========== 短信发送 1002012000 ==========
  91 + ErrorCode SMS_SEND_MOBILE_NOT_EXISTS = new ErrorCode(1002012000, "手机号不存在");
  92 + ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1002012001, "模板参数({})缺失");
  93 +
  94 +
81 } 95 }
src/main/java/cn/iocoder/dashboard/modules/system/enums/dict/SysDictTypeEnum.java
@@ -18,6 +18,10 @@ public enum SysDictTypeEnum { @@ -18,6 +18,10 @@ public enum SysDictTypeEnum {
18 SYS_LOGIN_RESULT("sys_login_result"), // 登陆结果 18 SYS_LOGIN_RESULT("sys_login_result"), // 登陆结果
19 SYS_CONFIG_TYPE("sys_config_type"), // 参数配置类型 19 SYS_CONFIG_TYPE("sys_config_type"), // 参数配置类型
20 SYS_BOOLEAN_STRING("sys_boolean_string"), // Boolean 是否类型 20 SYS_BOOLEAN_STRING("sys_boolean_string"), // Boolean 是否类型
  21 + SYS_SMS_CHANNEL_CODE("sys_sms_channel_code"), // 短信渠道编码
  22 + SYS_SMS_TEMPLATE_TYPE("sys_sms_template_type"), // 短信模板类型
  23 + SYS_SMS_SEND_STATUS("sys_sms_send_status"), // 短信发送状态
  24 + SYS_SMS_RECEIVE_STATUS("sys_sms_receive_status"), // 短信接收状态
21 25
22 INF_REDIS_TIMEOUT_TYPE("inf_redis_timeout_type"), // Redis 超时类型 26 INF_REDIS_TIMEOUT_TYPE("inf_redis_timeout_type"), // Redis 超时类型
23 INF_JOB_STATUS("inf_job_status"), // 定时任务状态的枚举 27 INF_JOB_STATUS("inf_job_status"), // 定时任务状态的枚举
src/main/java/cn/iocoder/dashboard/modules/system/enums/sms/SysSmsReceiveStatusEnum.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.enums.sms;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * 短信的接收状态枚举
  8 + *
  9 + * @author 芋道源码
  10 + * @date 2021/2/1 13:39
  11 + */
  12 +@Getter
  13 +@AllArgsConstructor
  14 +public enum SysSmsReceiveStatusEnum {
  15 +
  16 + INIT(0), // 初始化
  17 + SUCCESS(10), // 接收成功
  18 + FAILURE(20), // 接收失败
  19 + ;
  20 +
  21 + private final int status;
  22 +
  23 +}
src/main/java/cn/iocoder/dashboard/modules/system/enums/sms/SysSmsSendStatusEnum.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.enums.sms;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * 短信的发送状态枚举
  8 + *
  9 + * @author zzf
  10 + * @date 2021/2/1 13:39
  11 + */
  12 +@Getter
  13 +@AllArgsConstructor
  14 +public enum SysSmsSendStatusEnum {
  15 +
  16 + INIT(0), // 初始化
  17 + SUCCESS(10), // 发送成功
  18 + FAILURE(20), // 发送失败
  19 + IGNORE(30), // 忽略,即不发送
  20 + ;
  21 +
  22 + private final int status;
  23 +
  24 +}
src/main/java/cn/iocoder/dashboard/modules/system/enums/sms/SysSmsTemplateTypeEnum.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.enums.sms;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * 短信的模板类型枚举
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +@Getter
  12 +@AllArgsConstructor
  13 +public enum SysSmsTemplateTypeEnum {
  14 +
  15 + VERIFICATION_CODE(1), // 验证码
  16 + NOTICE(2), // 通知
  17 + PROMOTION(3), // 营销
  18 + ;
  19 +
  20 + /**
  21 + * 类型
  22 + */
  23 + private final int type;
  24 +
  25 +}
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SysSmsChannelRefreshConsumer.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.mq.consumer.sms;
  2 +
  3 +import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener;
  4 +import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsChannelRefreshMessage;
  5 +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +import javax.annotation.Resource;
  10 +
  11 +/**
  12 + * 针对 {@link SysSmsChannelRefreshMessage} 的消费者
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@Component
  17 +@Slf4j
  18 +public class SysSmsChannelRefreshConsumer extends AbstractChannelMessageListener<SysSmsChannelRefreshMessage> {
  19 +
  20 + @Resource
  21 + private SysSmsChannelService smsChannelService;
  22 +
  23 + @Override
  24 + public void onMessage(SysSmsChannelRefreshMessage message) {
  25 + log.info("[onMessage][收到 SmsChannel 刷新消息]");
  26 + smsChannelService.initSmsClients();
  27 + }
  28 +
  29 +}
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SysSmsSendConsumer.java
@@ -2,16 +2,29 @@ package cn.iocoder.dashboard.modules.system.mq.consumer.sms; @@ -2,16 +2,29 @@ package cn.iocoder.dashboard.modules.system.mq.consumer.sms;
2 2
3 import cn.iocoder.dashboard.framework.redis.core.stream.AbstractStreamMessageListener; 3 import cn.iocoder.dashboard.framework.redis.core.stream.AbstractStreamMessageListener;
4 import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage; 4 import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage;
  5 +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService;
5 import lombok.extern.slf4j.Slf4j; 6 import lombok.extern.slf4j.Slf4j;
6 import org.springframework.stereotype.Component; 7 import org.springframework.stereotype.Component;
7 8
  9 +import javax.annotation.Resource;
  10 +
  11 +/**
  12 + * 针对 {@link SysSmsSendMessage} 的消费者
  13 + *
  14 + * @author zzf
  15 + * @date 2021/3/9 16:35
  16 + */
8 @Component 17 @Component
9 @Slf4j 18 @Slf4j
10 public class SysSmsSendConsumer extends AbstractStreamMessageListener<SysSmsSendMessage> { 19 public class SysSmsSendConsumer extends AbstractStreamMessageListener<SysSmsSendMessage> {
11 20
  21 + @Resource
  22 + private SysSmsService smsService;
  23 +
12 @Override 24 @Override
13 public void onMessage(SysSmsSendMessage message) { 25 public void onMessage(SysSmsSendMessage message) {
14 log.info("[onMessage][消息内容({})]", message); 26 log.info("[onMessage][消息内容({})]", message);
  27 + smsService.doSendSms(message);
15 } 28 }
16 29
17 } 30 }
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SysSmsTemplateRefreshConsumer.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.mq.consumer.sms;
  2 +
  3 +import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener;
  4 +import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsTemplateRefreshMessage;
  5 +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsTemplateService;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +import javax.annotation.Resource;
  10 +
  11 +/**
  12 + * 针对 {@link SysSmsTemplateRefreshMessage} 的消费者
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@Component
  17 +@Slf4j
  18 +public class SysSmsTemplateRefreshConsumer extends AbstractChannelMessageListener<SysSmsTemplateRefreshMessage> {
  19 +
  20 + @Resource
  21 + private SysSmsTemplateService smsTemplateService;
  22 +
  23 + @Override
  24 + public void onMessage(SysSmsTemplateRefreshMessage message) {
  25 + log.info("[onMessage][收到 SmsTemplate 刷新消息]");
  26 + smsTemplateService.initLocalCache();
  27 + }
  28 +
  29 +}
src/main/java/cn/iocoder/dashboard/modules/system/mq/message/sms/SysSmsChannelRefreshMessage.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.mq.message.sms;
  2 +
  3 +import cn.iocoder.dashboard.framework.redis.core.pubsub.ChannelMessage;
  4 +import lombok.Data;
  5 +
  6 +/**
  7 + * 短信渠道的数据刷新 Message
  8 + */
  9 +@Data
  10 +public class SysSmsChannelRefreshMessage implements ChannelMessage {
  11 +
  12 + @Override
  13 + public String getChannel() {
  14 + return "system.sms-channel.refresh";
  15 + }
  16 +
  17 +}
src/main/java/cn/iocoder/dashboard/modules/system/mq/message/sms/SysSmsSendMessage.java
1 package cn.iocoder.dashboard.modules.system.mq.message.sms; 1 package cn.iocoder.dashboard.modules.system.mq.message.sms;
2 2
  3 +import cn.iocoder.dashboard.common.core.KeyValue;
3 import cn.iocoder.dashboard.framework.redis.core.stream.StreamMessage; 4 import cn.iocoder.dashboard.framework.redis.core.stream.StreamMessage;
4 import lombok.Data; 5 import lombok.Data;
5 6
6 import javax.validation.constraints.NotNull; 7 import javax.validation.constraints.NotNull;
7 -import java.util.Map; 8 +import java.util.List;
8 9
9 /** 10 /**
10 * 短信发送消息 11 * 短信发送消息
@@ -15,28 +16,29 @@ import java.util.Map; @@ -15,28 +16,29 @@ import java.util.Map;
15 public class SysSmsSendMessage implements StreamMessage { 16 public class SysSmsSendMessage implements StreamMessage {
16 17
17 /** 18 /**
  19 + * 短信日志编号
  20 + */
  21 + @NotNull(message = "短信日志编号不能为空")
  22 + private Long logId;
  23 + /**
18 * 手机号 24 * 手机号
19 */ 25 */
20 @NotNull(message = "手机号不能为空") 26 @NotNull(message = "手机号不能为空")
21 private String mobile; 27 private String mobile;
22 /** 28 /**
23 - * 短信模板编号 29 + * 短信渠道编号
24 */ 30 */
25 - @NotNull(message = "短信模板编号不能为空")  
26 - private String templateCode; 31 + @NotNull(message = "短信渠道编号不能为空")
  32 + private Long channelId;
27 /** 33 /**
28 - * 短信模板参数 34 + * 短信 API 的模板编号
29 */ 35 */
30 - private Map<String, Object> templateParams;  
31 - 36 + @NotNull(message = "短信 API 的模板编号不能为空")
  37 + private String apiTemplateId;
32 /** 38 /**
33 - * 用户编号,允许空  
34 - */  
35 - private Integer userId;  
36 - /**  
37 - * 用户类型,允许空 39 + * 短信模板参数
38 */ 40 */
39 - private Integer userType; 41 + private List<KeyValue<String, Object>> templateParams;
40 42
41 @Override 43 @Override
42 public String getStreamKey() { 44 public String getStreamKey() {
src/main/java/cn/iocoder/dashboard/modules/system/mq/message/sms/SysSmsTemplateRefreshMessage.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.mq.message.sms;
  2 +
  3 +import cn.iocoder.dashboard.framework.redis.core.pubsub.ChannelMessage;
  4 +import lombok.Data;
  5 +
  6 +/**
  7 + * 短信模板的数据刷新 Message
  8 + */
  9 +@Data
  10 +public class SysSmsTemplateRefreshMessage implements ChannelMessage {
  11 +
  12 + @Override
  13 + public String getChannel() {
  14 + return "system.sms-template.refresh";
  15 + }
  16 +
  17 +}
src/main/java/cn/iocoder/dashboard/modules/system/mq/producer/permission/SysRoleProducer.java
@@ -9,6 +9,8 @@ import javax.annotation.Resource; @@ -9,6 +9,8 @@ import javax.annotation.Resource;
9 9
10 /** 10 /**
11 * Role 角色相关消息的 Producer 11 * Role 角色相关消息的 Producer
  12 + *
  13 + * @author 芋道源码
12 */ 14 */
13 @Component 15 @Component
14 public class SysRoleProducer { 16 public class SysRoleProducer {
src/main/java/cn/iocoder/dashboard/modules/system/mq/producer/sms/SysSmsProducer.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.mq.producer.sms;
  2 +
  3 +import cn.iocoder.dashboard.common.core.KeyValue;
  4 +import cn.iocoder.dashboard.framework.redis.core.util.RedisMessageUtils;
  5 +import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsChannelRefreshMessage;
  6 +import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage;
  7 +import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsTemplateRefreshMessage;
  8 +import lombok.extern.slf4j.Slf4j;
  9 +import org.springframework.data.redis.core.StringRedisTemplate;
  10 +import org.springframework.stereotype.Component;
  11 +
  12 +import javax.annotation.Resource;
  13 +import java.util.List;
  14 +
  15 +/**
  16 + * Sms 短信相关消息的 Producer
  17 + *
  18 + * @author zzf
  19 + * @date 2021/3/9 16:35
  20 + */
  21 +@Slf4j
  22 +@Component
  23 +public class SysSmsProducer {
  24 +
  25 + @Resource
  26 + private StringRedisTemplate stringRedisTemplate;
  27 +
  28 + /**
  29 + * 发送 {@link SysSmsSendMessage} 消息
  30 + *
  31 + * @param logId 短信日志编号
  32 + * @param mobile 手机号
  33 + * @param channelId 渠道编号
  34 + * @param apiTemplateId 短信模板编号
  35 + * @param templateParams 短信模板参数
  36 + */
  37 + public void sendSmsSendMessage(Long logId, String mobile,
  38 + Long channelId, String apiTemplateId, List<KeyValue<String, Object>> templateParams) {
  39 + SysSmsSendMessage message = new SysSmsSendMessage().setLogId(logId).setMobile(mobile);
  40 + message.setChannelId(channelId).setApiTemplateId(apiTemplateId).setTemplateParams(templateParams);
  41 + RedisMessageUtils.sendStreamMessage(stringRedisTemplate, message);
  42 + }
  43 +
  44 + /**
  45 + * 发送 {@link SysSmsChannelRefreshMessage} 消息
  46 + */
  47 + public void sendSmsChannelRefreshMessage() {
  48 + SysSmsChannelRefreshMessage message = new SysSmsChannelRefreshMessage();
  49 + RedisMessageUtils.sendChannelMessage(stringRedisTemplate, message);
  50 + }
  51 +
  52 + /**
  53 + * 发送 {@link SysSmsTemplateRefreshMessage} 消息
  54 + */
  55 + public void sendSmsTemplateRefreshMessage() {
  56 + SysSmsTemplateRefreshMessage message = new SysSmsTemplateRefreshMessage();
  57 + RedisMessageUtils.sendChannelMessage(stringRedisTemplate, message);
  58 + }
  59 +
  60 +}
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysPermissionService.java
@@ -18,7 +18,7 @@ import java.util.Set; @@ -18,7 +18,7 @@ import java.util.Set;
18 public interface SysPermissionService extends SecurityPermissionFrameworkService { 18 public interface SysPermissionService extends SecurityPermissionFrameworkService {
19 19
20 /** 20 /**
21 - * 初始化 21 + * 初始化权限的本地缓存
22 */ 22 */
23 void initLocalCache(); 23 void initLocalCache();
24 24
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysMenuServiceImpl.java
@@ -15,6 +15,7 @@ import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysMenuProduce @@ -15,6 +15,7 @@ import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysMenuProduce
15 import cn.iocoder.dashboard.modules.system.service.permission.SysMenuService; 15 import cn.iocoder.dashboard.modules.system.service.permission.SysMenuService;
16 import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService; 16 import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
17 import cn.iocoder.dashboard.util.collection.CollectionUtils; 17 import cn.iocoder.dashboard.util.collection.CollectionUtils;
  18 +import com.google.common.annotations.VisibleForTesting;
18 import com.google.common.collect.ImmutableMap; 19 import com.google.common.collect.ImmutableMap;
19 import com.google.common.collect.ImmutableMultimap; 20 import com.google.common.collect.ImmutableMultimap;
20 import com.google.common.collect.Multimap; 21 import com.google.common.collect.Multimap;
@@ -168,10 +169,6 @@ public class SysMenuServiceImpl implements SysMenuService { @@ -168,10 +169,6 @@ public class SysMenuServiceImpl implements SysMenuService {
168 */ 169 */
169 @Transactional(rollbackFor = Exception.class) 170 @Transactional(rollbackFor = Exception.class)
170 public void deleteMenu(Long menuId) { 171 public void deleteMenu(Long menuId) {
171 - // 校验更新的菜单是否存在  
172 - if (menuMapper.selectById(menuId) == null) {  
173 - throw ServiceExceptionUtil.exception(MENU_NOT_EXISTS);  
174 - }  
175 // 校验是否还有子菜单 172 // 校验是否还有子菜单
176 if (menuMapper.selectCountByParentId(menuId) > 0) { 173 if (menuMapper.selectCountByParentId(menuId) > 0) {
177 throw ServiceExceptionUtil.exception(MENU_EXISTS_CHILDREN); 174 throw ServiceExceptionUtil.exception(MENU_EXISTS_CHILDREN);
@@ -250,7 +247,8 @@ public class SysMenuServiceImpl implements SysMenuService { @@ -250,7 +247,8 @@ public class SysMenuServiceImpl implements SysMenuService {
250 * @param parentId 父菜单编号 247 * @param parentId 父菜单编号
251 * @param childId 当前菜单编号 248 * @param childId 当前菜单编号
252 */ 249 */
253 - private void checkParentResource(Long parentId, Long childId) { 250 + @VisibleForTesting
  251 + public void checkParentResource(Long parentId, Long childId) {
254 if (parentId == null || MenuIdEnum.ROOT.getId().equals(parentId)) { 252 if (parentId == null || MenuIdEnum.ROOT.getId().equals(parentId)) {
255 return; 253 return;
256 } 254 }
@@ -279,7 +277,8 @@ public class SysMenuServiceImpl implements SysMenuService { @@ -279,7 +277,8 @@ public class SysMenuServiceImpl implements SysMenuService {
279 * @param parentId 父菜单编号 277 * @param parentId 父菜单编号
280 * @param id 菜单编号 278 * @param id 菜单编号
281 */ 279 */
282 - private void checkResource(Long parentId, String name, Long id) { 280 + @VisibleForTesting
  281 + public void checkResource(Long parentId, String name, Long id) {
283 SysMenuDO menu = menuMapper.selectByParentIdAndName(parentId, name); 282 SysMenuDO menu = menuMapper.selectByParentIdAndName(parentId, name);
284 if (menu == null) { 283 if (menu == null) {
285 return; 284 return;
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysPermissionServiceImpl.java
@@ -3,7 +3,6 @@ package cn.iocoder.dashboard.modules.system.service.permission.impl; @@ -3,7 +3,6 @@ package cn.iocoder.dashboard.modules.system.service.permission.impl;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.collection.CollectionUtil; 4 import cn.hutool.core.collection.CollectionUtil;
5 import cn.hutool.core.util.ArrayUtil; 5 import cn.hutool.core.util.ArrayUtil;
6 -import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;  
7 import cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils; 6 import cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils;
8 import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysRoleMenuMapper; 7 import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysRoleMenuMapper;
9 import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysUserRoleMapper; 8 import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysUserRoleMapper;
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysRoleServiceImpl.java
@@ -18,6 +18,7 @@ import cn.iocoder.dashboard.modules.system.enums.permission.SysRoleTypeEnum; @@ -18,6 +18,7 @@ import cn.iocoder.dashboard.modules.system.enums.permission.SysRoleTypeEnum;
18 import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysRoleProducer; 18 import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysRoleProducer;
19 import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService; 19 import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
20 import cn.iocoder.dashboard.modules.system.service.permission.SysRoleService; 20 import cn.iocoder.dashboard.modules.system.service.permission.SysRoleService;
  21 +import com.google.common.annotations.VisibleForTesting;
21 import com.google.common.collect.ImmutableMap; 22 import com.google.common.collect.ImmutableMap;
22 import lombok.extern.slf4j.Slf4j; 23 import lombok.extern.slf4j.Slf4j;
23 import org.springframework.lang.Nullable; 24 import org.springframework.lang.Nullable;
@@ -58,7 +59,7 @@ public class SysRoleServiceImpl implements SysRoleService { @@ -58,7 +59,7 @@ public class SysRoleServiceImpl implements SysRoleService {
58 */ 59 */
59 private volatile Map<Long, SysRoleDO> roleCache; 60 private volatile Map<Long, SysRoleDO> roleCache;
60 /** 61 /**
61 - * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新 62 + * 缓存角色的最大更新时间,用于后续的增量轮询,判断是否有更新
62 */ 63 */
63 private volatile Date maxUpdateTime; 64 private volatile Date maxUpdateTime;
64 65
@@ -77,7 +78,7 @@ public class SysRoleServiceImpl implements SysRoleService { @@ -77,7 +78,7 @@ public class SysRoleServiceImpl implements SysRoleService {
77 @Override 78 @Override
78 @PostConstruct 79 @PostConstruct
79 public void initLocalCache() { 80 public void initLocalCache() {
80 - // 获取菜单列表,如果有更新 81 + // 获取角色列表,如果有更新
81 List<SysRoleDO> roleList = this.loadRoleIfUpdate(maxUpdateTime); 82 List<SysRoleDO> roleList = this.loadRoleIfUpdate(maxUpdateTime);
82 if (CollUtil.isEmpty(roleList)) { 83 if (CollUtil.isEmpty(roleList)) {
83 return; 84 return;
@@ -98,23 +99,23 @@ public class SysRoleServiceImpl implements SysRoleService { @@ -98,23 +99,23 @@ public class SysRoleServiceImpl implements SysRoleService {
98 } 99 }
99 100
100 /** 101 /**
101 - * 如果菜单发生变化,从数据库中获取最新的全量菜单。 102 + * 如果角色发生变化,从数据库中获取最新的全量角色。
102 * 如果未发生变化,则返回空 103 * 如果未发生变化,则返回空
103 * 104 *
104 - * @param maxUpdateTime 当前菜单的最大更新时间  
105 - * @return 菜单列表 105 + * @param maxUpdateTime 当前角色的最大更新时间
  106 + * @return 角色列表
106 */ 107 */
107 private List<SysRoleDO> loadRoleIfUpdate(Date maxUpdateTime) { 108 private List<SysRoleDO> loadRoleIfUpdate(Date maxUpdateTime) {
108 // 第一步,判断是否要更新。 109 // 第一步,判断是否要更新。
109 if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据 110 if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
110 - log.info("[loadRoleIfUpdate][首次加载全量菜单]");  
111 - } else { // 判断数据库中是否有更新的菜单 111 + log.info("[loadRoleIfUpdate][首次加载全量角色]");
  112 + } else { // 判断数据库中是否有更新的角色
112 if (!roleMapper.selectExistsByUpdateTimeAfter(maxUpdateTime)) { 113 if (!roleMapper.selectExistsByUpdateTimeAfter(maxUpdateTime)) {
113 return null; 114 return null;
114 } 115 }
115 - log.info("[loadRoleIfUpdate][增量加载全量菜单]"); 116 + log.info("[loadRoleIfUpdate][增量加载全量角色]");
116 } 117 }
117 - // 第二步,如果有更新,则从数据库加载所有菜单 118 + // 第二步,如果有更新,则从数据库加载所有角色
118 return roleMapper.selectList(); 119 return roleMapper.selectList();
119 } 120 }
120 121
@@ -245,7 +246,8 @@ public class SysRoleServiceImpl implements SysRoleService { @@ -245,7 +246,8 @@ public class SysRoleServiceImpl implements SysRoleService {
245 * @param code 角色额编码 246 * @param code 角色额编码
246 * @param id 角色编号 247 * @param id 角色编号
247 */ 248 */
248 - private void checkDuplicateRole(String name, String code, Long id) { 249 + @VisibleForTesting
  250 + public void checkDuplicateRole(String name, String code, Long id) {
249 // 1. 该 name 名字被其它角色所使用 251 // 1. 该 name 名字被其它角色所使用
250 SysRoleDO role = roleMapper.selectByName(name); 252 SysRoleDO role = roleMapper.selectByName(name);
251 if (role != null && !role.getId().equals(id)) { 253 if (role != null && !role.getId().equals(id)) {
@@ -258,7 +260,7 @@ public class SysRoleServiceImpl implements SysRoleService { @@ -258,7 +260,7 @@ public class SysRoleServiceImpl implements SysRoleService {
258 // 该 code 编码被其它角色所使用 260 // 该 code 编码被其它角色所使用
259 role = roleMapper.selectByCode(code); 261 role = roleMapper.selectByCode(code);
260 if (role != null && !role.getId().equals(id)) { 262 if (role != null && !role.getId().equals(id)) {
261 - throw ServiceExceptionUtil.exception(ROLE_CODE_DUPLICATE, name); 263 + throw ServiceExceptionUtil.exception(ROLE_CODE_DUPLICATE, code);
262 } 264 }
263 } 265 }
264 266
@@ -267,7 +269,8 @@ public class SysRoleServiceImpl implements SysRoleService { @@ -267,7 +269,8 @@ public class SysRoleServiceImpl implements SysRoleService {
267 * 269 *
268 * @param id 角色编号 270 * @param id 角色编号
269 */ 271 */
270 - private void checkUpdateRole(Long id) { 272 + @VisibleForTesting
  273 + public void checkUpdateRole(Long id) {
271 SysRoleDO roleDO = roleMapper.selectById(id); 274 SysRoleDO roleDO = roleMapper.selectById(id);
272 if (roleDO == null) { 275 if (roleDO == null) {
273 throw ServiceExceptionUtil.exception(ROLE_NOT_EXISTS); 276 throw ServiceExceptionUtil.exception(ROLE_NOT_EXISTS);
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsChannelService.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.service.sms;
  2 +
  3 +import cn.iocoder.dashboard.common.pojo.PageResult;
  4 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelCreateReqVO;
  5 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelPageReqVO;
  6 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelUpdateReqVO;
  7 +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO;
  8 +
  9 +import javax.validation.Valid;
  10 +import java.util.Collection;
  11 +import java.util.List;
  12 +
  13 +/**
  14 + * 短信渠道Service接口
  15 + *
  16 + * @author zzf
  17 + * @date 2021/1/25 9:24
  18 + */
  19 +public interface SysSmsChannelService {
  20 +
  21 + /**
  22 + * 初始化短信客户端
  23 + */
  24 + void initSmsClients();
  25 +
  26 + /**
  27 + * 创建短信渠道
  28 + *
  29 + * @param createReqVO 创建信息
  30 + * @return 编号
  31 + */
  32 + Long createSmsChannel(@Valid SysSmsChannelCreateReqVO createReqVO);
  33 +
  34 + /**
  35 + * 更新短信渠道
  36 + *
  37 + * @param updateReqVO 更新信息
  38 + */
  39 + void updateSmsChannel(@Valid SysSmsChannelUpdateReqVO updateReqVO);
  40 +
  41 + /**
  42 + * 删除短信渠道
  43 + *
  44 + * @param id 编号
  45 + */
  46 + void deleteSmsChannel(Long id);
  47 +
  48 + /**
  49 + * 获得短信渠道
  50 + *
  51 + * @param id 编号
  52 + * @return 短信渠道
  53 + */
  54 + SysSmsChannelDO getSmsChannel(Long id);
  55 +
  56 + /**
  57 + * 获得短信渠道列表
  58 + *
  59 + * @param ids 编号
  60 + * @return 短信渠道列表
  61 + */
  62 + List<SysSmsChannelDO> getSmsChannelList(Collection<Long> ids);
  63 +
  64 + /**
  65 + * 获得所有短信渠道列表
  66 + *
  67 + * @return 短信渠道列表
  68 + */
  69 + List<SysSmsChannelDO> getSmsChannelList();
  70 +
  71 + /**
  72 + * 获得短信渠道分页
  73 + *
  74 + * @param pageReqVO 分页查询
  75 + * @return 短信渠道分页
  76 + */
  77 + PageResult<SysSmsChannelDO> getSmsChannelPage(SysSmsChannelPageReqVO pageReqVO);
  78 +
  79 +}
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsLogService.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.service.sms;
  2 +
  3 +import cn.iocoder.dashboard.common.pojo.PageResult;
  4 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogExportReqVO;
  5 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogPageReqVO;
  6 +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsLogDO;
  7 +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
  8 +
  9 +import java.util.Date;
  10 +import java.util.List;
  11 +import java.util.Map;
  12 +
  13 +/**
  14 + * 短信日志 Service 实现类
  15 + *
  16 + * @author zzf
  17 + * @date 13:48 2021/3/2
  18 + */
  19 +public interface SysSmsLogService {
  20 +
  21 + /**
  22 + * 创建短信日志
  23 + *
  24 + * @param mobile 手机号
  25 + * @param userId 用户编号
  26 + * @param userType 用户类型
  27 + * @param isSend 是否发送
  28 + * @param template 短信模板
  29 + * @param templateContent 短信内容
  30 + * @param templateParams 短信参数
  31 + * @return 发送日志编号
  32 + */
  33 + Long createSmsLog(String mobile, Long userId, Integer userType, Boolean isSend,
  34 + SysSmsTemplateDO template, String templateContent, Map<String, Object> templateParams);
  35 +
  36 + /**
  37 + * 更新日志的发送结果
  38 + *
  39 + * @param id 日志编号
  40 + * @param sendCode 发送结果的编码
  41 + * @param sendMsg 发送结果的提示
  42 + * @param apiSendCode 短信 API 发送结果的编码
  43 + * @param apiSendMsg 短信 API 发送失败的提示
  44 + * @param apiRequestId 短信 API 发送返回的唯一请求 ID
  45 + * @param apiSerialNo 短信 API 发送返回的序号
  46 + */
  47 + void updateSmsSendResult(Long id, Integer sendCode, String sendMsg,
  48 + String apiSendCode, String apiSendMsg, String apiRequestId, String apiSerialNo);
  49 +
  50 + /**
  51 + * 更新日志的接收结果
  52 + *
  53 + * @param id 日志编号
  54 + * @param success 是否接收成功
  55 + * @param receiveTime 用户接收时间
  56 + * @param apiReceiveCode API 接收结果的编码
  57 + * @param apiReceiveMsg API 接收结果的说明
  58 + */
  59 + void updateSmsReceiveResult(Long id, Boolean success, Date receiveTime, String apiReceiveCode, String apiReceiveMsg);
  60 +
  61 + /**
  62 + * 获得短信日志分页
  63 + *
  64 + * @param pageReqVO 分页查询
  65 + * @return 短信日志分页
  66 + */
  67 + PageResult<SysSmsLogDO> getSmsLogPage(SysSmsLogPageReqVO pageReqVO);
  68 +
  69 + /**
  70 + * 获得短信日志列表, 用于 Excel 导出
  71 + *
  72 + * @param exportReqVO 查询条件
  73 + * @return 短信日志列表
  74 + */
  75 + List<SysSmsLogDO> getSmsLogList(SysSmsLogExportReqVO exportReqVO);
  76 +
  77 +}