Commit fb2fbbffb8a4cf7d1bc6ec9691682f9136ada3cc
Merge branch 'master' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/multi-datasource
Conflicts: pom.xml src/main/resources/application-local.yaml
Showing
158 changed files
with
8755 additions
and
286 deletions
README.md
| ... | ... | @@ -102,9 +102,9 @@ |
| 102 | 102 | | [Hibernate Validator](https://github.com/hibernate/hibernate-validator) | 参数校验组件 | 6.1.7 | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao) | |
| 103 | 103 | | [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao) | |
| 104 | 104 | | [Knife4j](https://gitee.com/xiaoym/knife4j) | Swagger 增强 UI 实现 | 3.0.2 | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao) | |
| 105 | -| [Resilience4j](https://github.com/quartz-scheduler) | 服务保障组件 | 1.7.0 | [文档](http://www.iocoder.cn/Spring-Boot/Resilience4j/?yudao) | | |
| 105 | +| [Resilience4j](https://github.com/resilience4j/resilience4j) | 服务保障组件 | 1.7.0 | [文档](http://www.iocoder.cn/Spring-Boot/Resilience4j/?yudao) | | |
| 106 | 106 | | [SkyWalking](https://skywalking.apache.org/) | 分布式应用追踪系统 | 8.6.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao) | |
| 107 | -| [Spring Boot Admin](https://github.com/skywalking) | Spring Boot 监控平台 | 8.6.0 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) | | |
| 107 | +| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 8.6.0 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) | | |
| 108 | 108 | | [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.11.4 | | |
| 109 | 109 | | [MapStruct](https://mapstruct.org/) | Java Bean 转换 | 1.4.1 | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao) | |
| 110 | 110 | | [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.16.14 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) | | ... | ... |
pom.xml
| ... | ... | @@ -30,6 +30,7 @@ |
| 30 | 30 | <mysql-connector-java.version>5.1.46</mysql-connector-java.version> |
| 31 | 31 | <druid.version>1.2.4</druid.version> |
| 32 | 32 | <mybatis-plus.version>3.4.2</mybatis-plus.version> |
| 33 | + <dynamic-datasource.version>3.3.2</dynamic-datasource.version> | |
| 33 | 34 | <redisson.version>3.15.1</redisson.version> |
| 34 | 35 | <!-- Config 配置中心相关 --> |
| 35 | 36 | <apollo.version>1.7.0</apollo.version> |
| ... | ... | @@ -46,7 +47,12 @@ |
| 46 | 47 | <easyexcel.verion>2.2.7</easyexcel.verion> |
| 47 | 48 | <velocity.version>2.2</velocity.version> |
| 48 | 49 | <screw.version>1.0.5</screw.version> |
| 49 | - <dynamic-datasource.version>3.3.2</dynamic-datasource.version> | |
| 50 | + <podam.version>7.2.6.RELEASE</podam.version> | |
| 51 | + <jedis-mock.version>0.1.16</jedis-mock.version> | |
| 52 | + <!-- 三方云服务相关 --> | |
| 53 | + <aliyun-java-sdk-core.version>4.5.18</aliyun-java-sdk-core.version> | |
| 54 | + <aliyun-java-sdk-dysmsapi.version>2.1.0</aliyun-java-sdk-dysmsapi.version> | |
| 55 | + <yunpian-java-sdk.version>1.2.7</yunpian-java-sdk.version> | |
| 50 | 56 | </properties> |
| 51 | 57 | |
| 52 | 58 | <!-- 依赖声明 --> |
| ... | ... | @@ -134,6 +140,11 @@ |
| 134 | 140 | <artifactId>mybatis-plus-boot-starter</artifactId> |
| 135 | 141 | <version>${mybatis-plus.version}</version> |
| 136 | 142 | </dependency> |
| 143 | + <dependency> | |
| 144 | + <groupId>com.baomidou</groupId> | |
| 145 | + <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <!-- 多数据源 --> | |
| 146 | + <version>${dynamic-datasource.version}</version> | |
| 147 | + </dependency> | |
| 137 | 148 | |
| 138 | 149 | <dependency> |
| 139 | 150 | <groupId>org.redisson</groupId> |
| ... | ... | @@ -213,14 +224,14 @@ |
| 213 | 224 | <dependency> |
| 214 | 225 | <groupId>com.github.fppt</groupId> <!-- 单元测试,我们采用内嵌的 Redis 数据库 --> |
| 215 | 226 | <artifactId>jedis-mock</artifactId> |
| 216 | - <version>0.1.16</version> | |
| 227 | + <version>${jedis-mock.version}</version> | |
| 217 | 228 | <scope>test</scope> |
| 218 | 229 | </dependency> |
| 219 | 230 | |
| 220 | 231 | <dependency> |
| 221 | 232 | <groupId>uk.co.jemos.podam</groupId> <!-- 单元测试,随机生成 POJO 类 --> |
| 222 | 233 | <artifactId>podam</artifactId> |
| 223 | - <version>7.2.6.RELEASE</version> | |
| 234 | + <version>${podam.version}</version> | |
| 224 | 235 | <scope>test</scope> |
| 225 | 236 | </dependency> |
| 226 | 237 | |
| ... | ... | @@ -272,13 +283,26 @@ |
| 272 | 283 | <version>${screw.version}</version> |
| 273 | 284 | </dependency> |
| 274 | 285 | |
| 275 | - <!-- 多数据源 --> | |
| 286 | + <!-- 三方云服务相关 --> | |
| 287 | + | |
| 288 | + <!-- SMS SDK begin --> | |
| 276 | 289 | <dependency> |
| 277 | - <groupId>com.baomidou</groupId> | |
| 278 | - <artifactId>dynamic-datasource-spring-boot-starter</artifactId> | |
| 279 | - <version>${dynamic-datasource.version}</version> | |
| 290 | + <groupId>com.yunpian.sdk</groupId> | |
| 291 | + <artifactId>yunpian-java-sdk</artifactId> | |
| 292 | + <version>${yunpian-java-sdk.version}</version> | |
| 280 | 293 | </dependency> |
| 281 | 294 | |
| 295 | + <dependency> | |
| 296 | + <groupId>com.aliyun</groupId> | |
| 297 | + <artifactId>aliyun-java-sdk-core</artifactId> | |
| 298 | + <version>${aliyun-java-sdk-core.version}</version> | |
| 299 | + </dependency> | |
| 300 | + <dependency> | |
| 301 | + <groupId>com.aliyun</groupId> | |
| 302 | + <artifactId>aliyun-java-sdk-dysmsapi</artifactId> | |
| 303 | + <version>${aliyun-java-sdk-dysmsapi.version}</version> | |
| 304 | + </dependency> | |
| 305 | + <!-- SMS SDK end --> | |
| 282 | 306 | |
| 283 | 307 | </dependencies> |
| 284 | 308 | ... | ... |
ruoyi-ui/.env.demo1024
0 → 100644
ruoyi-ui/package.json
| ... | ... | @@ -8,6 +8,7 @@ |
| 8 | 8 | "dev": "vue-cli-service serve", |
| 9 | 9 | "build:prod": "vue-cli-service build", |
| 10 | 10 | "build:stage": "vue-cli-service build --mode staging", |
| 11 | + "build:demo1024": "vue-cli-service build --mode demo1024", | |
| 11 | 12 | "preview": "node build/index.js --preview", |
| 12 | 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 | 17 | SYS_OPERATE_TYPE: 'sys_operate_type', |
| 18 | 18 | SYS_LOGIN_RESULT: 'sys_login_result', |
| 19 | 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 | 25 | INF_REDIS_TIMEOUT_TYPE: 'inf_redis_timeout_type', |
| 22 | 26 | INF_JOB_STATUS: 'inf_job_status', | ... | ... |
ruoyi-ui/src/utils/request.js
| ... | ... | @@ -73,6 +73,18 @@ service.interceptors.response.use(res => { |
| 73 | 73 | type: 'error' |
| 74 | 74 | }) |
| 75 | 75 | return Promise.reject(new Error(msg)) |
| 76 | + } else if (code === 901) { | |
| 77 | + Message({ | |
| 78 | + type: 'error', | |
| 79 | + duration: 0, | |
| 80 | + dangerouslyUseHTMLString: true, | |
| 81 | + message: '<div>演示模式,不发进行写操作</div>' | |
| 82 | + + '<div> </div>' | |
| 83 | + + '<div>参考 https://www.iocoder.cn/Yudao/build-debugger-environment 教程</div>' | |
| 84 | + + '<div> </div>' | |
| 85 | + + '<div>5 分钟搭建本地环境</div>', | |
| 86 | + }) | |
| 87 | + return Promise.reject(new Error(msg)) | |
| 76 | 88 | } else if (code !== 200) { |
| 77 | 89 | Notification.error({ |
| 78 | 90 | title: msg | ... | ... |
ruoyi-ui/src/views/system/operatelog/index.vue
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> | ... | ... |
ruoyi-ui/src/views/system/user/index.vue
| ... | ... | @@ -362,7 +362,7 @@ export default { |
| 362 | 362 | created() { |
| 363 | 363 | this.getList(); |
| 364 | 364 | this.getTreeselect(); |
| 365 | - this.getConfigKey("sys.user.initPassword").then(response => { | |
| 365 | + this.getConfigKey("sys.user.init-password").then(response => { | |
| 366 | 366 | this.initPassword = response.msg; |
| 367 | 367 | }); |
| 368 | 368 | }, | ... | ... |
sql/ruoyi-vue-pro.sql
| 1 | 1 | /* |
| 2 | 2 | Navicat Premium Data Transfer |
| 3 | 3 | |
| 4 | - Source Server : 127.0.0.1 | |
| 4 | + Source Server : local-mysql001 | |
| 5 | 5 | Source Server Type : MySQL |
| 6 | 6 | Source Server Version : 50718 |
| 7 | 7 | Source Host : localhost:3306 |
| ... | ... | @@ -11,7 +11,7 @@ |
| 11 | 11 | Target Server Version : 50718 |
| 12 | 12 | File Encoding : 65001 |
| 13 | 13 | |
| 14 | - Date: 21/03/2021 18:53:24 | |
| 14 | + Date: 18/04/2021 00:36:06 | |
| 15 | 15 | */ |
| 16 | 16 | |
| 17 | 17 | SET NAMES utf8mb4; |
| ... | ... | @@ -43,7 +43,7 @@ CREATE TABLE `inf_api_access_log` ( |
| 43 | 43 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 44 | 44 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 45 | 45 | PRIMARY KEY (`id`) USING BTREE |
| 46 | -) ENGINE=InnoDB AUTO_INCREMENT=1318 DEFAULT CHARSET=utf8mb4 COMMENT='API 访问日志表'; | |
| 46 | +) ENGINE=InnoDB AUTO_INCREMENT=3750 DEFAULT CHARSET=utf8mb4 COMMENT='API 访问日志表'; | |
| 47 | 47 | |
| 48 | 48 | -- ---------------------------- |
| 49 | 49 | -- Records of inf_api_access_log |
| ... | ... | @@ -84,7 +84,7 @@ CREATE TABLE `inf_api_error_log` ( |
| 84 | 84 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 85 | 85 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 86 | 86 | PRIMARY KEY (`id`) USING BTREE |
| 87 | -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COMMENT='系统异常日志'; | |
| 87 | +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COMMENT='系统异常日志'; | |
| 88 | 88 | |
| 89 | 89 | -- ---------------------------- |
| 90 | 90 | -- Records of inf_api_error_log |
| ... | ... | @@ -201,7 +201,7 @@ CREATE TABLE `inf_job_log` ( |
| 201 | 201 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 202 | 202 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 203 | 203 | PRIMARY KEY (`id`) USING BTREE |
| 204 | -) ENGINE=InnoDB AUTO_INCREMENT=627 DEFAULT CHARSET=utf8mb4 COMMENT='定时任务日志表'; | |
| 204 | +) ENGINE=InnoDB AUTO_INCREMENT=2322 DEFAULT CHARSET=utf8mb4 COMMENT='定时任务日志表'; | |
| 205 | 205 | |
| 206 | 206 | -- ---------------------------- |
| 207 | 207 | -- Records of inf_job_log |
| ... | ... | @@ -264,7 +264,7 @@ CREATE TABLE `sys_dict_data` ( |
| 264 | 264 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 265 | 265 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 266 | 266 | PRIMARY KEY (`id`) USING BTREE |
| 267 | -) ENGINE=InnoDB AUTO_INCREMENT=65 DEFAULT CHARSET=utf8mb4 COMMENT='å—典数æ®è¡¨'; | |
| 267 | +) ENGINE=InnoDB AUTO_INCREMENT=78 DEFAULT CHARSET=utf8mb4 COMMENT='å—典数æ®è¡¨'; | |
| 268 | 268 | |
| 269 | 269 | -- ---------------------------- |
| 270 | 270 | -- Records of sys_dict_data |
| ... | ... | @@ -324,6 +324,19 @@ INSERT INTO `sys_dict_data` VALUES (61, 2, '管ç†å‘˜', '2', 'user_type', 0, NUL |
| 324 | 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 | 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 | 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, 1, '云片', 'YUN_PIAN', 'sys_sms_channel_code', 0, NULL, '1', '2021-04-05 01:05:14', '1', '2021-04-13 00:20:13', b'0'); | |
| 328 | +INSERT INTO `sys_dict_data` VALUES (66, 2, '阿里云', 'ALIYUN', 'sys_sms_channel_code', 0, NULL, '1', '2021-04-05 01:05:26', '1', '2021-04-13 00:20:16', 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'); | |
| 332 | +INSERT INTO `sys_dict_data` VALUES (70, 0, 'åˆå§‹åŒ–', '0', 'sys_sms_send_status', 0, NULL, '1', '2021-04-11 20:18:33', '1', '2021-04-11 09:30:18', b'0'); | |
| 333 | +INSERT INTO `sys_dict_data` VALUES (71, 1, 'å‘逿ˆåŠŸ', '10', 'sys_sms_send_status', 0, NULL, '1', '2021-04-11 20:18:43', '1', '2021-04-11 09:30:20', b'0'); | |
| 334 | +INSERT INTO `sys_dict_data` VALUES (72, 2, 'å‘é€å¤±è´¥', '20', 'sys_sms_send_status', 0, NULL, '1', '2021-04-11 20:18:49', '1', '2021-04-11 09:30:22', b'0'); | |
| 335 | +INSERT INTO `sys_dict_data` VALUES (73, 3, 'ä¸å‘é€', '30', 'sys_sms_send_status', 0, NULL, '1', '2021-04-11 20:19:44', '1', '2021-04-11 09:30:23', b'0'); | |
| 336 | +INSERT INTO `sys_dict_data` VALUES (74, 0, 'ç‰å¾…结果', '0', 'sys_sms_receive_status', 0, NULL, '1', '2021-04-11 20:27:43', '1', '2021-04-17 23:05:19', b'0'); | |
| 337 | +INSERT INTO `sys_dict_data` VALUES (75, 1, '接收æˆåŠŸ', '10', 'sys_sms_receive_status', 0, NULL, '1', '2021-04-11 20:29:25', '1', '2021-04-11 20:29:35', b'0'); | |
| 338 | +INSERT INTO `sys_dict_data` VALUES (76, 2, '接收失败', '20', 'sys_sms_receive_status', 0, NULL, '1', '2021-04-11 20:29:31', '1', '2021-04-11 20:29:39', b'0'); | |
| 339 | +INSERT INTO `sys_dict_data` VALUES (77, 0, '调试(钉钉)', 'DEBUG_DING_TALK', 'sys_sms_channel_code', 0, NULL, '1', '2021-04-13 00:20:37', '1', '2021-04-13 00:20:37', b'0'); | |
| 327 | 340 | COMMIT; |
| 328 | 341 | |
| 329 | 342 | -- ---------------------------- |
| ... | ... | @@ -343,7 +356,7 @@ CREATE TABLE `sys_dict_type` ( |
| 343 | 356 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 344 | 357 | PRIMARY KEY (`id`) USING BTREE, |
| 345 | 358 | UNIQUE KEY `dict_type` (`type`) |
| 346 | -) ENGINE=InnoDB AUTO_INCREMENT=111 DEFAULT CHARSET=utf8mb4 COMMENT='å—典类型表'; | |
| 359 | +) ENGINE=InnoDB AUTO_INCREMENT=115 DEFAULT CHARSET=utf8mb4 COMMENT='å—典类型表'; | |
| 347 | 360 | |
| 348 | 361 | -- ---------------------------- |
| 349 | 362 | -- Records of sys_dict_type |
| ... | ... | @@ -366,6 +379,10 @@ INSERT INTO `sys_dict_type` VALUES (107, '定时任务状æ€', 'inf_job_status', |
| 366 | 379 | 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 | 380 | 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 | 381 | 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'); |
| 382 | +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'); | |
| 383 | +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'); | |
| 384 | +INSERT INTO `sys_dict_type` VALUES (113, 'çŸä¿¡å‘é€çжæ€', 'sys_sms_send_status', 0, NULL, '1', '2021-04-11 20:18:03', '1', '2021-04-11 09:30:02', b'0'); | |
| 385 | +INSERT INTO `sys_dict_type` VALUES (114, 'çŸä¿¡æŽ¥æ”¶çжæ€', 'sys_sms_receive_status', 0, NULL, '1', '2021-04-11 20:27:14', '1', '2021-04-11 20:27:14', b'0'); | |
| 369 | 386 | COMMIT; |
| 370 | 387 | |
| 371 | 388 | -- ---------------------------- |
| ... | ... | @@ -386,7 +403,7 @@ CREATE TABLE `sys_login_log` ( |
| 386 | 403 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 387 | 404 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 388 | 405 | PRIMARY KEY (`id`) USING BTREE |
| 389 | -) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8mb4 COMMENT='系统访问记录'; | |
| 406 | +) ENGINE=InnoDB AUTO_INCREMENT=84 DEFAULT CHARSET=utf8mb4 COMMENT='系统访问记录'; | |
| 390 | 407 | |
| 391 | 408 | -- ---------------------------- |
| 392 | 409 | -- Records of sys_login_log |
| ... | ... | @@ -415,7 +432,7 @@ CREATE TABLE `sys_menu` ( |
| 415 | 432 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 416 | 433 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 417 | 434 | PRIMARY KEY (`id`) USING BTREE |
| 418 | -) ENGINE=InnoDB AUTO_INCREMENT=1093 DEFAULT CHARSET=utf8mb4 COMMENT='èœå•æƒé™è¡¨'; | |
| 435 | +) ENGINE=InnoDB AUTO_INCREMENT=1110 DEFAULT CHARSET=utf8mb4 COMMENT='èœå•æƒé™è¡¨'; | |
| 419 | 436 | |
| 420 | 437 | -- ---------------------------- |
| 421 | 438 | -- Records of sys_menu |
| ... | ... | @@ -530,6 +547,22 @@ INSERT INTO `sys_menu` VALUES (1089, '日志查询', 'infra:api-error-log:query' |
| 530 | 547 | 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 | 548 | 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 | 549 | 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'); |
| 550 | +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'); | |
| 551 | +INSERT INTO `sys_menu` VALUES (1094, 'çŸä¿¡æ¸ é“', '', 2, 0, 1093, 'sms-channel', 'phone', 'system/sms/smsChannel', 0, '', '2021-04-01 11:07:15', '1', '2021-04-09 23:44:07', b'0'); | |
| 552 | +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'); | |
| 553 | +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'); | |
| 554 | +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'); | |
| 555 | +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'); | |
| 556 | +INSERT INTO `sys_menu` VALUES (1100, 'çŸä¿¡æ¨¡æ¿', '', 2, 1, 1093, 'sms-template', 'phone', 'system/sms/smsTemplate', 0, '', '2021-04-01 17:35:17', '1', '2021-04-11 19:34:21', b'0'); | |
| 557 | +INSERT INTO `sys_menu` VALUES (1101, 'çŸä¿¡æ¨¡æ¿æŸ¥è¯¢', 'system:sms-template:query', 3, 1, 1100, '', '', '', 0, '', '2021-04-01 17:35:17', '', '2021-04-01 17:35:17', b'0'); | |
| 558 | +INSERT INTO `sys_menu` VALUES (1102, 'çŸä¿¡æ¨¡æ¿åˆ›å»º', 'system:sms-template:create', 3, 2, 1100, '', '', '', 0, '', '2021-04-01 17:35:17', '', '2021-04-01 17:35:17', b'0'); | |
| 559 | +INSERT INTO `sys_menu` VALUES (1103, 'çŸä¿¡æ¨¡æ¿æ›´æ–°', 'system:sms-template:update', 3, 3, 1100, '', '', '', 0, '', '2021-04-01 17:35:17', '', '2021-04-01 17:35:17', b'0'); | |
| 560 | +INSERT INTO `sys_menu` VALUES (1104, 'çŸä¿¡æ¨¡æ¿åˆ 除', 'system:sms-template:delete', 3, 4, 1100, '', '', '', 0, '', '2021-04-01 17:35:17', '', '2021-04-01 17:35:17', b'0'); | |
| 561 | +INSERT INTO `sys_menu` VALUES (1105, 'çŸä¿¡æ¨¡æ¿å¯¼å‡º', 'system:sms-template:export', 3, 5, 1100, '', '', '', 0, '', '2021-04-01 17:35:17', '', '2021-04-01 17:35:17', b'0'); | |
| 562 | +INSERT INTO `sys_menu` VALUES (1106, 'å‘逿µ‹è¯•çŸä¿¡', 'system:sms-template:send-sms', 3, 6, 1100, '', '', '', 0, '1', '2021-04-11 00:26:40', '1', '2021-04-11 00:26:40', b'0'); | |
| 563 | +INSERT INTO `sys_menu` VALUES (1107, 'çŸä¿¡æ—¥å¿—', '', 2, 2, 1093, 'sms-log', 'phone', 'system/sms/smsLog', 0, '', '2021-04-11 08:37:05', '1', '2021-04-11 19:34:25', b'0'); | |
| 564 | +INSERT INTO `sys_menu` VALUES (1108, 'çŸä¿¡æ—¥å¿—查询', 'system:sms-log:query', 3, 1, 1107, '', '', '', 0, '', '2021-04-11 08:37:05', '', '2021-04-11 08:37:05', b'0'); | |
| 565 | +INSERT INTO `sys_menu` VALUES (1109, 'çŸä¿¡æ—¥å¿—导出', 'system:sms-log:export', 3, 5, 1107, '', '', '', 0, '', '2021-04-11 08:37:05', '', '2021-04-11 08:37:05', b'0'); | |
| 533 | 566 | COMMIT; |
| 534 | 567 | |
| 535 | 568 | -- ---------------------------- |
| ... | ... | @@ -589,7 +622,7 @@ CREATE TABLE `sys_operate_log` ( |
| 589 | 622 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 590 | 623 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 591 | 624 | PRIMARY KEY (`id`) USING BTREE |
| 592 | -) ENGINE=InnoDB AUTO_INCREMENT=65 DEFAULT CHARSET=utf8mb4 COMMENT='æ“作日志记录'; | |
| 625 | +) ENGINE=InnoDB AUTO_INCREMENT=177 DEFAULT CHARSET=utf8mb4 COMMENT='æ“作日志记录'; | |
| 593 | 626 | |
| 594 | 627 | -- ---------------------------- |
| 595 | 628 | -- Records of sys_operate_log |
| ... | ... | @@ -835,6 +868,116 @@ INSERT INTO `sys_role_menu` VALUES (238, 101, 1065, '', '2021-01-21 03:23:27', ' |
| 835 | 868 | COMMIT; |
| 836 | 869 | |
| 837 | 870 | -- ---------------------------- |
| 871 | +-- Table structure for sys_sms_channel | |
| 872 | +-- ---------------------------- | |
| 873 | +DROP TABLE IF EXISTS `sys_sms_channel`; | |
| 874 | +CREATE TABLE `sys_sms_channel` ( | |
| 875 | + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ç¼–å·', | |
| 876 | + `signature` varchar(10) NOT NULL COMMENT 'çŸä¿¡ç¾å', | |
| 877 | + `code` varchar(63) NOT NULL COMMENT 'æ¸ é“ç¼–ç ', | |
| 878 | + `status` tinyint(4) NOT NULL COMMENT 'å¼€å¯çжæ€', | |
| 879 | + `remark` varchar(255) DEFAULT NULL COMMENT '备注', | |
| 880 | + `api_key` varchar(128) NOT NULL COMMENT 'çŸä¿¡ API 的账å·', | |
| 881 | + `api_secret` varchar(128) DEFAULT NULL COMMENT 'çŸä¿¡ API 的秘钥', | |
| 882 | + `callback_url` varchar(255) DEFAULT NULL COMMENT 'çŸä¿¡å‘é€å›žè°ƒ URL', | |
| 883 | + `creator` varchar(64) DEFAULT '' COMMENT '创建者', | |
| 884 | + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | |
| 885 | + `updater` varchar(64) DEFAULT '' COMMENT '更新者', | |
| 886 | + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', | |
| 887 | + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', | |
| 888 | + PRIMARY KEY (`id`) USING BTREE | |
| 889 | +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COMMENT='çŸä¿¡æ¸ é“'; | |
| 890 | + | |
| 891 | +-- ---------------------------- | |
| 892 | +-- Records of sys_sms_channel | |
| 893 | +-- ---------------------------- | |
| 894 | +BEGIN; | |
| 895 | +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-13 01:03:19', b'0'); | |
| 896 | +INSERT INTO `sys_sms_channel` VALUES (2, 'Ballcat', 'ALIYUN', 0, '啦啦啦', 'LTAI5tCnKso2uG3kJ5gRav88', 'fGJ5SNXL7P1NHNRmJ7DJaMJGPyE55C', NULL, '', '2021-03-31 11:53:10', '1', '2021-04-14 00:08:37', b'0'); | |
| 897 | +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-11 16:21:58', b'1'); | |
| 898 | +INSERT INTO `sys_sms_channel` VALUES (4, 'æµ‹è¯•æ¸ é“', 'DEBUG_DING_TALK', 0, '123', '696b5d8ead48071237e4aa5861ff08dbadb2b4ded1c688a7b7c9afc615579859', 'SEC5c4e5ff888bc8a9923ae47f59e7ccd30af1f14d93c55b4e2c9cb094e35aeed67', NULL, '1', '2021-04-13 00:23:14', '1', '2021-04-14 00:07:10', b'0'); | |
| 899 | +COMMIT; | |
| 900 | + | |
| 901 | +-- ---------------------------- | |
| 902 | +-- Table structure for sys_sms_log | |
| 903 | +-- ---------------------------- | |
| 904 | +DROP TABLE IF EXISTS `sys_sms_log`; | |
| 905 | +CREATE TABLE `sys_sms_log` ( | |
| 906 | + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ç¼–å·', | |
| 907 | + `channel_id` bigint(20) NOT NULL COMMENT 'çŸä¿¡æ¸ é“ç¼–å·', | |
| 908 | + `channel_code` varchar(63) NOT NULL COMMENT 'çŸä¿¡æ¸ é“ç¼–ç ', | |
| 909 | + `template_id` bigint(20) NOT NULL COMMENT '模æ¿ç¼–å·', | |
| 910 | + `template_code` varchar(63) NOT NULL COMMENT '模æ¿ç¼–ç ', | |
| 911 | + `template_type` tinyint(4) NOT NULL COMMENT 'çŸä¿¡ç±»åž‹', | |
| 912 | + `template_content` varchar(255) NOT NULL COMMENT 'çŸä¿¡å†…容', | |
| 913 | + `template_params` varchar(255) NOT NULL COMMENT 'çŸä¿¡å‚æ•°', | |
| 914 | + `api_template_id` varchar(63) NOT NULL COMMENT 'çŸä¿¡ API 的模æ¿ç¼–å·', | |
| 915 | + `mobile` varchar(11) NOT NULL COMMENT '手机å·', | |
| 916 | + `user_id` bigint(20) DEFAULT NULL COMMENT '用户编å·', | |
| 917 | + `user_type` tinyint(4) DEFAULT NULL COMMENT '用户类型', | |
| 918 | + `send_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT 'å‘é€çжæ€', | |
| 919 | + `send_time` datetime DEFAULT NULL COMMENT 'å‘逿—¶é—´', | |
| 920 | + `send_code` int(11) DEFAULT NULL COMMENT 'å‘é€ç»“果的编ç ', | |
| 921 | + `send_msg` varchar(255) DEFAULT NULL COMMENT 'å‘é€ç»“果的æç¤º', | |
| 922 | + `api_send_code` varchar(63) DEFAULT NULL COMMENT 'çŸä¿¡ API å‘é€ç»“果的编ç ', | |
| 923 | + `api_send_msg` varchar(255) DEFAULT NULL COMMENT 'çŸä¿¡ API å‘é€å¤±è´¥çš„æç¤º', | |
| 924 | + `api_request_id` varchar(255) DEFAULT NULL COMMENT 'çŸä¿¡ API å‘é€è¿”回的唯一请求 ID', | |
| 925 | + `api_serial_no` varchar(255) DEFAULT NULL COMMENT 'çŸä¿¡ API å‘é€è¿”回的åºå·', | |
| 926 | + `receive_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '接收状æ€', | |
| 927 | + `receive_time` datetime DEFAULT NULL COMMENT '接收时间', | |
| 928 | + `api_receive_code` varchar(63) DEFAULT NULL COMMENT 'API 接收结果的编ç ', | |
| 929 | + `api_receive_msg` varchar(255) DEFAULT NULL COMMENT 'API 接收结果的说明', | |
| 930 | + `creator` varchar(64) DEFAULT '' COMMENT '创建者', | |
| 931 | + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | |
| 932 | + `updater` varchar(64) DEFAULT '' COMMENT '更新者', | |
| 933 | + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', | |
| 934 | + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', | |
| 935 | + PRIMARY KEY (`id`) USING BTREE | |
| 936 | +) ENGINE=InnoDB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8mb4 COMMENT='çŸä¿¡æ—¥å¿—'; | |
| 937 | + | |
| 938 | +-- ---------------------------- | |
| 939 | +-- Records of sys_sms_log | |
| 940 | +-- ---------------------------- | |
| 941 | +BEGIN; | |
| 942 | +COMMIT; | |
| 943 | + | |
| 944 | +-- ---------------------------- | |
| 945 | +-- Table structure for sys_sms_template | |
| 946 | +-- ---------------------------- | |
| 947 | +DROP TABLE IF EXISTS `sys_sms_template`; | |
| 948 | +CREATE TABLE `sys_sms_template` ( | |
| 949 | + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ç¼–å·', | |
| 950 | + `type` tinyint(4) NOT NULL COMMENT 'çŸä¿¡ç¾å', | |
| 951 | + `status` tinyint(4) NOT NULL COMMENT 'å¼€å¯çжæ€', | |
| 952 | + `code` varchar(63) NOT NULL COMMENT '模æ¿ç¼–ç ', | |
| 953 | + `name` varchar(63) NOT NULL COMMENT '模æ¿åç§°', | |
| 954 | + `content` varchar(255) NOT NULL COMMENT '模æ¿å†…容', | |
| 955 | + `params` varchar(255) NOT NULL COMMENT '傿•°æ•°ç»„', | |
| 956 | + `remark` varchar(255) DEFAULT NULL COMMENT '备注', | |
| 957 | + `api_template_id` varchar(63) NOT NULL COMMENT 'çŸä¿¡ API 的模æ¿ç¼–å·', | |
| 958 | + `channel_id` bigint(20) NOT NULL COMMENT 'çŸä¿¡æ¸ é“ç¼–å·', | |
| 959 | + `channel_code` varchar(63) NOT NULL COMMENT 'çŸä¿¡æ¸ é“ç¼–ç ', | |
| 960 | + `creator` varchar(64) DEFAULT '' COMMENT '创建者', | |
| 961 | + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | |
| 962 | + `updater` varchar(64) DEFAULT '' COMMENT '更新者', | |
| 963 | + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', | |
| 964 | + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', | |
| 965 | + PRIMARY KEY (`id`) USING BTREE | |
| 966 | +) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COMMENT='çŸä¿¡æ¨¡æ¿'; | |
| 967 | + | |
| 968 | +-- ---------------------------- | |
| 969 | +-- Records of sys_sms_template | |
| 970 | +-- ---------------------------- | |
| 971 | +BEGIN; | |
| 972 | +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', '1', '2021-04-10 01:22:00', b'0'); | |
| 973 | +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', '1', '2021-04-10 01:22:02', b'0'); | |
| 974 | +INSERT INTO `sys_sms_template` VALUES (4, 3, 0, 'test-ooxx', '测试模æ¿', 'biubiubiu', '[]', '1111', '110', 1, 'YUN_PIAN', '1', '2021-04-09 23:58:42', '1', '2021-04-02 07:47:12', b'1'); | |
| 975 | +INSERT INTO `sys_sms_template` VALUES (5, 3, 0, 'test-ooxx', '测试模æ¿', 'biubiubiu', '[]', '1111', '110', 1, 'YUN_PIAN', '1', '2021-04-09 23:58:42', '1', '2021-04-02 08:32:22', b'1'); | |
| 976 | +INSERT INTO `sys_sms_template` VALUES (6, 3, 0, 'test-01', '测试模æ¿', '哈哈哈 {name}', '[\"name\"]', 'f哈哈哈', '4383920', 1, 'YUN_PIAN', '1', '2021-04-10 01:07:21', '1', '2021-04-10 01:22:05', b'0'); | |
| 977 | +INSERT INTO `sys_sms_template` VALUES (7, 3, 0, 'test-04', '测试下', 'è€é¸¡{name},牛逼{code}', '[\"name\",\"code\"]', NULL, 'suibian', 4, 'DEBUG_DING_TALK', '1', '2021-04-13 00:29:53', '1', '2021-04-14 00:30:38', b'0'); | |
| 978 | +COMMIT; | |
| 979 | + | |
| 980 | +-- ---------------------------- | |
| 838 | 981 | -- Table structure for sys_user |
| 839 | 982 | -- ---------------------------- |
| 840 | 983 | DROP TABLE IF EXISTS `sys_user`; |
| ... | ... | @@ -865,8 +1008,8 @@ CREATE TABLE `sys_user` ( |
| 865 | 1008 | -- Records of sys_user |
| 866 | 1009 | -- ---------------------------- |
| 867 | 1010 | 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'); | |
| 1011 | +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'); | |
| 1012 | +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 | 1013 | 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 | 1014 | 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 | 1015 | 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,13 +1067,40 @@ CREATE TABLE `sys_user_session` ( |
| 924 | 1067 | -- Records of sys_user_session |
| 925 | 1068 | -- ---------------------------- |
| 926 | 1069 | BEGIN; |
| 1070 | +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'); | |
| 1071 | +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'); | |
| 1072 | +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-01 17:25:41', b'1'); | |
| 1073 | +INSERT INTO `sys_user_session` VALUES ('12166cd28b4f448ea468d13c471dfc6e', 1, '2021-04-09 19:48:58', '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-09 19:18:58', NULL, '2021-04-02 04:33:15', b'1'); | |
| 1074 | +INSERT INTO `sys_user_session` VALUES ('134d908ae33146bd9b5291471c04f604', 1, '2021-04-10 00:29: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-04-09 23:10:26', NULL, '2021-04-02 08:08:17', b'1'); | |
| 1075 | +INSERT INTO `sys_user_session` VALUES ('1407ce21e47947b9b8d93bff1b55c7d6', 1, '2021-04-07 01:09:11', '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-07 00:07:43', NULL, '2021-04-01 18:10:44', b'1'); | |
| 1076 | +INSERT INTO `sys_user_session` VALUES ('1477c38290ff4cee8887ebfe593faa02', 1, '2021-04-17 23:34:57', '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-17 23:04:57', NULL, '2021-04-12 21:36:29', b'1'); | |
| 1077 | +INSERT INTO `sys_user_session` VALUES ('3c75ea73e13b4857a18eb57ca2eea80f', 1, '2021-04-11 20:06:52', '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-11 19:36:52', NULL, '2021-04-11 09:10:53', b'1'); | |
| 1078 | +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'); | |
| 1079 | +INSERT INTO `sys_user_session` VALUES ('43676e85d0e04980b2a67181f8d9933b', 1, '2021-04-11 10:41:09', '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-11 09:56:31', NULL, '2021-04-02 17:32:07', b'1'); | |
| 927 | 1080 | 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'); |
| 1081 | +INSERT INTO `sys_user_session` VALUES ('56a92b99f7b642ccbb7508042a25cb0c', 1, '2021-04-11 21:16:33', '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-11 20:14:54', NULL, '2021-04-11 10:08:20', b'1'); | |
| 928 | 1082 | 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'); |
| 1083 | +INSERT INTO `sys_user_session` VALUES ('5c30d80eb72048daa1a24d3d4f01317b', 1, '2021-04-12 00:31:10', '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-11 23:08:14', NULL, '2021-04-11 15:47:45', b'1'); | |
| 1084 | +INSERT INTO `sys_user_session` VALUES ('5dca80a5c61541479a4dbb6e004c2e28', 1, '2021-04-14 00:57:25', '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-13 23:38:30', NULL, '2021-04-12 17:33:48', b'1'); | |
| 1085 | +INSERT INTO `sys_user_session` VALUES ('7324a76b029a49ee95bf54ceb4164ba9', 1, '2021-04-13 01:29:14', '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-13 00:18:40', NULL, '2021-04-11 23:15:00', b'1'); | |
| 1086 | +INSERT INTO `sys_user_session` VALUES ('749619894bc441bb9773902515f81e6a', 1, '2021-04-11 00:39:51', '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-11 00:09:51', NULL, '2021-04-02 16:50:56', b'1'); | |
| 1087 | +INSERT INTO `sys_user_session` VALUES ('7768ae62ad974fd989f5159649a4be82', 1, '2021-04-11 00:53:39', '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-10 23:23:27', NULL, '2021-04-02 17:04:58', b'1'); | |
| 1088 | +INSERT INTO `sys_user_session` VALUES ('79efcb8f64aa42af9f4b327fb383532f', 1, '2021-04-11 22:44:07', '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-11 21:34:53', NULL, '2021-04-11 14:10:45', b'1'); | |
| 1089 | +INSERT INTO `sys_user_session` VALUES ('87d5b95fdad9447189a95abf8a5152df', 1, '2021-04-17 23:01:18', '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-17 22:31:18', NULL, '2021-04-12 21:03:27', b'1'); | |
| 1090 | +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 | 1091 | 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'); |
| 1092 | +INSERT INTO `sys_user_session` VALUES ('a2fb443b31c049008975ff8ee5499db1', 1, '2021-04-11 09:42:09', '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-11 09:12:09', NULL, '2021-04-02 17:16:00', b'1'); | |
| 1093 | +INSERT INTO `sys_user_session` VALUES ('a71a74adf9d141e2849d2a411d558205', 1, '2021-04-17 18:24:44', '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-17 17:54:44', NULL, '2021-04-12 17:54:50', b'1'); | |
| 930 | 1094 | 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'); | |
| 1095 | +INSERT INTO `sys_user_session` VALUES ('b727853eccea4c8589e006ffea985146', 1, '2021-04-12 01:36:00', '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-12 01:06:00', NULL, '2021-04-11 19:40:07', b'1'); | |
| 1096 | +INSERT INTO `sys_user_session` VALUES ('c095616db95044c5bed66a3f84519b8b', 1, '2021-04-11 19:59:33', '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-11 19:29:33', NULL, '2021-04-11 09:04:19', b'1'); | |
| 1097 | +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'); | |
| 1098 | +INSERT INTO `sys_user_session` VALUES ('dfbce0af867547f4bb01ac6f2e583337', 1, '2021-04-11 17:06:15', '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-11 16:36:15', NULL, '2021-04-11 08:24:03', b'1'); | |
| 1099 | +INSERT INTO `sys_user_session` VALUES ('e5ecf10e40a5463b8f9b5b453cb1649b', 1, '2021-04-11 17:06: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-11 16:36:22', NULL, '2021-04-11 08:24:03', b'1'); | |
| 1100 | +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 | 1101 | 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 | 1102 | 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'); |
| 1103 | +INSERT INTO `sys_user_session` VALUES ('f2a4182f62ea4559b9946a523928fac4', 1, '2021-04-10 01:45:57', '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-10 00:55:38', NULL, '2021-04-02 14:52:48', b'1'); | |
| 934 | 1104 | 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'); |
| 935 | 1105 | COMMIT; |
| 936 | 1106 | |
| ... | ... | @@ -964,7 +1134,7 @@ CREATE TABLE `tool_codegen_column` ( |
| 964 | 1134 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 965 | 1135 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 966 | 1136 | PRIMARY KEY (`id`) USING BTREE |
| 967 | -) ENGINE=InnoDB AUTO_INCREMENT=389 DEFAULT CHARSET=utf8mb4 COMMENT='代ç 生æˆè¡¨å—段定义'; | |
| 1137 | +) ENGINE=InnoDB AUTO_INCREMENT=447 DEFAULT CHARSET=utf8mb4 COMMENT='代ç 生æˆè¡¨å—段定义'; | |
| 968 | 1138 | |
| 969 | 1139 | -- ---------------------------- |
| 970 | 1140 | -- Records of tool_codegen_column |
| ... | ... | @@ -1126,6 +1296,64 @@ INSERT INTO `tool_codegen_column` VALUES (385, 33, 'create_time', 'datetime', 'å |
| 1126 | 1296 | 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 | 1297 | 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 | 1298 | 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'); |
| 1299 | +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'); | |
| 1300 | +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'); | |
| 1301 | +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'); | |
| 1302 | +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'); | |
| 1303 | +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'); | |
| 1304 | +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'); | |
| 1305 | +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'); | |
| 1306 | +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'); | |
| 1307 | +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'); | |
| 1308 | +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'); | |
| 1309 | +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'); | |
| 1310 | +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'); | |
| 1311 | +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'); | |
| 1312 | +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'); | |
| 1313 | +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'); | |
| 1314 | +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'); | |
| 1315 | +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'); | |
| 1316 | +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'); | |
| 1317 | +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'); | |
| 1318 | +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'); | |
| 1319 | +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'); | |
| 1320 | +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'); | |
| 1321 | +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'); | |
| 1322 | +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'); | |
| 1323 | +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'); | |
| 1324 | +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'); | |
| 1325 | +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'); | |
| 1326 | +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'); | |
| 1327 | +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'); | |
| 1328 | +INSERT INTO `tool_codegen_column` VALUES (418, 36, 'id', 'bigint(20)', 'ç¼–å·', b'0', b'1', '1', 1, 'Long', 'id', '', '1024', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1329 | +INSERT INTO `tool_codegen_column` VALUES (419, 36, 'channel_id', 'bigint(20)', 'çŸä¿¡æ¸ é“ç¼–å·', b'0', b'0', '0', 2, 'Long', 'channelId', '', '10', b'0', b'0', b'1', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1330 | +INSERT INTO `tool_codegen_column` VALUES (420, 36, 'channel_code', 'varchar(63)', 'çŸä¿¡æ¸ é“ç¼–ç ', b'0', b'0', '0', 3, 'String', 'channelCode', '', 'ALIYUN', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1331 | +INSERT INTO `tool_codegen_column` VALUES (421, 36, 'template_id', 'bigint(20)', '模æ¿ç¼–å·', b'0', b'0', '0', 4, 'Long', 'templateId', '', '20', b'0', b'0', b'1', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1332 | +INSERT INTO `tool_codegen_column` VALUES (422, 36, 'template_code', 'varchar(63)', '模æ¿ç¼–ç ', b'0', b'0', '0', 5, 'String', 'templateCode', '', 'test-01', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1333 | +INSERT INTO `tool_codegen_column` VALUES (423, 36, 'template_type', 'tinyint(4)', 'çŸä¿¡ç±»åž‹', b'0', b'0', '0', 6, 'Integer', 'templateType', 'sys_sms_template_type', '1', b'0', b'0', b'0', '=', b'1', 'select', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1334 | +INSERT INTO `tool_codegen_column` VALUES (424, 36, 'template_content', 'varchar(255)', 'çŸä¿¡å†…容', b'0', b'0', '0', 7, 'String', 'templateContent', '', 'ä½ å¥½ï¼Œä½ çš„éªŒè¯ç 是 1024', b'0', b'0', b'0', '=', b'1', 'editor', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1335 | +INSERT INTO `tool_codegen_column` VALUES (425, 36, 'template_params', 'varchar(255)', 'çŸä¿¡å‚æ•°', b'0', b'0', '0', 8, 'String', 'templateParams', '', 'name,code', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1336 | +INSERT INTO `tool_codegen_column` VALUES (426, 36, 'api_template_id', 'varchar(63)', 'çŸä¿¡ API 的模æ¿ç¼–å·', b'0', b'0', '0', 9, 'String', 'apiTemplateId', '', 'SMS_207945135', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1337 | +INSERT INTO `tool_codegen_column` VALUES (427, 36, 'mobile', 'varchar(11)', '手机å·', b'0', b'0', '0', 10, 'String', 'mobile', '', '15601691300', b'0', b'0', b'1', 'LIKE', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1338 | +INSERT INTO `tool_codegen_column` VALUES (428, 36, 'user_id', 'bigint(20)', '用户编å·', b'1', b'0', '0', 11, 'Long', 'userId', '', '10', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1339 | +INSERT INTO `tool_codegen_column` VALUES (429, 36, 'user_type', 'tinyint(4)', '用户类型', b'1', b'0', '0', 12, 'Integer', 'userType', 'user_type', '1', b'0', b'0', b'0', '=', b'1', 'select', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1340 | +INSERT INTO `tool_codegen_column` VALUES (430, 36, 'send_status', 'tinyint(4)', 'å‘é€çжæ€', b'0', b'0', '0', 13, 'Integer', 'sendStatus', 'sys_sms_send_status', '1', b'0', b'0', b'1', '=', b'1', 'radio', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1341 | +INSERT INTO `tool_codegen_column` VALUES (431, 36, 'send_time', 'datetime', 'å‘逿—¶é—´', b'1', b'0', '0', 14, 'Date', 'sendTime', '', NULL, b'0', b'0', b'1', 'BETWEEN', b'1', 'datetime', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1342 | +INSERT INTO `tool_codegen_column` VALUES (432, 36, 'send_code', 'int(11)', 'å‘é€ç»“果的编ç ', b'1', b'0', '0', 15, 'Integer', 'sendCode', '', '0', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1343 | +INSERT INTO `tool_codegen_column` VALUES (433, 36, 'send_msg', 'varchar(255)', 'å‘é€ç»“果的æç¤º', b'1', b'0', '0', 16, 'String', 'sendMsg', '', 'æˆåŠŸ', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1344 | +INSERT INTO `tool_codegen_column` VALUES (434, 36, 'api_send_code', 'varchar(63)', 'çŸä¿¡ API å‘é€ç»“果的编ç ', b'1', b'0', '0', 17, 'String', 'apiSendCode', '', 'SUCCESS', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1345 | +INSERT INTO `tool_codegen_column` VALUES (435, 36, 'api_send_msg', 'varchar(255)', 'çŸä¿¡ API å‘é€å¤±è´¥çš„æç¤º', b'1', b'0', '0', 18, 'String', 'apiSendMsg', '', 'æˆåŠŸ', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1346 | +INSERT INTO `tool_codegen_column` VALUES (436, 36, 'api_request_id', 'varchar(255)', 'çŸä¿¡ API å‘é€è¿”回的唯一请求 ID', b'1', b'0', '0', 19, 'String', 'apiRequestId', '', '3837C6D3-B96F-428C-BBB2-86135D4B5B99', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1347 | +INSERT INTO `tool_codegen_column` VALUES (437, 36, 'api_serial_no', 'varchar(255)', 'çŸä¿¡ API å‘é€è¿”回的åºå·', b'1', b'0', '0', 20, 'String', 'apiSerialNo', '', '62923244790', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1348 | +INSERT INTO `tool_codegen_column` VALUES (438, 36, 'receive_status', 'tinyint(4)', '接收状æ€', b'0', b'0', '0', 21, 'Integer', 'receiveStatus', 'sys_sms_receive_status', '0', b'0', b'0', b'1', '=', b'1', 'radio', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1349 | +INSERT INTO `tool_codegen_column` VALUES (439, 36, 'receive_time', 'datetime', '接收时间', b'1', b'0', '0', 22, 'Date', 'receiveTime', '', NULL, b'0', b'0', b'1', 'BETWEEN', b'1', 'datetime', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1350 | +INSERT INTO `tool_codegen_column` VALUES (440, 36, 'api_receive_code', 'varchar(63)', 'API 接收结果的编ç ', b'1', b'0', '0', 23, 'String', 'apiReceiveCode', '', 'DELIVRD', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1351 | +INSERT INTO `tool_codegen_column` VALUES (441, 36, 'api_receive_msg', 'varchar(255)', 'API 接收结果的说明', b'1', b'0', '0', 24, 'String', 'apiReceiveMsg', '', 'DELIVRD', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1352 | +INSERT INTO `tool_codegen_column` VALUES (442, 36, 'creator', 'varchar(64)', '创建者', b'1', b'0', '0', 25, 'String', 'creator', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1353 | +INSERT INTO `tool_codegen_column` VALUES (443, 36, 'create_time', 'datetime', '创建时间', b'0', b'0', '0', 26, 'Date', 'createTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'1', 'datetime', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1354 | +INSERT INTO `tool_codegen_column` VALUES (444, 36, 'updater', 'varchar(64)', '更新者', b'1', b'0', '0', 27, 'String', 'updater', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1355 | +INSERT INTO `tool_codegen_column` VALUES (445, 36, 'update_time', 'datetime', 'æ›´æ–°æ—¶é—´', b'0', b'0', '0', 28, 'Date', 'updateTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'0', 'datetime', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1356 | +INSERT INTO `tool_codegen_column` VALUES (446, 36, 'deleted', 'bit(1)', '是å¦åˆ 除', b'0', b'0', '0', 29, 'Boolean', 'deleted', '', NULL, b'0', b'0', b'0', '=', b'0', 'radio', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0'); | |
| 1129 | 1357 | COMMIT; |
| 1130 | 1358 | |
| 1131 | 1359 | -- ---------------------------- |
| ... | ... | @@ -1151,7 +1379,7 @@ CREATE TABLE `tool_codegen_table` ( |
| 1151 | 1379 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 1152 | 1380 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 1153 | 1381 | PRIMARY KEY (`id`) USING BTREE |
| 1154 | -) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8mb4 COMMENT='代ç 生æˆè¡¨å®šä¹‰'; | |
| 1382 | +) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8mb4 COMMENT='代ç 生æˆè¡¨å®šä¹‰'; | |
| 1155 | 1383 | |
| 1156 | 1384 | -- ---------------------------- |
| 1157 | 1385 | -- Records of tool_codegen_table |
| ... | ... | @@ -1169,6 +1397,9 @@ INSERT INTO `tool_codegen_table` VALUES (28, 1, 'sys_dict_type', 'å—å…¸ç±»åž‹è¡ |
| 1169 | 1397 | 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 | 1398 | 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 | 1399 | 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'); |
| 1400 | +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'); | |
| 1401 | +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'); | |
| 1402 | +INSERT INTO `tool_codegen_table` VALUES (36, 1, 'sys_sms_log', 'çŸä¿¡æ—¥å¿—', NULL, 'system', 'sms', 'SysSmsLog', 'çŸä¿¡æ—¥å¿—', 'èŠ‹é“æºç ', 1, 1093, '1', '2021-04-11 01:12:57', '1', '2021-04-11 20:33:54', b'0'); | |
| 1172 | 1403 | COMMIT; |
| 1173 | 1404 | |
| 1174 | 1405 | -- ---------------------------- | ... | ... |
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 | 1 | package cn.iocoder.dashboard.common.exception; |
| 2 | 2 | |
| 3 | +import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; | |
| 3 | 4 | import cn.iocoder.dashboard.common.exception.enums.ServiceErrorCodeRange; |
| 4 | 5 | import lombok.Data; |
| 5 | 6 | |
| 6 | 7 | /** |
| 7 | 8 | * 错误码对象 |
| 8 | 9 | * |
| 9 | - * 全局错误码,占用 [0, 999],参见 {@link GlobalException} | |
| 10 | + * 全局错误码,占用 [0, 999], 参见 {@link GlobalErrorCodeConstants} | |
| 10 | 11 | * 业务异常错误码,占用 [1 000 000 000, +∞),参见 {@link ServiceErrorCodeRange} |
| 11 | 12 | * |
| 12 | 13 | * TODO 错误码设计成对象的原因,为未来的 i18 国际化做准备 |
| ... | ... | @@ -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 | 27 | public ErrorCode(Integer code, String message) { |
| 27 | 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 | 30 | |
| 31 | 31 | public ServiceException(ErrorCode errorCode) { |
| 32 | 32 | this.code = errorCode.getCode(); |
| 33 | - this.message = errorCode.getMessage(); | |
| 33 | + this.message = errorCode.getMsg(); | |
| 34 | 34 | } |
| 35 | 35 | |
| 36 | 36 | public ServiceException(Integer code, String message) { | ... | ... |
src/main/java/cn/iocoder/dashboard/common/exception/enums/GlobalErrorCodeConstants.java
| ... | ... | @@ -32,6 +32,7 @@ public interface GlobalErrorCodeConstants { |
| 32 | 32 | |
| 33 | 33 | // ========== 自定义错误段 ========== |
| 34 | 34 | ErrorCode REPEATED_REQUESTS = new ErrorCode(900, "重复请求,请稍后重试"); // 重复请求 |
| 35 | + ErrorCode DEMO_DENY = new ErrorCode(901, "演示模式,禁止写操作"); | |
| 35 | 36 | |
| 36 | 37 | ErrorCode UNKNOWN = new ErrorCode(999, "未知错误"); |
| 37 | 38 | ... | ... |
src/main/java/cn/iocoder/dashboard/common/exception/util/ServiceExceptionUtil.java
| ... | ... | @@ -47,12 +47,12 @@ public class ServiceExceptionUtil { |
| 47 | 47 | // ========== 和 ServiceException 的集成 ========== |
| 48 | 48 | |
| 49 | 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 | 51 | return exception0(errorCode.getCode(), messagePattern); |
| 52 | 52 | } |
| 53 | 53 | |
| 54 | 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 | 56 | return exception0(errorCode.getCode(), messagePattern, params); |
| 57 | 57 | } |
| 58 | 58 | ... | ... |
src/main/java/cn/iocoder/dashboard/common/pojo/CommonResult.java
| 1 | 1 | package cn.iocoder.dashboard.common.pojo; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.common.exception.ErrorCode; |
| 4 | -import cn.iocoder.dashboard.common.exception.GlobalException; | |
| 5 | 4 | import cn.iocoder.dashboard.common.exception.ServiceException; |
| 6 | 5 | import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; |
| 7 | 6 | import com.fasterxml.jackson.annotation.JsonIgnore; |
| ... | ... | @@ -9,6 +8,7 @@ import lombok.Data; |
| 9 | 8 | import org.springframework.util.Assert; |
| 10 | 9 | |
| 11 | 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 | 16 | * @param <T> 数据泛型 |
| 17 | 17 | */ |
| 18 | 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<T> implements Serializable { |
| 31 | 31 | /** |
| 32 | 32 | * 错误提示,用户可阅读 |
| 33 | 33 | * |
| 34 | - * @see ErrorCode#getMessage() () | |
| 34 | + * @see ErrorCode#getMsg() () | |
| 35 | 35 | */ |
| 36 | 36 | private String msg; |
| 37 | 37 | |
| ... | ... | @@ -57,7 +57,7 @@ public final class CommonResult<T> implements Serializable { |
| 57 | 57 | } |
| 58 | 58 | |
| 59 | 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 | 63 | public static <T> CommonResult<T> success(T data) { |
| ... | ... | @@ -68,9 +68,13 @@ public final class CommonResult<T> implements Serializable { |
| 68 | 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 | 75 | @JsonIgnore // 避免 jackson 序列化 |
| 72 | 76 | public boolean isSuccess() { |
| 73 | - return GlobalErrorCodeConstants.SUCCESS.getCode().equals(code); | |
| 77 | + return isSuccess(code); | |
| 74 | 78 | } |
| 75 | 79 | |
| 76 | 80 | @JsonIgnore // 避免 jackson 序列化 |
| ... | ... | @@ -81,16 +85,12 @@ public final class CommonResult<T> implements Serializable { |
| 81 | 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 | 91 | if (isSuccess()) { |
| 88 | 92 | return; |
| 89 | 93 | } |
| 90 | - // 全局异常 | |
| 91 | - if (GlobalErrorCodeConstants.isMatch(code)) { | |
| 92 | - throw new GlobalException(code, msg); | |
| 93 | - } | |
| 94 | 94 | // 业务异常 |
| 95 | 95 | throw new ServiceException(code, msg); |
| 96 | 96 | } |
| ... | ... | @@ -99,8 +99,4 @@ public final class CommonResult<T> implements Serializable { |
| 99 | 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 | 13 | import com.alibaba.excel.metadata.property.ExcelContentProperty; |
| 14 | 14 | import lombok.extern.slf4j.Slf4j; |
| 15 | 15 | |
| 16 | +/** | |
| 17 | + * Excel {@link SysDictDataDO} 数据字典转换器 | |
| 18 | + * | |
| 19 | + * @author 芋道源码 | |
| 20 | + */ | |
| 16 | 21 | @Slf4j |
| 17 | 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 | 48 | * 创建 Redis Pub/Sub 广播消费的容器 |
| 49 | 49 | */ |
| 50 | 50 | @Bean |
| 51 | - public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory factory, | |
| 52 | - List<AbstractChannelMessageListener<?>> listeners) { | |
| 51 | + public RedisMessageListenerContainer redisMessageListenerContainer( | |
| 52 | + RedisConnectionFactory factory, List<AbstractChannelMessageListener<?>> listeners) { | |
| 53 | 53 | // 创建 RedisMessageListenerContainer 对象 |
| 54 | 54 | RedisMessageListenerContainer container = new RedisMessageListenerContainer(); |
| 55 | 55 | // 设置 RedisConnection 工厂。 |
| ... | ... | @@ -69,8 +69,8 @@ public class RedisConfig { |
| 69 | 69 | * Redis Stream 的 xreadgroup 命令:https://www.geek-book.com/src/docs/redis/redis/redis.io/commands/xreadgroup.html |
| 70 | 70 | */ |
| 71 | 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 | 74 | // 第一步,创建 StreamMessageListenerContainer 容器 |
| 75 | 75 | // 创建 options 配置 |
| 76 | 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 | 128 | // 设置每个请求的权限 |
| 129 | 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 | 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 | 138 | // Swagger 接口文档 |
| 139 | 139 | .antMatchers("/swagger-ui.html").anonymous() |
| 140 | 140 | .antMatchers("/swagger-resources/**").anonymous() |
| ... | ... | @@ -148,13 +148,19 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { |
| 148 | 148 | .antMatchers("/actuator/**").anonymous() |
| 149 | 149 | // Druid 监控 |
| 150 | 150 | .antMatchers("/druid/**").anonymous() |
| 151 | + // 短信回调 API | |
| 152 | + .antMatchers(api("/system/sms/callback/**")).anonymous() | |
| 151 | 153 | // 除上面外的所有请求全部需要鉴权认证 |
| 152 | 154 | .anyRequest().authenticated() |
| 153 | 155 | .and() |
| 154 | 156 | .headers().frameOptions().disable(); |
| 155 | - httpSecurity.logout().logoutUrl(webProperties.getApiPrefix() + "/logout").logoutSuccessHandler(logoutSuccessHandler); | |
| 157 | + httpSecurity.logout().logoutUrl(api("/logout")).logoutSuccessHandler(logoutSuccessHandler); | |
| 156 | 158 | // 添加 JWT Filter |
| 157 | 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
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×tamp=%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/config/WebConfiguration.java
| ... | ... | @@ -2,7 +2,9 @@ package cn.iocoder.dashboard.framework.web.config; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.framework.web.core.enums.FilterOrderEnum; |
| 4 | 4 | import cn.iocoder.dashboard.framework.web.core.filter.CacheRequestBodyFilter; |
| 5 | +import cn.iocoder.dashboard.framework.web.core.filter.DemoFilter; | |
| 5 | 6 | import cn.iocoder.dashboard.framework.web.core.filter.XssFilter; |
| 7 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |
| 6 | 8 | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| 7 | 9 | import org.springframework.boot.web.servlet.FilterRegistrationBean; |
| 8 | 10 | import org.springframework.context.annotation.Bean; |
| ... | ... | @@ -68,6 +70,15 @@ public class WebConfiguration implements WebMvcConfigurer { |
| 68 | 70 | return createFilterBean(new XssFilter(properties, pathMatcher), FilterOrderEnum.XSS_FILTER); |
| 69 | 71 | } |
| 70 | 72 | |
| 73 | + /** | |
| 74 | + * 创建 DemoFilter Bean,演示模式 | |
| 75 | + */ | |
| 76 | + @Bean | |
| 77 | + @ConditionalOnProperty(value = "yudao.demo", havingValue = "true") | |
| 78 | + public FilterRegistrationBean<DemoFilter> demoFilter() { | |
| 79 | + return createFilterBean(new DemoFilter(), FilterOrderEnum.DEMO_FILTER); | |
| 80 | + } | |
| 81 | + | |
| 71 | 82 | private static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) { |
| 72 | 83 | FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter); |
| 73 | 84 | bean.setOrder(order); | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/web/core/enums/FilterOrderEnum.java
| ... | ... | @@ -9,6 +9,7 @@ public interface FilterOrderEnum { |
| 9 | 9 | |
| 10 | 10 | int CORS_FILTER = Integer.MIN_VALUE; |
| 11 | 11 | |
| 12 | + | |
| 12 | 13 | int REQUEST_BODY_CACHE_FILTER = Integer.MIN_VALUE + 500; |
| 13 | 14 | |
| 14 | 15 | // OrderedRequestContextFilter 默认为 -105,用于国际化上下文等等 |
| ... | ... | @@ -19,4 +20,6 @@ public interface FilterOrderEnum { |
| 19 | 20 | |
| 20 | 21 | // Spring Security Filter 默认为 -100,可见 SecurityProperties 配置属性类 |
| 21 | 22 | |
| 23 | + int DEMO_FILTER = Integer.MAX_VALUE; | |
| 24 | + | |
| 22 | 25 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/web/core/filter/DemoFilter.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.web.core.filter; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.StrUtil; | |
| 4 | +import cn.iocoder.dashboard.common.pojo.CommonResult; | |
| 5 | +import cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils; | |
| 6 | +import cn.iocoder.dashboard.util.servlet.ServletUtils; | |
| 7 | +import org.springframework.web.filter.OncePerRequestFilter; | |
| 8 | + | |
| 9 | +import javax.servlet.FilterChain; | |
| 10 | +import javax.servlet.http.HttpServletRequest; | |
| 11 | +import javax.servlet.http.HttpServletResponse; | |
| 12 | + | |
| 13 | +import static cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants.DEMO_DENY; | |
| 14 | + | |
| 15 | +/** | |
| 16 | + * 演示 Filter,禁止用户发起写操作,避免影响测试数据 | |
| 17 | + * | |
| 18 | + * @author 芋道源码 | |
| 19 | + */ | |
| 20 | +public class DemoFilter extends OncePerRequestFilter { | |
| 21 | + | |
| 22 | + @Override | |
| 23 | + protected boolean shouldNotFilter(HttpServletRequest request) { | |
| 24 | + String method = request.getMethod(); | |
| 25 | + return !StrUtil.equalsAnyIgnoreCase(method, "POST", "PUT", "DELETE") // 写操作时,不进行过滤率 | |
| 26 | + || SecurityFrameworkUtils.getLoginUser() == null; // 非登陆用户时,不进行过滤 | |
| 27 | + } | |
| 28 | + | |
| 29 | + @Override | |
| 30 | + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) { | |
| 31 | + // 直接返回 DEMO_DENY 的结果。即,请求不继续 | |
| 32 | + ServletUtils.writeJSON(response, CommonResult.error(DEMO_DENY)); | |
| 33 | + } | |
| 34 | + | |
| 35 | +} | ... | ... |
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 | 3 | import cn.hutool.core.exceptions.ExceptionUtil; |
| 4 | 4 | import cn.hutool.core.map.MapUtil; |
| 5 | 5 | import cn.hutool.extra.servlet.ServletUtil; |
| 6 | -import cn.iocoder.dashboard.common.exception.GlobalException; | |
| 7 | 6 | import cn.iocoder.dashboard.common.exception.ServiceException; |
| 8 | 7 | import cn.iocoder.dashboard.common.pojo.CommonResult; |
| 9 | 8 | import cn.iocoder.dashboard.framework.logger.apilog.core.service.ApiErrorLogFrameworkService; |
| ... | ... | @@ -96,9 +95,6 @@ public class GlobalExceptionHandler { |
| 96 | 95 | if (ex instanceof AccessDeniedException) { |
| 97 | 96 | return accessDeniedExceptionHandler(request, (AccessDeniedException) ex); |
| 98 | 97 | } |
| 99 | - if (ex instanceof GlobalException) { | |
| 100 | - return globalExceptionHandler(request, (GlobalException) ex); | |
| 101 | - } | |
| 102 | 98 | return defaultExceptionHandler(request, ex); |
| 103 | 99 | } |
| 104 | 100 | |
| ... | ... | @@ -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 | 224 | @ExceptionHandler(value = Exception.class) |
| ... | ... | @@ -250,7 +227,7 @@ public class GlobalExceptionHandler { |
| 250 | 227 | // 插入异常日志 |
| 251 | 228 | this.createExceptionLog(req, ex); |
| 252 | 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 | 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 | 61 | @GetMapping("/list-all-simple") |
| 62 | 62 | @ApiOperation(value = "获得全部字典数据列表", notes = "一般用于管理后台缓存字典数据在本地") |
| 63 | 63 | // 无需添加权限认证,因为前端全局都需要 |
| 64 | - public CommonResult<List<SysDictDataSimpleVO>> getSimpleDictDatas() { | |
| 64 | + public CommonResult<List<SysDictDataSimpleRespVO>> getSimpleDictDatas() { | |
| 65 | 65 | List<SysDictDataDO> list = dictDataService.getDictDatas(); |
| 66 | 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 | 41 | return success(dictTypeId); |
| 42 | 42 | } |
| 43 | 43 | |
| 44 | - @PostMapping("update") | |
| 44 | + @PutMapping("/update") | |
| 45 | 45 | @ApiOperation("修改字典类型") |
| 46 | 46 | @PreAuthorize("@ss.hasPermission('system:dict:update')") |
| 47 | 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 | 4 | import io.swagger.annotations.ApiModelProperty; |
| 5 | 5 | import lombok.Data; |
| 6 | 6 | |
| 7 | -@ApiModel("数据字典精简 VO") | |
| 7 | +@ApiModel("数据字典精简 Response VO") | |
| 8 | 8 | @Data |
| 9 | -public class SysDictDataSimpleVO { | |
| 9 | +public class SysDictDataSimpleRespVO { | |
| 10 | 10 | |
| 11 | 11 | @ApiModelProperty(value = "字典类型", required = true, example = "gender") |
| 12 | 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
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/controller/user/SysUserController.java
| ... | ... | @@ -149,9 +149,9 @@ public class SysUserController { |
| 149 | 149 | // 手动创建导出 demo |
| 150 | 150 | List<SysUserImportExcelVO> list = Arrays.asList( |
| 151 | 151 | SysUserImportExcelVO.builder().username("yudao").deptId(1L).email("yudao@iocoder.cn").mobile("15601691300") |
| 152 | - .nickname("芋道").status(CommonStatusEnum.ENABLE.getStatus()).sex(SysSexEnum.MALE.getSEX()).build(), | |
| 152 | + .nickname("芋道").status(CommonStatusEnum.ENABLE.getStatus()).sex(SysSexEnum.MALE.getSex()).build(), | |
| 153 | 153 | SysUserImportExcelVO.builder().username("yuanma").deptId(2L).email("yuanma@iocoder.cn").mobile("15601701300") |
| 154 | - .nickname("源码").status(CommonStatusEnum.DISABLE.getStatus()).sex(SysSexEnum.FEMALE.getSEX()).build() | |
| 154 | + .nickname("源码").status(CommonStatusEnum.DISABLE.getStatus()).sex(SysSexEnum.FEMALE.getSex()).build() | |
| 155 | 155 | ); |
| 156 | 156 | |
| 157 | 157 | // 输出 | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/controller/user/vo/profile/SysUserProfileUpdateReqVO.java
| ... | ... | @@ -3,9 +3,9 @@ package cn.iocoder.dashboard.modules.system.controller.user.vo.profile; |
| 3 | 3 | import io.swagger.annotations.ApiModel; |
| 4 | 4 | import io.swagger.annotations.ApiModelProperty; |
| 5 | 5 | import lombok.Data; |
| 6 | +import org.hibernate.validator.constraints.Length; | |
| 6 | 7 | |
| 7 | 8 | import javax.validation.constraints.Email; |
| 8 | -import javax.validation.constraints.Pattern; | |
| 9 | 9 | import javax.validation.constraints.Size; |
| 10 | 10 | |
| 11 | 11 | @ApiModel("用户个人信息更新 Request VO") |
| ... | ... | @@ -22,7 +22,7 @@ public class SysUserProfileUpdateReqVO { |
| 22 | 22 | private String email; |
| 23 | 23 | |
| 24 | 24 | @ApiModelProperty(value = "手机号码", example = "15601691300") |
| 25 | - @Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$",message = "手机号码格式错误") | |
| 25 | + @Length(min = 11, max = 11, message = "手机号长度必须 11 位") | |
| 26 | 26 | private String mobile; |
| 27 | 27 | |
| 28 | 28 | @ApiModelProperty(value = "用户性别", example = "1", notes = "参见 SysSexEnum 枚举类") | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/controller/user/vo/user/SysUserBaseVO.java
| ... | ... | @@ -2,10 +2,10 @@ package cn.iocoder.dashboard.modules.system.controller.user.vo.user; |
| 2 | 2 | |
| 3 | 3 | import io.swagger.annotations.ApiModelProperty; |
| 4 | 4 | import lombok.Data; |
| 5 | +import org.hibernate.validator.constraints.Length; | |
| 5 | 6 | |
| 6 | 7 | import javax.validation.constraints.Email; |
| 7 | 8 | import javax.validation.constraints.NotBlank; |
| 8 | -import javax.validation.constraints.Pattern; | |
| 9 | 9 | import javax.validation.constraints.Size; |
| 10 | 10 | import java.util.Set; |
| 11 | 11 | |
| ... | ... | @@ -40,7 +40,7 @@ public class SysUserBaseVO { |
| 40 | 40 | private String email; |
| 41 | 41 | |
| 42 | 42 | @ApiModelProperty(value = "手机号码", example = "15601691300") |
| 43 | - @Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$",message = "手机号码格式错误") | |
| 43 | + @Length(min = 11, max = 11, message = "手机号长度必须 11 位") | |
| 44 | 44 | private String mobile; |
| 45 | 45 | |
| 46 | 46 | @ApiModelProperty(value = "用户性别", example = "1", notes = "参见 SysSexEnum 枚举类") | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/convert/dict/SysDictDataConvert.java
| ... | ... | @@ -13,7 +13,7 @@ public interface SysDictDataConvert { |
| 13 | 13 | |
| 14 | 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 | 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
| ... | ... | @@ -78,4 +78,18 @@ public interface SysErrorCodeConstants { |
| 78 | 78 | ErrorCode FILE_UPLOAD_FAILED = new ErrorCode(1002009002, "文件上传失败"); |
| 79 | 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/common/SysSexEnum.java
| ... | ... | @@ -3,6 +3,11 @@ package cn.iocoder.dashboard.modules.system.enums.common; |
| 3 | 3 | import lombok.AllArgsConstructor; |
| 4 | 4 | import lombok.Getter; |
| 5 | 5 | |
| 6 | +/** | |
| 7 | + * 性别的枚举值 | |
| 8 | + * | |
| 9 | + * @author 芋道源码 | |
| 10 | + */ | |
| 6 | 11 | @Getter |
| 7 | 12 | @AllArgsConstructor |
| 8 | 13 | public enum SysSexEnum { |
| ... | ... | @@ -10,6 +15,9 @@ public enum SysSexEnum { |
| 10 | 15 | MALE(1), // 男 |
| 11 | 16 | FEMALE(2); // 女 |
| 12 | 17 | |
| 13 | - private final Integer SEX; | |
| 18 | + /** | |
| 19 | + * 性别 | |
| 20 | + */ | |
| 21 | + private final Integer sex; | |
| 14 | 22 | |
| 15 | 23 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/enums/dict/SysDictTypeEnum.java
| ... | ... | @@ -18,6 +18,10 @@ public enum SysDictTypeEnum { |
| 18 | 18 | SYS_LOGIN_RESULT("sys_login_result"), // 登陆结果 |
| 19 | 19 | SYS_CONFIG_TYPE("sys_config_type"), // 参数配置类型 |
| 20 | 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 | 26 | INF_REDIS_TIMEOUT_TYPE("inf_redis_timeout_type"), // Redis 超时类型 |
| 23 | 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 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.framework.redis.core.stream.AbstractStreamMessageListener; |
| 4 | 4 | import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage; |
| 5 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService; | |
| 5 | 6 | import lombok.extern.slf4j.Slf4j; |
| 6 | 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 | 17 | @Component |
| 9 | 18 | @Slf4j |
| 10 | 19 | public class SysSmsSendConsumer extends AbstractStreamMessageListener<SysSmsSendMessage> { |
| 11 | 20 | |
| 21 | + @Resource | |
| 22 | + private SysSmsService smsService; | |
| 23 | + | |
| 12 | 24 | @Override |
| 13 | 25 | public void onMessage(SysSmsSendMessage message) { |
| 14 | 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 | 1 | package cn.iocoder.dashboard.modules.system.mq.message.sms; |
| 2 | 2 | |
| 3 | +import cn.iocoder.dashboard.common.core.KeyValue; | |
| 3 | 4 | import cn.iocoder.dashboard.framework.redis.core.stream.StreamMessage; |
| 4 | 5 | import lombok.Data; |
| 5 | 6 | |
| 6 | 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 | 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 | 26 | @NotNull(message = "手机号不能为空") |
| 21 | 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 | 43 | @Override |
| 42 | 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
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
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 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.collection.CollectionUtil; |
| 5 | 5 | import cn.hutool.core.util.ArrayUtil; |
| 6 | -import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; | |
| 7 | 6 | import cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils; |
| 8 | 7 | import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysRoleMenuMapper; |
| 9 | 8 | import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysUserRoleMapper; | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysRoleServiceImpl.java
| ... | ... | @@ -59,7 +59,7 @@ public class SysRoleServiceImpl implements SysRoleService { |
| 59 | 59 | */ |
| 60 | 60 | private volatile Map<Long, SysRoleDO> roleCache; |
| 61 | 61 | /** |
| 62 | - * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新 | |
| 62 | + * 缓存角色的最大更新时间,用于后续的增量轮询,判断是否有更新 | |
| 63 | 63 | */ |
| 64 | 64 | private volatile Date maxUpdateTime; |
| 65 | 65 | |
| ... | ... | @@ -78,7 +78,7 @@ public class SysRoleServiceImpl implements SysRoleService { |
| 78 | 78 | @Override |
| 79 | 79 | @PostConstruct |
| 80 | 80 | public void initLocalCache() { |
| 81 | - // 获取菜单列表,如果有更新 | |
| 81 | + // 获取角色列表,如果有更新 | |
| 82 | 82 | List<SysRoleDO> roleList = this.loadRoleIfUpdate(maxUpdateTime); |
| 83 | 83 | if (CollUtil.isEmpty(roleList)) { |
| 84 | 84 | return; |
| ... | ... | @@ -99,23 +99,23 @@ public class SysRoleServiceImpl implements SysRoleService { |
| 99 | 99 | } |
| 100 | 100 | |
| 101 | 101 | /** |
| 102 | - * 如果菜单发生变化,从数据库中获取最新的全量菜单。 | |
| 102 | + * 如果角色发生变化,从数据库中获取最新的全量角色。 | |
| 103 | 103 | * 如果未发生变化,则返回空 |
| 104 | 104 | * |
| 105 | - * @param maxUpdateTime 当前菜单的最大更新时间 | |
| 106 | - * @return 菜单列表 | |
| 105 | + * @param maxUpdateTime 当前角色的最大更新时间 | |
| 106 | + * @return 角色列表 | |
| 107 | 107 | */ |
| 108 | 108 | private List<SysRoleDO> loadRoleIfUpdate(Date maxUpdateTime) { |
| 109 | 109 | // 第一步,判断是否要更新。 |
| 110 | 110 | if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据 |
| 111 | - log.info("[loadRoleIfUpdate][首次加载全量菜单]"); | |
| 112 | - } else { // 判断数据库中是否有更新的菜单 | |
| 111 | + log.info("[loadRoleIfUpdate][首次加载全量角色]"); | |
| 112 | + } else { // 判断数据库中是否有更新的角色 | |
| 113 | 113 | if (!roleMapper.selectExistsByUpdateTimeAfter(maxUpdateTime)) { |
| 114 | 114 | return null; |
| 115 | 115 | } |
| 116 | - log.info("[loadRoleIfUpdate][增量加载全量菜单]"); | |
| 116 | + log.info("[loadRoleIfUpdate][增量加载全量角色]"); | |
| 117 | 117 | } |
| 118 | - // 第二步,如果有更新,则从数据库加载所有菜单 | |
| 118 | + // 第二步,如果有更新,则从数据库加载所有角色 | |
| 119 | 119 | return roleMapper.selectList(); |
| 120 | 120 | } |
| 121 | 121 | ... | ... |
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 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsService.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.sms; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage; | |
| 4 | + | |
| 5 | +import java.util.List; | |
| 6 | +import java.util.Map; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * 短信Service接口 | |
| 10 | + * 只支持异步,因此没有返回值 | |
| 11 | + * | |
| 12 | + * @author zzf | |
| 13 | + * @date 2021/1/25 9:24 | |
| 14 | + */ | |
| 15 | +public interface SysSmsService { | |
| 16 | + | |
| 17 | + /** | |
| 18 | + * 发送单条短信给用户(管理员) | |
| 19 | + * | |
| 20 | + * 在 mobile 为空时,使用 userId 加载对应管理员的手机号 | |
| 21 | + * | |
| 22 | + * @param mobile 手机号 | |
| 23 | + * @param userId 用户编号 | |
| 24 | + * @param templateCode 短信模板编号 | |
| 25 | + * @param templateParams 短信模板参数 | |
| 26 | + * @return 发送日志编号 | |
| 27 | + */ | |
| 28 | + Long sendSingleSmsToAdmin(String mobile, Long userId, | |
| 29 | + String templateCode, Map<String, Object> templateParams); | |
| 30 | + | |
| 31 | + /** | |
| 32 | + * 发送单条短信给用户(会员) | |
| 33 | + * | |
| 34 | + * 在 mobile 为空时,使用 userId 加载对应会员的手机号 | |
| 35 | + * | |
| 36 | + * @param mobile 手机号 | |
| 37 | + * @param userId 用户编号 | |
| 38 | + * @param templateCode 短信模板编号 | |
| 39 | + * @param templateParams 短信模板参数 | |
| 40 | + * @return 发送日志编号 | |
| 41 | + */ | |
| 42 | + Long sendSingleSmsToMember(String mobile, Long userId, | |
| 43 | + String templateCode, Map<String, Object> templateParams); | |
| 44 | + | |
| 45 | + Long sendSingleSms(String mobile, Long userId, Integer userType, | |
| 46 | + String templateCode, Map<String, Object> templateParams); | |
| 47 | + | |
| 48 | + void sendBatchSms(List<String> mobiles, List<Long> userIds, Integer userType, | |
| 49 | + String templateCode, Map<String, Object> templateParams); | |
| 50 | + | |
| 51 | + void doSendSms(SysSmsSendMessage message); | |
| 52 | + | |
| 53 | + /** | |
| 54 | + * 接收短信的接收结果 | |
| 55 | + * | |
| 56 | + * @param channelCode 渠道编码 | |
| 57 | + * @param text 结果内容 | |
| 58 | + * @throws Throwable 处理失败时,抛出异常 | |
| 59 | + */ | |
| 60 | + void receiveSmsStatus(String channelCode, String text) throws Throwable; | |
| 61 | + | |
| 62 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsTemplateService.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.template.SysSmsTemplateCreateReqVO; | |
| 5 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplateExportReqVO; | |
| 6 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplatePageReqVO; | |
| 7 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplateUpdateReqVO; | |
| 8 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; | |
| 9 | + | |
| 10 | +import javax.validation.Valid; | |
| 11 | +import java.util.Collection; | |
| 12 | +import java.util.List; | |
| 13 | +import java.util.Map; | |
| 14 | + | |
| 15 | +/** | |
| 16 | + * 短信模板 Service 接口 | |
| 17 | + * | |
| 18 | + * @author zzf | |
| 19 | + * @date 2021/1/25 9:24 | |
| 20 | + */ | |
| 21 | +public interface SysSmsTemplateService { | |
| 22 | + | |
| 23 | + /** | |
| 24 | + * 初始化短信模板的本地缓存 | |
| 25 | + */ | |
| 26 | + void initLocalCache(); | |
| 27 | + | |
| 28 | + /** | |
| 29 | + * 获得短信模板 | |
| 30 | + * | |
| 31 | + * @param code 模板编码 | |
| 32 | + * @return 短信模板 | |
| 33 | + */ | |
| 34 | + SysSmsTemplateDO getSmsTemplateByCode(String code); | |
| 35 | + | |
| 36 | + /** | |
| 37 | + * 获得短信模板,从缓存中 | |
| 38 | + * | |
| 39 | + * @param code 模板编码 | |
| 40 | + * @return 短信模板 | |
| 41 | + */ | |
| 42 | + SysSmsTemplateDO getSmsTemplateByCodeFromCache(String code); | |
| 43 | + | |
| 44 | + /** | |
| 45 | + * 格式化短信内容 | |
| 46 | + * | |
| 47 | + * @param content 短信模板的内容 | |
| 48 | + * @param params 内容的参数 | |
| 49 | + * @return 格式化后的内容 | |
| 50 | + */ | |
| 51 | + String formatSmsTemplateContent(String content, Map<String, Object> params); | |
| 52 | + | |
| 53 | + /** | |
| 54 | + * 创建短信模板 | |
| 55 | + * | |
| 56 | + * @param createReqVO 创建信息 | |
| 57 | + * @return 编号 | |
| 58 | + */ | |
| 59 | + Long createSmsTemplate(@Valid SysSmsTemplateCreateReqVO createReqVO); | |
| 60 | + | |
| 61 | + /** | |
| 62 | + * 更新短信模板 | |
| 63 | + * | |
| 64 | + * @param updateReqVO 更新信息 | |
| 65 | + */ | |
| 66 | + void updateSmsTemplate(@Valid SysSmsTemplateUpdateReqVO updateReqVO); | |
| 67 | + | |
| 68 | + /** | |
| 69 | + * 删除短信模板 | |
| 70 | + * | |
| 71 | + * @param id 编号 | |
| 72 | + */ | |
| 73 | + void deleteSmsTemplate(Long id); | |
| 74 | + | |
| 75 | + /** | |
| 76 | + * 获得短信模板 | |
| 77 | + * | |
| 78 | + * @param id 编号 | |
| 79 | + * @return 短信模板 | |
| 80 | + */ | |
| 81 | + SysSmsTemplateDO getSmsTemplate(Long id); | |
| 82 | + | |
| 83 | + /** | |
| 84 | + * 获得短信模板列表 | |
| 85 | + * | |
| 86 | + * @param ids 编号 | |
| 87 | + * @return 短信模板列表 | |
| 88 | + */ | |
| 89 | + List<SysSmsTemplateDO> getSmsTemplateList(Collection<Long> ids); | |
| 90 | + | |
| 91 | + /** | |
| 92 | + * 获得短信模板分页 | |
| 93 | + * | |
| 94 | + * @param pageReqVO 分页查询 | |
| 95 | + * @return 短信模板分页 | |
| 96 | + */ | |
| 97 | + PageResult<SysSmsTemplateDO> getSmsTemplatePage(SysSmsTemplatePageReqVO pageReqVO); | |
| 98 | + | |
| 99 | + /** | |
| 100 | + * 获得短信模板列表, 用于 Excel 导出 | |
| 101 | + * | |
| 102 | + * @param exportReqVO 查询条件 | |
| 103 | + * @return 短信模板分页 | |
| 104 | + */ | |
| 105 | + List<SysSmsTemplateDO> getSmsTemplateList(SysSmsTemplateExportReqVO exportReqVO); | |
| 106 | + | |
| 107 | + /** | |
| 108 | + * 获得指定短信渠道下的短信模板数量 | |
| 109 | + * | |
| 110 | + * @param channelId 短信渠道编号 | |
| 111 | + * @return 数量 | |
| 112 | + */ | |
| 113 | + Integer countByChannelId(Long channelId); | |
| 114 | + | |
| 115 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsChannelServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.sms.impl; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.iocoder.dashboard.common.pojo.PageResult; | |
| 5 | +import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; | |
| 6 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory; | |
| 7 | +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; | |
| 8 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelCreateReqVO; | |
| 9 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelPageReqVO; | |
| 10 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelUpdateReqVO; | |
| 11 | +import cn.iocoder.dashboard.modules.system.convert.sms.SysSmsChannelConvert; | |
| 12 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO; | |
| 13 | +import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsChannelMapper; | |
| 14 | +import cn.iocoder.dashboard.modules.system.mq.producer.sms.SysSmsProducer; | |
| 15 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService; | |
| 16 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsTemplateService; | |
| 17 | +import lombok.extern.slf4j.Slf4j; | |
| 18 | +import org.springframework.scheduling.annotation.Scheduled; | |
| 19 | +import org.springframework.stereotype.Service; | |
| 20 | + | |
| 21 | +import javax.annotation.PostConstruct; | |
| 22 | +import javax.annotation.Resource; | |
| 23 | +import java.util.Collection; | |
| 24 | +import java.util.Comparator; | |
| 25 | +import java.util.Date; | |
| 26 | +import java.util.List; | |
| 27 | + | |
| 28 | +import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception; | |
| 29 | +import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN; | |
| 30 | +import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS; | |
| 31 | + | |
| 32 | +/** | |
| 33 | + * 短信渠道Service实现类 | |
| 34 | + * | |
| 35 | + * @author zzf | |
| 36 | + * @date 2021/1/25 9:25 | |
| 37 | + */ | |
| 38 | +@Service | |
| 39 | +@Slf4j | |
| 40 | +public class SysSmsChannelServiceImpl implements SysSmsChannelService { | |
| 41 | + | |
| 42 | + /** | |
| 43 | + * 定时执行 {@link #schedulePeriodicRefresh()} 的周期 | |
| 44 | + * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高 | |
| 45 | + */ | |
| 46 | + private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L; | |
| 47 | + | |
| 48 | + /** | |
| 49 | + * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新 | |
| 50 | + */ | |
| 51 | + private volatile Date maxUpdateTime; | |
| 52 | + | |
| 53 | + @Resource | |
| 54 | + private SmsClientFactory smsClientFactory; | |
| 55 | + | |
| 56 | + @Resource | |
| 57 | + private SysSmsChannelMapper smsChannelMapper; | |
| 58 | + | |
| 59 | + @Resource | |
| 60 | + private SysSmsTemplateService smsTemplateService; | |
| 61 | + | |
| 62 | + @Resource | |
| 63 | + private SysSmsProducer smsProducer; | |
| 64 | + | |
| 65 | + @Override | |
| 66 | + @PostConstruct | |
| 67 | + public void initSmsClients() { | |
| 68 | + // 获取短信渠道,如果有更新 | |
| 69 | + List<SysSmsChannelDO> smsChannels = this.loadSmsChannelIfUpdate(maxUpdateTime); | |
| 70 | + if (CollUtil.isEmpty(smsChannels)) { | |
| 71 | + return; | |
| 72 | + } | |
| 73 | + | |
| 74 | + // 创建或更新短信 Client | |
| 75 | + List<SmsChannelProperties> propertiesList = SysSmsChannelConvert.INSTANCE.convertList02(smsChannels); | |
| 76 | + propertiesList.forEach(properties -> smsClientFactory.createOrUpdateSmsClient(properties)); | |
| 77 | + | |
| 78 | + // 写入缓存 | |
| 79 | + assert smsChannels.size() > 0; // 断言,避免告警 | |
| 80 | + maxUpdateTime = smsChannels.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime(); | |
| 81 | + log.info("[initSmsClients][初始化 SmsChannel 数量为 {}]", smsChannels.size()); | |
| 82 | + } | |
| 83 | + | |
| 84 | + @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD) | |
| 85 | + public void schedulePeriodicRefresh() { | |
| 86 | + initSmsClients(); | |
| 87 | + } | |
| 88 | + | |
| 89 | + /** | |
| 90 | + * 如果短信渠道发生变化,从数据库中获取最新的全量短信渠道。 | |
| 91 | + * 如果未发生变化,则返回空 | |
| 92 | + * | |
| 93 | + * @param maxUpdateTime 当前短信渠道的最大更新时间 | |
| 94 | + * @return 短信渠道列表 | |
| 95 | + */ | |
| 96 | + private List<SysSmsChannelDO> loadSmsChannelIfUpdate(Date maxUpdateTime) { | |
| 97 | + // 第一步,判断是否要更新。 | |
| 98 | + if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据 | |
| 99 | + log.info("[loadSmsChannelIfUpdate][首次加载全量短信渠道]"); | |
| 100 | + } else { // 判断数据库中是否有更新的短信渠道 | |
| 101 | + if (smsChannelMapper.selectExistsByUpdateTimeAfter(maxUpdateTime) == null) { | |
| 102 | + return null; | |
| 103 | + } | |
| 104 | + log.info("[loadSmsChannelIfUpdate][增量加载全量短信渠道]"); | |
| 105 | + } | |
| 106 | + // 第二步,如果有更新,则从数据库加载所有短信渠道 | |
| 107 | + return smsChannelMapper.selectList(); | |
| 108 | + } | |
| 109 | + | |
| 110 | + @Override | |
| 111 | + public Long createSmsChannel(SysSmsChannelCreateReqVO createReqVO) { | |
| 112 | + // 插入 | |
| 113 | + SysSmsChannelDO smsChannel = SysSmsChannelConvert.INSTANCE.convert(createReqVO); | |
| 114 | + smsChannelMapper.insert(smsChannel); | |
| 115 | + // 发送刷新消息 | |
| 116 | + smsProducer.sendSmsChannelRefreshMessage(); | |
| 117 | + // 返回 | |
| 118 | + return smsChannel.getId(); | |
| 119 | + } | |
| 120 | + | |
| 121 | + @Override | |
| 122 | + public void updateSmsChannel(SysSmsChannelUpdateReqVO updateReqVO) { | |
| 123 | + // 校验存在 | |
| 124 | + this.validateSmsChannelExists(updateReqVO.getId()); | |
| 125 | + // 更新 | |
| 126 | + SysSmsChannelDO updateObj = SysSmsChannelConvert.INSTANCE.convert(updateReqVO); | |
| 127 | + smsChannelMapper.updateById(updateObj); | |
| 128 | + // 发送刷新消息 | |
| 129 | + smsProducer.sendSmsChannelRefreshMessage(); | |
| 130 | + } | |
| 131 | + | |
| 132 | + @Override | |
| 133 | + public void deleteSmsChannel(Long id) { | |
| 134 | + // 校验存在 | |
| 135 | + this.validateSmsChannelExists(id); | |
| 136 | + // 校验是否有字典数据 | |
| 137 | + if (smsTemplateService.countByChannelId(id) > 0) { | |
| 138 | + throw exception(SMS_CHANNEL_HAS_CHILDREN); | |
| 139 | + } | |
| 140 | + // 删除 | |
| 141 | + smsChannelMapper.deleteById(id); | |
| 142 | + // 发送刷新消息 | |
| 143 | + smsProducer.sendSmsChannelRefreshMessage(); | |
| 144 | + } | |
| 145 | + | |
| 146 | + private void validateSmsChannelExists(Long id) { | |
| 147 | + if (smsChannelMapper.selectById(id) == null) { | |
| 148 | + throw exception(SMS_CHANNEL_NOT_EXISTS); | |
| 149 | + } | |
| 150 | + } | |
| 151 | + | |
| 152 | + @Override | |
| 153 | + public SysSmsChannelDO getSmsChannel(Long id) { | |
| 154 | + return smsChannelMapper.selectById(id); | |
| 155 | + } | |
| 156 | + | |
| 157 | + @Override | |
| 158 | + public List<SysSmsChannelDO> getSmsChannelList(Collection<Long> ids) { | |
| 159 | + return smsChannelMapper.selectBatchIds(ids); | |
| 160 | + } | |
| 161 | + | |
| 162 | + @Override | |
| 163 | + public List<SysSmsChannelDO> getSmsChannelList() { | |
| 164 | + return smsChannelMapper.selectList(); | |
| 165 | + } | |
| 166 | + | |
| 167 | + @Override | |
| 168 | + public PageResult<SysSmsChannelDO> getSmsChannelPage(SysSmsChannelPageReqVO pageReqVO) { | |
| 169 | + return smsChannelMapper.selectPage(pageReqVO); | |
| 170 | + } | |
| 171 | + | |
| 172 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsLogServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.sms.impl; | |
| 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.log.SysSmsLogExportReqVO; | |
| 6 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogPageReqVO; | |
| 7 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsLogDO; | |
| 8 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; | |
| 9 | +import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsLogMapper; | |
| 10 | +import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsReceiveStatusEnum; | |
| 11 | +import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum; | |
| 12 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsLogService; | |
| 13 | +import lombok.extern.slf4j.Slf4j; | |
| 14 | +import org.springframework.stereotype.Service; | |
| 15 | + | |
| 16 | +import javax.annotation.Resource; | |
| 17 | +import java.util.Date; | |
| 18 | +import java.util.List; | |
| 19 | +import java.util.Map; | |
| 20 | +import java.util.Objects; | |
| 21 | + | |
| 22 | +/** | |
| 23 | + * 短信日志 Service 实现类 | |
| 24 | + * | |
| 25 | + * @author zzf | |
| 26 | + * @date 2021/1/25 9:25 | |
| 27 | + */ | |
| 28 | +@Slf4j | |
| 29 | +@Service | |
| 30 | +public class SysSmsLogServiceImpl implements SysSmsLogService { | |
| 31 | + | |
| 32 | + @Resource | |
| 33 | + private SysSmsLogMapper smsLogMapper; | |
| 34 | + | |
| 35 | + @Override | |
| 36 | + public Long createSmsLog(String mobile, Long userId, Integer userType, Boolean isSend, | |
| 37 | + SysSmsTemplateDO template, String templateContent, Map<String, Object> templateParams) { | |
| 38 | + SysSmsLogDO.SysSmsLogDOBuilder logBuilder = SysSmsLogDO.builder(); | |
| 39 | + // 根据是否要发送,设置状态 | |
| 40 | + logBuilder.sendStatus(Objects.equals(isSend, true) ? SysSmsSendStatusEnum.INIT.getStatus() | |
| 41 | + : SysSmsSendStatusEnum.IGNORE.getStatus()); | |
| 42 | + // 设置手机相关字段 | |
| 43 | + logBuilder.mobile(mobile).userId(userId).userType(userType); | |
| 44 | + // 设置模板相关字段 | |
| 45 | + logBuilder.templateId(template.getId()).templateCode(template.getCode()).templateType(template.getType()); | |
| 46 | + logBuilder.templateContent(templateContent).templateParams(templateParams).apiTemplateId(template.getApiTemplateId()); | |
| 47 | + // 设置渠道相关字段 | |
| 48 | + logBuilder.channelId(template.getChannelId()).channelCode(template.getChannelCode()); | |
| 49 | + // 设置接收相关字段 | |
| 50 | + logBuilder.receiveStatus(SysSmsReceiveStatusEnum.INIT.getStatus()); | |
| 51 | + | |
| 52 | + // 插入数据库 | |
| 53 | + SysSmsLogDO logDO = logBuilder.build(); | |
| 54 | + smsLogMapper.insert(logDO); | |
| 55 | + return logDO.getId(); | |
| 56 | + } | |
| 57 | + | |
| 58 | + @Override | |
| 59 | + public void updateSmsSendResult(Long id, Integer sendCode, String sendMsg, | |
| 60 | + String apiSendCode, String apiSendMsg, String apiRequestId, String apiSerialNo) { | |
| 61 | + SysSmsSendStatusEnum sendStatus = CommonResult.isSuccess(sendCode) ? SysSmsSendStatusEnum.SUCCESS | |
| 62 | + : SysSmsSendStatusEnum.FAILURE; | |
| 63 | + smsLogMapper.updateById(SysSmsLogDO.builder().id(id).sendStatus(sendStatus.getStatus()).sendTime(new Date()) | |
| 64 | + .sendCode(sendCode).sendMsg(sendMsg).apiSendCode(apiSendCode).apiSendMsg(apiSendMsg) | |
| 65 | + .apiRequestId(apiRequestId).apiSerialNo(apiSerialNo).build()); | |
| 66 | + } | |
| 67 | + | |
| 68 | + @Override | |
| 69 | + public void updateSmsReceiveResult(Long id, Boolean success, Date receiveTime, String apiReceiveCode, String apiReceiveMsg) { | |
| 70 | + SysSmsReceiveStatusEnum receiveStatus = Objects.equals(success, true) ? SysSmsReceiveStatusEnum.SUCCESS | |
| 71 | + : SysSmsReceiveStatusEnum.FAILURE; | |
| 72 | + smsLogMapper.updateById(SysSmsLogDO.builder().id(id).receiveStatus(receiveStatus.getStatus()).receiveTime(receiveTime) | |
| 73 | + .apiReceiveCode(apiReceiveCode).apiReceiveMsg(apiReceiveMsg).build()); | |
| 74 | + } | |
| 75 | + | |
| 76 | + @Override | |
| 77 | + public PageResult<SysSmsLogDO> getSmsLogPage(SysSmsLogPageReqVO pageReqVO) { | |
| 78 | + return smsLogMapper.selectPage(pageReqVO); | |
| 79 | + } | |
| 80 | + | |
| 81 | + @Override | |
| 82 | + public List<SysSmsLogDO> getSmsLogList(SysSmsLogExportReqVO exportReqVO) { | |
| 83 | + return smsLogMapper.selectList(exportReqVO); | |
| 84 | + } | |
| 85 | + | |
| 86 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.sms.impl; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.util.StrUtil; | |
| 5 | +import cn.iocoder.dashboard.common.core.KeyValue; | |
| 6 | +import cn.iocoder.dashboard.common.enums.CommonStatusEnum; | |
| 7 | +import cn.iocoder.dashboard.common.enums.UserTypeEnum; | |
| 8 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsClient; | |
| 9 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory; | |
| 10 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; | |
| 11 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO; | |
| 12 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; | |
| 13 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; | |
| 14 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO; | |
| 15 | +import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage; | |
| 16 | +import cn.iocoder.dashboard.modules.system.mq.producer.sms.SysSmsProducer; | |
| 17 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsLogService; | |
| 18 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService; | |
| 19 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsTemplateService; | |
| 20 | +import cn.iocoder.dashboard.modules.system.service.user.SysUserService; | |
| 21 | +import com.google.common.annotations.VisibleForTesting; | |
| 22 | +import lombok.extern.slf4j.Slf4j; | |
| 23 | +import org.springframework.stereotype.Service; | |
| 24 | +import org.springframework.util.Assert; | |
| 25 | + | |
| 26 | +import javax.annotation.Resource; | |
| 27 | +import java.util.List; | |
| 28 | +import java.util.Map; | |
| 29 | +import java.util.stream.Collectors; | |
| 30 | + | |
| 31 | +import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception; | |
| 32 | +import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; | |
| 33 | + | |
| 34 | +/** | |
| 35 | + * 短信日志Service实现类 | |
| 36 | + * | |
| 37 | + * @author zzf | |
| 38 | + * @date 2021/1/25 9:25 | |
| 39 | + */ | |
| 40 | +@Service | |
| 41 | +@Slf4j | |
| 42 | +public class SysSmsServiceImpl implements SysSmsService { | |
| 43 | + | |
| 44 | + @Resource | |
| 45 | + private SysSmsTemplateService smsTemplateService; | |
| 46 | + @Resource | |
| 47 | + private SysSmsLogService smsLogService; | |
| 48 | + @Resource | |
| 49 | + private SysSmsProducer smsProducer; | |
| 50 | + @Resource | |
| 51 | + private SmsClientFactory smsClientFactory; | |
| 52 | + | |
| 53 | + @Resource | |
| 54 | + private SysUserService userService; | |
| 55 | + | |
| 56 | + @Override | |
| 57 | + public Long sendSingleSmsToAdmin(String mobile, Long userId, String templateCode, Map<String, Object> templateParams) { | |
| 58 | + // 如果 mobile 为空,则加载用户编号对应的手机号 | |
| 59 | + if (StrUtil.isEmpty(mobile)) { | |
| 60 | + SysUserDO user = userService.getUser(userId); | |
| 61 | + if (user != null) { | |
| 62 | + mobile = user.getMobile(); | |
| 63 | + } | |
| 64 | + } | |
| 65 | + // 执行发送 | |
| 66 | + return this.sendSingleSms(mobile, userId, UserTypeEnum.ADMIN.getValue(), templateCode, templateParams); | |
| 67 | + } | |
| 68 | + | |
| 69 | + @Override | |
| 70 | + public Long sendSingleSmsToMember(String mobile, Long userId, String templateCode, Map<String, Object> templateParams) { | |
| 71 | + throw new UnsupportedOperationException("暂时不支持该操作,感兴趣可以实现该功能哟!"); | |
| 72 | + } | |
| 73 | + | |
| 74 | + @Override | |
| 75 | + public Long sendSingleSms(String mobile, Long userId, Integer userType, | |
| 76 | + String templateCode, Map<String, Object> templateParams) { | |
| 77 | + // 校验短信模板是否合法 | |
| 78 | + SysSmsTemplateDO template = this.checkSmsTemplateValid(templateCode); | |
| 79 | + // 校验手机号码是否存在 | |
| 80 | + mobile = this.checkMobile(mobile); | |
| 81 | + // 构建有序的模板参数。为什么放在这个位置,是提前保证模板参数的正确性,而不是到了插入发送日志 | |
| 82 | + List<KeyValue<String, Object>> newTemplateParams = this.buildTemplateParams(template, templateParams); | |
| 83 | + | |
| 84 | + // 创建发送日志 | |
| 85 | + Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus()); // 如果模板被禁用,则不发送短信,只记录日志 | |
| 86 | + String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams); | |
| 87 | + Long sendLogId = smsLogService.createSmsLog(mobile, userId, userType, isSend, template, content, templateParams); | |
| 88 | + | |
| 89 | + // 发送 MQ 消息,异步执行发送短信 | |
| 90 | + if (isSend) { | |
| 91 | + smsProducer.sendSmsSendMessage(sendLogId, mobile, template.getChannelId(), template.getApiTemplateId(), newTemplateParams); | |
| 92 | + } | |
| 93 | + return sendLogId; | |
| 94 | + } | |
| 95 | + | |
| 96 | + @Override | |
| 97 | + public void sendBatchSms(List<String> mobiles, List<Long> userIds, Integer userType, | |
| 98 | + String templateCode, Map<String, Object> templateParams) { | |
| 99 | + throw new UnsupportedOperationException("暂时不支持该操作,感兴趣可以实现该功能哟!"); | |
| 100 | + } | |
| 101 | + | |
| 102 | + @VisibleForTesting | |
| 103 | + public SysSmsTemplateDO checkSmsTemplateValid(String templateCode) { | |
| 104 | + // 获得短信模板。考虑到效率,从缓存中获取 | |
| 105 | + SysSmsTemplateDO template = smsTemplateService.getSmsTemplateByCodeFromCache(templateCode); | |
| 106 | + // 短信模板不存在 | |
| 107 | + if (template == null) { | |
| 108 | + throw exception(SMS_TEMPLATE_NOT_EXISTS); | |
| 109 | + } | |
| 110 | + return template; | |
| 111 | + } | |
| 112 | + | |
| 113 | + /** | |
| 114 | + * 将参数模板,处理成有序的 KeyValue 数组 | |
| 115 | + * | |
| 116 | + * 原因是,部分短信平台并不是使用 key 作为参数,而是数组下标,例如说腾讯云 https://cloud.tencent.com/document/product/382/39023 | |
| 117 | + * | |
| 118 | + * @param template 短信模板 | |
| 119 | + * @param templateParams 原始参数 | |
| 120 | + * @return 处理后的参数 | |
| 121 | + */ | |
| 122 | + @VisibleForTesting | |
| 123 | + public List<KeyValue<String, Object>> buildTemplateParams(SysSmsTemplateDO template, Map<String, Object> templateParams) { | |
| 124 | + return template.getParams().stream().map(key -> { | |
| 125 | + Object value = templateParams.get(key); | |
| 126 | + if (value == null) { | |
| 127 | + throw exception(SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS, key); | |
| 128 | + } | |
| 129 | + return new KeyValue<>(key, value); | |
| 130 | + }).collect(Collectors.toList()); | |
| 131 | + } | |
| 132 | + | |
| 133 | + @VisibleForTesting | |
| 134 | + public String checkMobile(String mobile) { | |
| 135 | + if (StrUtil.isEmpty(mobile)) { | |
| 136 | + throw exception(SMS_SEND_MOBILE_NOT_EXISTS); | |
| 137 | + } | |
| 138 | + return mobile; | |
| 139 | + } | |
| 140 | + | |
| 141 | + @Override | |
| 142 | + public void doSendSms(SysSmsSendMessage message) { | |
| 143 | + // 获得渠道对应的 SmsClient 客户端 | |
| 144 | + SmsClient smsClient = smsClientFactory.getSmsClient(message.getChannelId()); | |
| 145 | + Assert.notNull(smsClient, String.format("短信客户端(%d) 不存在", message.getChannelId())); | |
| 146 | + // 发送短信 | |
| 147 | + SmsCommonResult<SmsSendRespDTO> sendResult = smsClient.sendSms(message.getLogId(), message.getMobile(), | |
| 148 | + message.getApiTemplateId(), message.getTemplateParams()); | |
| 149 | + smsLogService.updateSmsSendResult(message.getLogId(), sendResult.getCode(), sendResult.getMsg(), | |
| 150 | + sendResult.getApiCode(), sendResult.getApiMsg(), sendResult.getApiRequestId(), | |
| 151 | + sendResult.getData() != null ? sendResult.getData().getSerialNo() : null); | |
| 152 | + } | |
| 153 | + | |
| 154 | + @Override | |
| 155 | + public void receiveSmsStatus(String channelCode, String text) throws Throwable { | |
| 156 | + // 获得渠道对应的 SmsClient 客户端 | |
| 157 | + SmsClient smsClient = smsClientFactory.getSmsClient(channelCode); | |
| 158 | + Assert.notNull(smsClient, String.format("短信客户端(%s) 不存在", channelCode)); | |
| 159 | + // 解析内容 | |
| 160 | + List<SmsReceiveRespDTO> receiveResults = smsClient.parseSmsReceiveStatus(text); | |
| 161 | + if (CollUtil.isEmpty(receiveResults)) { | |
| 162 | + return; | |
| 163 | + } | |
| 164 | + // 更新短信日志的接收结果. 因为量一般不打,所以先使用 for 循环更新 | |
| 165 | + receiveResults.forEach(result -> { | |
| 166 | + smsLogService.updateSmsReceiveResult(result.getLogId(), result.getSuccess(), result.getReceiveTime(), | |
| 167 | + result.getErrorCode(), result.getErrorCode()); | |
| 168 | + }); | |
| 169 | + } | |
| 170 | + | |
| 171 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsTemplateServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.sms.impl; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.util.ReUtil; | |
| 5 | +import cn.hutool.core.util.StrUtil; | |
| 6 | +import cn.iocoder.dashboard.common.enums.CommonStatusEnum; | |
| 7 | +import cn.iocoder.dashboard.common.pojo.PageResult; | |
| 8 | +import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; | |
| 9 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsClient; | |
| 10 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory; | |
| 11 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; | |
| 12 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsTemplateRespDTO; | |
| 13 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplateCreateReqVO; | |
| 14 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplateExportReqVO; | |
| 15 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplatePageReqVO; | |
| 16 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplateUpdateReqVO; | |
| 17 | +import cn.iocoder.dashboard.modules.system.convert.sms.SysSmsTemplateConvert; | |
| 18 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO; | |
| 19 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; | |
| 20 | +import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsTemplateMapper; | |
| 21 | +import cn.iocoder.dashboard.modules.system.mq.producer.sms.SysSmsProducer; | |
| 22 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService; | |
| 23 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsTemplateService; | |
| 24 | +import com.google.common.annotations.VisibleForTesting; | |
| 25 | +import com.google.common.collect.ImmutableMap; | |
| 26 | +import lombok.extern.slf4j.Slf4j; | |
| 27 | +import org.springframework.scheduling.annotation.Scheduled; | |
| 28 | +import org.springframework.stereotype.Service; | |
| 29 | +import org.springframework.util.Assert; | |
| 30 | + | |
| 31 | +import javax.annotation.PostConstruct; | |
| 32 | +import javax.annotation.Resource; | |
| 33 | +import java.util.*; | |
| 34 | +import java.util.regex.Pattern; | |
| 35 | + | |
| 36 | +import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception; | |
| 37 | +import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; | |
| 38 | + | |
| 39 | +/** | |
| 40 | + * 短信模板Service实现类 | |
| 41 | + * | |
| 42 | + * @author zzf | |
| 43 | + * @date 2021/1/25 9:25 | |
| 44 | + */ | |
| 45 | +@Service | |
| 46 | +@Slf4j | |
| 47 | +public class SysSmsTemplateServiceImpl implements SysSmsTemplateService { | |
| 48 | + | |
| 49 | + /** | |
| 50 | + * 正则表达式,匹配 {} 中的变量 | |
| 51 | + */ | |
| 52 | + private static final Pattern PATTERN_PARAMS = Pattern.compile("\\{(.*?)}"); | |
| 53 | + | |
| 54 | + /** | |
| 55 | + * 定时执行 {@link #schedulePeriodicRefresh()} 的周期 | |
| 56 | + * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高 | |
| 57 | + */ | |
| 58 | + private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L; | |
| 59 | + | |
| 60 | + /** | |
| 61 | + * 短信模板缓存 | |
| 62 | + * key:短信模板编码 {@link SysSmsTemplateDO#getCode()} | |
| 63 | + * | |
| 64 | + * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 | |
| 65 | + */ | |
| 66 | + private volatile Map<String, SysSmsTemplateDO> smsTemplateCache; | |
| 67 | + /** | |
| 68 | + * 缓存短信模板的最大更新时间,用于后续的增量轮询,判断是否有更新 | |
| 69 | + */ | |
| 70 | + private volatile Date maxUpdateTime; | |
| 71 | + | |
| 72 | + @Resource | |
| 73 | + private SysSmsTemplateMapper smsTemplateMapper; | |
| 74 | + | |
| 75 | + @Resource | |
| 76 | + private SysSmsChannelService smsChannelService; | |
| 77 | + | |
| 78 | + @Resource | |
| 79 | + private SmsClientFactory smsClientFactory; | |
| 80 | + | |
| 81 | + @Resource | |
| 82 | + private SysSmsProducer smsProducer; | |
| 83 | + | |
| 84 | + /** | |
| 85 | + * 初始化 {@link #smsTemplateCache} 缓存 | |
| 86 | + */ | |
| 87 | + @Override | |
| 88 | + @PostConstruct | |
| 89 | + public void initLocalCache() { | |
| 90 | + // 获取短信模板列表,如果有更新 | |
| 91 | + List<SysSmsTemplateDO> smsTemplateList = this.loadSmsTemplateIfUpdate(maxUpdateTime); | |
| 92 | + if (CollUtil.isEmpty(smsTemplateList)) { | |
| 93 | + return; | |
| 94 | + } | |
| 95 | + | |
| 96 | + // 写入缓存 | |
| 97 | + ImmutableMap.Builder<String, SysSmsTemplateDO> builder = ImmutableMap.builder(); | |
| 98 | + smsTemplateList.forEach(sysSmsTemplateDO -> builder.put(sysSmsTemplateDO.getCode(), sysSmsTemplateDO)); | |
| 99 | + smsTemplateCache = builder.build(); | |
| 100 | + assert smsTemplateList.size() > 0; // 断言,避免告警 | |
| 101 | + maxUpdateTime = smsTemplateList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime(); | |
| 102 | + log.info("[initLocalCache][初始化 SmsTemplate 数量为 {}]", smsTemplateList.size()); | |
| 103 | + } | |
| 104 | + | |
| 105 | + @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD) | |
| 106 | + public void schedulePeriodicRefresh() { | |
| 107 | + initLocalCache(); | |
| 108 | + } | |
| 109 | + | |
| 110 | + /** | |
| 111 | + * 如果短信模板发生变化,从数据库中获取最新的全量短信模板。 | |
| 112 | + * 如果未发生变化,则返回空 | |
| 113 | + * | |
| 114 | + * @param maxUpdateTime 当前短信模板的最大更新时间 | |
| 115 | + * @return 短信模板列表 | |
| 116 | + */ | |
| 117 | + private List<SysSmsTemplateDO> loadSmsTemplateIfUpdate(Date maxUpdateTime) { | |
| 118 | + // 第一步,判断是否要更新。 | |
| 119 | + if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据 | |
| 120 | + log.info("[loadSmsTemplateIfUpdate][首次加载全量短信模板]"); | |
| 121 | + } else { // 判断数据库中是否有更新的短信模板 | |
| 122 | + if (smsTemplateMapper.selectExistsByUpdateTimeAfter(maxUpdateTime) == null) { | |
| 123 | + return null; | |
| 124 | + } | |
| 125 | + log.info("[loadSmsTemplateIfUpdate][增量加载全量短信模板]"); | |
| 126 | + } | |
| 127 | + // 第二步,如果有更新,则从数据库加载所有短信模板 | |
| 128 | + return smsTemplateMapper.selectList(); | |
| 129 | + } | |
| 130 | + | |
| 131 | + @Override | |
| 132 | + public SysSmsTemplateDO getSmsTemplateByCode(String code) { | |
| 133 | + return smsTemplateMapper.selectByCode(code); | |
| 134 | + } | |
| 135 | + | |
| 136 | + @Override | |
| 137 | + public SysSmsTemplateDO getSmsTemplateByCodeFromCache(String code) { | |
| 138 | + return smsTemplateCache.get(code); | |
| 139 | + } | |
| 140 | + | |
| 141 | + @Override | |
| 142 | + public String formatSmsTemplateContent(String content, Map<String, Object> params) { | |
| 143 | + return StrUtil.format(content, params); | |
| 144 | + } | |
| 145 | + | |
| 146 | + @VisibleForTesting | |
| 147 | + public List<String> parseTemplateContentParams(String content) { | |
| 148 | + return ReUtil.findAllGroup1(PATTERN_PARAMS, content); | |
| 149 | + } | |
| 150 | + | |
| 151 | + @Override | |
| 152 | + public Long createSmsTemplate(SysSmsTemplateCreateReqVO createReqVO) { | |
| 153 | + // 校验短信渠道 | |
| 154 | + SysSmsChannelDO channelDO = checkSmsChannel(createReqVO.getChannelId()); | |
| 155 | + // 校验短信编码是否重复 | |
| 156 | + checkSmsTemplateCodeDuplicate(null, createReqVO.getCode()); | |
| 157 | + // 校验短信模板 | |
| 158 | + checkApiTemplate(createReqVO.getChannelId(), createReqVO.getApiTemplateId()); | |
| 159 | + | |
| 160 | + // 插入 | |
| 161 | + SysSmsTemplateDO template = SysSmsTemplateConvert.INSTANCE.convert(createReqVO); | |
| 162 | + template.setParams(parseTemplateContentParams(template.getContent())); | |
| 163 | + template.setChannelCode(channelDO.getCode()); | |
| 164 | + smsTemplateMapper.insert(template); | |
| 165 | + // 发送刷新消息 | |
| 166 | + smsProducer.sendSmsTemplateRefreshMessage(); | |
| 167 | + // 返回 | |
| 168 | + return template.getId(); | |
| 169 | + } | |
| 170 | + | |
| 171 | + @Override | |
| 172 | + public void updateSmsTemplate(SysSmsTemplateUpdateReqVO updateReqVO) { | |
| 173 | + // 校验存在 | |
| 174 | + this.validateSmsTemplateExists(updateReqVO.getId()); | |
| 175 | + // 校验短信渠道 | |
| 176 | + SysSmsChannelDO channelDO = checkSmsChannel(updateReqVO.getChannelId()); | |
| 177 | + // 校验短信编码是否重复 | |
| 178 | + checkSmsTemplateCodeDuplicate(updateReqVO.getId(), updateReqVO.getCode()); | |
| 179 | + // 校验短信模板 | |
| 180 | + checkApiTemplate(updateReqVO.getChannelId(), updateReqVO.getApiTemplateId()); | |
| 181 | + | |
| 182 | + // 更新 | |
| 183 | + SysSmsTemplateDO updateObj = SysSmsTemplateConvert.INSTANCE.convert(updateReqVO); | |
| 184 | + updateObj.setParams(parseTemplateContentParams(updateObj.getContent())); | |
| 185 | + updateObj.setChannelCode(channelDO.getCode()); | |
| 186 | + smsTemplateMapper.updateById(updateObj); | |
| 187 | + // 发送刷新消息 | |
| 188 | + smsProducer.sendSmsTemplateRefreshMessage(); | |
| 189 | + } | |
| 190 | + | |
| 191 | + @Override | |
| 192 | + public void deleteSmsTemplate(Long id) { | |
| 193 | + // 校验存在 | |
| 194 | + this.validateSmsTemplateExists(id); | |
| 195 | + // 更新 | |
| 196 | + smsTemplateMapper.deleteById(id); | |
| 197 | + // 发送刷新消息 | |
| 198 | + smsProducer.sendSmsTemplateRefreshMessage(); | |
| 199 | + } | |
| 200 | + | |
| 201 | + private void validateSmsTemplateExists(Long id) { | |
| 202 | + if (smsTemplateMapper.selectById(id) == null) { | |
| 203 | + throw exception(SMS_TEMPLATE_NOT_EXISTS); | |
| 204 | + } | |
| 205 | + } | |
| 206 | + | |
| 207 | + @Override | |
| 208 | + public SysSmsTemplateDO getSmsTemplate(Long id) { | |
| 209 | + return smsTemplateMapper.selectById(id); | |
| 210 | + } | |
| 211 | + | |
| 212 | + @Override | |
| 213 | + public List<SysSmsTemplateDO> getSmsTemplateList(Collection<Long> ids) { | |
| 214 | + return smsTemplateMapper.selectBatchIds(ids); | |
| 215 | + } | |
| 216 | + | |
| 217 | + @Override | |
| 218 | + public PageResult<SysSmsTemplateDO> getSmsTemplatePage(SysSmsTemplatePageReqVO pageReqVO) { | |
| 219 | + return smsTemplateMapper.selectPage(pageReqVO); | |
| 220 | + } | |
| 221 | + | |
| 222 | + @Override | |
| 223 | + public List<SysSmsTemplateDO> getSmsTemplateList(SysSmsTemplateExportReqVO exportReqVO) { | |
| 224 | + return smsTemplateMapper.selectList(exportReqVO); | |
| 225 | + } | |
| 226 | + | |
| 227 | + @Override | |
| 228 | + public Integer countByChannelId(Long channelId) { | |
| 229 | + return smsTemplateMapper.selectCountByChannelId(channelId); | |
| 230 | + } | |
| 231 | + | |
| 232 | + @VisibleForTesting | |
| 233 | + public SysSmsChannelDO checkSmsChannel(Long channelId) { | |
| 234 | + SysSmsChannelDO channelDO = smsChannelService.getSmsChannel(channelId); | |
| 235 | + if (channelDO == null) { | |
| 236 | + throw exception(SMS_CHANNEL_NOT_EXISTS); | |
| 237 | + } | |
| 238 | + if (!Objects.equals(channelDO.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { | |
| 239 | + throw exception(SMS_CHANNEL_DISABLE); | |
| 240 | + } | |
| 241 | + return channelDO; | |
| 242 | + } | |
| 243 | + | |
| 244 | + @VisibleForTesting | |
| 245 | + public void checkSmsTemplateCodeDuplicate(Long id, String code) { | |
| 246 | + SysSmsTemplateDO template = smsTemplateMapper.selectByCode(code); | |
| 247 | + if (template == null) { | |
| 248 | + return; | |
| 249 | + } | |
| 250 | + // 如果 id 为空,说明不用比较是否为相同 id 的字典类型 | |
| 251 | + if (id == null) { | |
| 252 | + throw exception(SMS_TEMPLATE_CODE_DUPLICATE, code); | |
| 253 | + } | |
| 254 | + if (!template.getId().equals(id)) { | |
| 255 | + throw exception(SMS_TEMPLATE_CODE_DUPLICATE, code); | |
| 256 | + } | |
| 257 | + } | |
| 258 | + | |
| 259 | + /** | |
| 260 | + * 校验 API 短信平台的模板是否有效 | |
| 261 | + * | |
| 262 | + * @param channelId 渠道编号 | |
| 263 | + * @param apiTemplateId API 模板编号 | |
| 264 | + */ | |
| 265 | + @VisibleForTesting | |
| 266 | + public void checkApiTemplate(Long channelId, String apiTemplateId) { | |
| 267 | + // 获得短信模板 | |
| 268 | + SmsClient smsClient = smsClientFactory.getSmsClient(channelId); | |
| 269 | + Assert.notNull(smsClient, String.format("短信客户端(%d) 不存在", channelId)); | |
| 270 | + SmsCommonResult<SmsTemplateRespDTO> templateResult = smsClient.getSmsTemplate(apiTemplateId); | |
| 271 | + // 校验短信模板是否正确 | |
| 272 | + templateResult.checkError(); | |
| 273 | + } | |
| 274 | + | |
| 275 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserServiceImpl.java
| ... | ... | @@ -6,17 +6,11 @@ import cn.hutool.core.util.IdUtil; |
| 6 | 6 | import cn.hutool.core.util.StrUtil; |
| 7 | 7 | import cn.iocoder.dashboard.common.enums.CommonStatusEnum; |
| 8 | 8 | import cn.iocoder.dashboard.common.exception.ServiceException; |
| 9 | -import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil; | |
| 10 | 9 | import cn.iocoder.dashboard.common.pojo.PageResult; |
| 11 | 10 | import cn.iocoder.dashboard.modules.infra.service.file.InfFileService; |
| 12 | 11 | import cn.iocoder.dashboard.modules.system.controller.user.vo.profile.SysUserProfileUpdatePasswordReqVO; |
| 13 | 12 | import cn.iocoder.dashboard.modules.system.controller.user.vo.profile.SysUserProfileUpdateReqVO; |
| 14 | -import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserCreateReqVO; | |
| 15 | -import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserExportReqVO; | |
| 16 | -import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserImportExcelVO; | |
| 17 | -import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserImportRespVO; | |
| 18 | -import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserPageReqVO; | |
| 19 | -import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserUpdateReqVO; | |
| 13 | +import cn.iocoder.dashboard.modules.system.controller.user.vo.user.*; | |
| 20 | 14 | import cn.iocoder.dashboard.modules.system.convert.user.SysUserConvert; |
| 21 | 15 | import cn.iocoder.dashboard.modules.system.dal.dataobject.dept.SysDeptDO; |
| 22 | 16 | import cn.iocoder.dashboard.modules.system.dal.dataobject.dept.SysPostDO; |
| ... | ... | @@ -26,21 +20,18 @@ import cn.iocoder.dashboard.modules.system.service.dept.SysDeptService; |
| 26 | 20 | import cn.iocoder.dashboard.modules.system.service.dept.SysPostService; |
| 27 | 21 | import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService; |
| 28 | 22 | import cn.iocoder.dashboard.util.collection.CollectionUtils; |
| 23 | +import com.google.common.annotations.VisibleForTesting; | |
| 29 | 24 | import lombok.extern.slf4j.Slf4j; |
| 25 | +import org.springframework.beans.factory.annotation.Value; | |
| 30 | 26 | import org.springframework.security.crypto.password.PasswordEncoder; |
| 31 | 27 | import org.springframework.stereotype.Service; |
| 32 | 28 | import org.springframework.transaction.annotation.Transactional; |
| 33 | 29 | |
| 34 | 30 | import javax.annotation.Resource; |
| 35 | 31 | import java.io.InputStream; |
| 36 | -import java.util.ArrayList; | |
| 37 | -import java.util.Collection; | |
| 38 | -import java.util.Collections; | |
| 39 | -import java.util.LinkedHashMap; | |
| 40 | -import java.util.List; | |
| 41 | -import java.util.Map; | |
| 42 | -import java.util.Set; | |
| 32 | +import java.util.*; | |
| 43 | 33 | |
| 34 | +import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception; | |
| 44 | 35 | import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; |
| 45 | 36 | |
| 46 | 37 | |
| ... | ... | @@ -53,6 +44,9 @@ import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; |
| 53 | 44 | @Slf4j |
| 54 | 45 | public class SysUserServiceImpl implements SysUserService { |
| 55 | 46 | |
| 47 | + @Value("${sys.user.init-password:yudaoyuanma}") | |
| 48 | + private String userInitPassword; | |
| 49 | + | |
| 56 | 50 | @Resource |
| 57 | 51 | private SysUserMapper userMapper; |
| 58 | 52 | |
| ... | ... | @@ -142,8 +136,6 @@ public class SysUserServiceImpl implements SysUserService { |
| 142 | 136 | updateObj.setId(id); |
| 143 | 137 | updateObj.setStatus(status); |
| 144 | 138 | userMapper.updateById(updateObj); |
| 145 | - // 删除用户关联数据 | |
| 146 | - permissionService.processUserDeleted(id); | |
| 147 | 139 | } |
| 148 | 140 | |
| 149 | 141 | @Override |
| ... | ... | @@ -152,6 +144,8 @@ public class SysUserServiceImpl implements SysUserService { |
| 152 | 144 | this.checkUserExists(id); |
| 153 | 145 | // 删除用户 |
| 154 | 146 | userMapper.deleteById(id); |
| 147 | + // 删除用户关联数据 | |
| 148 | + permissionService.processUserDeleted(id); | |
| 155 | 149 | } |
| 156 | 150 | |
| 157 | 151 | @Override |
| ... | ... | @@ -221,18 +215,20 @@ public class SysUserServiceImpl implements SysUserService { |
| 221 | 215 | this.checkPostEnable(postIds); |
| 222 | 216 | } |
| 223 | 217 | |
| 224 | - private void checkUserExists(Long id) { | |
| 218 | + @VisibleForTesting | |
| 219 | + void checkUserExists(Long id) { | |
| 225 | 220 | if (id == null) { |
| 226 | 221 | return; |
| 227 | 222 | } |
| 228 | 223 | SysUserDO user = userMapper.selectById(id); |
| 229 | 224 | if (user == null) { |
| 230 | - throw ServiceExceptionUtil.exception(USER_NOT_EXISTS); | |
| 225 | + throw exception(USER_NOT_EXISTS); | |
| 231 | 226 | } |
| 232 | 227 | } |
| 233 | 228 | |
| 234 | - private void checkUsernameUnique(Long id, String username) { | |
| 235 | - if (StrUtil.isNotBlank(username)) { | |
| 229 | + @VisibleForTesting | |
| 230 | + void checkUsernameUnique(Long id, String username) { | |
| 231 | + if (StrUtil.isBlank(username)) { | |
| 236 | 232 | return; |
| 237 | 233 | } |
| 238 | 234 | SysUserDO user = userMapper.selectByUsername(username); |
| ... | ... | @@ -241,15 +237,16 @@ public class SysUserServiceImpl implements SysUserService { |
| 241 | 237 | } |
| 242 | 238 | // 如果 id 为空,说明不用比较是否为相同 id 的用户 |
| 243 | 239 | if (id == null) { |
| 244 | - throw ServiceExceptionUtil.exception(USER_USERNAME_EXISTS); | |
| 240 | + throw exception(USER_USERNAME_EXISTS); | |
| 245 | 241 | } |
| 246 | 242 | if (!user.getId().equals(id)) { |
| 247 | - throw ServiceExceptionUtil.exception(USER_USERNAME_EXISTS); | |
| 243 | + throw exception(USER_USERNAME_EXISTS); | |
| 248 | 244 | } |
| 249 | 245 | } |
| 250 | 246 | |
| 251 | - private void checkEmailUnique(Long id, String email) { | |
| 252 | - if (StrUtil.isNotBlank(email)) { | |
| 247 | + @VisibleForTesting | |
| 248 | + void checkEmailUnique(Long id, String email) { | |
| 249 | + if (StrUtil.isBlank(email)) { | |
| 253 | 250 | return; |
| 254 | 251 | } |
| 255 | 252 | SysUserDO user = userMapper.selectByEmail(email); |
| ... | ... | @@ -258,59 +255,62 @@ public class SysUserServiceImpl implements SysUserService { |
| 258 | 255 | } |
| 259 | 256 | // 如果 id 为空,说明不用比较是否为相同 id 的用户 |
| 260 | 257 | if (id == null) { |
| 261 | - throw ServiceExceptionUtil.exception(USER_EMAIL_EXISTS); | |
| 258 | + throw exception(USER_EMAIL_EXISTS); | |
| 262 | 259 | } |
| 263 | 260 | if (!user.getId().equals(id)) { |
| 264 | - throw ServiceExceptionUtil.exception(USER_EMAIL_EXISTS); | |
| 261 | + throw exception(USER_EMAIL_EXISTS); | |
| 265 | 262 | } |
| 266 | 263 | } |
| 267 | 264 | |
| 268 | - private void checkMobileUnique(Long id, String email) { | |
| 269 | - if (StrUtil.isNotBlank(email)) { | |
| 265 | + @VisibleForTesting | |
| 266 | + void checkMobileUnique(Long id, String mobile) { | |
| 267 | + if (StrUtil.isBlank(mobile)) { | |
| 270 | 268 | return; |
| 271 | 269 | } |
| 272 | - SysUserDO user = userMapper.selectByMobile(email); | |
| 270 | + SysUserDO user = userMapper.selectByMobile(mobile); | |
| 273 | 271 | if (user == null) { |
| 274 | 272 | return; |
| 275 | 273 | } |
| 276 | 274 | // 如果 id 为空,说明不用比较是否为相同 id 的用户 |
| 277 | 275 | if (id == null) { |
| 278 | - throw ServiceExceptionUtil.exception(USER_MOBILE_EXISTS); | |
| 276 | + throw exception(USER_MOBILE_EXISTS); | |
| 279 | 277 | } |
| 280 | 278 | if (!user.getId().equals(id)) { |
| 281 | - throw ServiceExceptionUtil.exception(USER_MOBILE_EXISTS); | |
| 279 | + throw exception(USER_MOBILE_EXISTS); | |
| 282 | 280 | } |
| 283 | 281 | } |
| 284 | 282 | |
| 285 | - private void checkDeptEnable(Long deptId) { | |
| 283 | + @VisibleForTesting | |
| 284 | + void checkDeptEnable(Long deptId) { | |
| 286 | 285 | if (deptId == null) { // 允许不选择 |
| 287 | 286 | return; |
| 288 | 287 | } |
| 289 | 288 | SysDeptDO dept = deptService.getDept(deptId); |
| 290 | 289 | if (dept == null) { |
| 291 | - throw ServiceExceptionUtil.exception(DEPT_NOT_FOUND); | |
| 290 | + throw exception(DEPT_NOT_FOUND); | |
| 292 | 291 | } |
| 293 | 292 | if (!CommonStatusEnum.ENABLE.getStatus().equals(dept.getStatus())) { |
| 294 | - throw ServiceExceptionUtil.exception(DEPT_NOT_ENABLE); | |
| 293 | + throw exception(DEPT_NOT_ENABLE); | |
| 295 | 294 | } |
| 296 | 295 | } |
| 297 | 296 | |
| 298 | - private void checkPostEnable(Set<Long> postIds) { | |
| 297 | + @VisibleForTesting | |
| 298 | + void checkPostEnable(Set<Long> postIds) { | |
| 299 | 299 | if (CollUtil.isEmpty(postIds)) { // 允许不选择 |
| 300 | 300 | return; |
| 301 | 301 | } |
| 302 | 302 | List<SysPostDO> posts = postService.getPosts(postIds, null); |
| 303 | 303 | if (CollUtil.isEmpty(posts)) { |
| 304 | - throw ServiceExceptionUtil.exception(POST_NOT_FOUND); | |
| 304 | + throw exception(POST_NOT_FOUND); | |
| 305 | 305 | } |
| 306 | 306 | Map<Long, SysPostDO> postMap = CollectionUtils.convertMap(posts, SysPostDO::getId); |
| 307 | 307 | postIds.forEach(postId -> { |
| 308 | 308 | SysPostDO post = postMap.get(postId); |
| 309 | 309 | if (post == null) { |
| 310 | - throw ServiceExceptionUtil.exception(POST_NOT_FOUND); | |
| 310 | + throw exception(POST_NOT_FOUND); | |
| 311 | 311 | } |
| 312 | 312 | if (!CommonStatusEnum.ENABLE.getStatus().equals(post.getStatus())) { |
| 313 | - throw ServiceExceptionUtil.exception(POST_NOT_ENABLE, post.getName()); | |
| 313 | + throw exception(POST_NOT_ENABLE, post.getName()); | |
| 314 | 314 | } |
| 315 | 315 | }); |
| 316 | 316 | } |
| ... | ... | @@ -321,13 +321,14 @@ public class SysUserServiceImpl implements SysUserService { |
| 321 | 321 | * @param id 用户 id |
| 322 | 322 | * @param oldPassword 旧密码 |
| 323 | 323 | */ |
| 324 | - private void checkOldPassword(Long id, String oldPassword) { | |
| 324 | + @VisibleForTesting | |
| 325 | + void checkOldPassword(Long id, String oldPassword) { | |
| 325 | 326 | SysUserDO user = userMapper.selectById(id); |
| 326 | 327 | if (user == null) { |
| 327 | - throw ServiceExceptionUtil.exception(USER_NOT_EXISTS); | |
| 328 | + throw exception(USER_NOT_EXISTS); | |
| 328 | 329 | } |
| 329 | 330 | if (!passwordEncoder.matches(oldPassword, user.getPassword())) { |
| 330 | - throw ServiceExceptionUtil.exception(USER_PASSWORD_FAILED); | |
| 331 | + throw exception(USER_PASSWORD_FAILED); | |
| 331 | 332 | } |
| 332 | 333 | } |
| 333 | 334 | |
| ... | ... | @@ -335,7 +336,7 @@ public class SysUserServiceImpl implements SysUserService { |
| 335 | 336 | @Transactional(rollbackFor = Exception.class) // 添加事务,异常则回滚所有导入 |
| 336 | 337 | public SysUserImportRespVO importUsers(List<SysUserImportExcelVO> importUsers, boolean isUpdateSupport) { |
| 337 | 338 | if (CollUtil.isEmpty(importUsers)) { |
| 338 | - throw ServiceExceptionUtil.exception(USER_IMPORT_LIST_IS_EMPTY); | |
| 339 | + throw exception(USER_IMPORT_LIST_IS_EMPTY); | |
| 339 | 340 | } |
| 340 | 341 | SysUserImportRespVO respVO = SysUserImportRespVO.builder().createUsernames(new ArrayList<>()) |
| 341 | 342 | .updateUsernames(new ArrayList<>()).failureUsernames(new LinkedHashMap<>()).build(); |
| ... | ... | @@ -351,14 +352,14 @@ public class SysUserServiceImpl implements SysUserService { |
| 351 | 352 | // 判断如果不存在,在进行插入 |
| 352 | 353 | SysUserDO existUser = userMapper.selectByUsername(importUser.getUsername()); |
| 353 | 354 | if (existUser == null) { |
| 354 | - // TODO 芋艿:初始密码 | |
| 355 | - userMapper.insert(SysUserConvert.INSTANCE.convert(importUser)); | |
| 355 | + userMapper.insert(SysUserConvert.INSTANCE.convert(importUser) | |
| 356 | + .setPassword(passwordEncoder.encode(userInitPassword))); // 设置默认密码 | |
| 356 | 357 | respVO.getCreateUsernames().add(importUser.getUsername()); |
| 357 | 358 | return; |
| 358 | 359 | } |
| 359 | 360 | // 如果存在,判断是否允许更新 |
| 360 | 361 | if (!isUpdateSupport) { |
| 361 | - respVO.getFailureUsernames().put(importUser.getUsername(), USER_USERNAME_EXISTS.getMessage()); | |
| 362 | + respVO.getFailureUsernames().put(importUser.getUsername(), USER_USERNAME_EXISTS.getMsg()); | |
| 362 | 363 | return; |
| 363 | 364 | } |
| 364 | 365 | SysUserDO updateUser = SysUserConvert.INSTANCE.convert(importUser); | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/tool/dal/mysql/codegen/ToolSchemaColumnMapper.java
| ... | ... | @@ -10,9 +10,10 @@ import java.util.List; |
| 10 | 10 | @Mapper |
| 11 | 11 | public interface ToolSchemaColumnMapper extends BaseMapperX<ToolSchemaColumnDO> { |
| 12 | 12 | |
| 13 | - default List<ToolSchemaColumnDO> selectListByTableName(String tableName) { | |
| 13 | + default List<ToolSchemaColumnDO> selectListByTableName(String tableSchema, String tableName) { | |
| 14 | 14 | return selectList(new QueryWrapper<ToolSchemaColumnDO>().eq("table_name", tableName) |
| 15 | - .orderByAsc("ordinal_position")); | |
| 15 | + .eq("table_schema", tableSchema) | |
| 16 | + .orderByAsc("ordinal_position")); | |
| 16 | 17 | } |
| 17 | 18 | |
| 18 | 19 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/tool/dal/mysql/codegen/ToolSchemaTableMapper.java
| ... | ... | @@ -22,8 +22,9 @@ public interface ToolSchemaTableMapper extends BaseMapperX<ToolSchemaTableDO> { |
| 22 | 22 | return selectList(new QueryWrapper<ToolSchemaTableDO>().eq("table_schema", tableSchema)); |
| 23 | 23 | } |
| 24 | 24 | |
| 25 | - default ToolSchemaTableDO selectByTableName(String tableName) { | |
| 26 | - return selectOne(new QueryWrapper<ToolSchemaTableDO>().eq("table_name", tableName)); | |
| 25 | + default ToolSchemaTableDO selectByTableName1(String tableSchema, String tableName) { | |
| 26 | + return selectOne(new QueryWrapper<ToolSchemaTableDO>().eq("table_schema",tableSchema) | |
| 27 | + .eq("table_name", tableName)); | |
| 27 | 28 | } |
| 28 | 29 | |
| 29 | 30 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/tool/service/codegen/impl/ToolCodegenServiceImpl.java
| ... | ... | @@ -101,9 +101,11 @@ public class ToolCodegenServiceImpl implements ToolCodegenService { |
| 101 | 101 | |
| 102 | 102 | @Override |
| 103 | 103 | public Long createCodegen(String tableName) { |
| 104 | + //获取当前schema | |
| 105 | + String tableSchema = codegenProperties.getDbSchemas().iterator().next(); | |
| 104 | 106 | // 从数据库中,获得数据库表结构 |
| 105 | - ToolSchemaTableDO schemaTable = schemaTableMapper.selectByTableName(tableName); | |
| 106 | - List<ToolSchemaColumnDO> schemaColumns = schemaColumnMapper.selectListByTableName(tableName); | |
| 107 | + ToolSchemaTableDO schemaTable = schemaTableMapper.selectByTableName1(tableSchema, tableName); | |
| 108 | + List<ToolSchemaColumnDO> schemaColumns = schemaColumnMapper.selectListByTableName(tableSchema, tableName); | |
| 107 | 109 | // 导入 |
| 108 | 110 | return this.createCodegen0(ToolCodegenImportTypeEnum.DB, schemaTable, schemaColumns); |
| 109 | 111 | } |
| ... | ... | @@ -141,8 +143,9 @@ public class ToolCodegenServiceImpl implements ToolCodegenService { |
| 141 | 143 | if (table == null) { |
| 142 | 144 | throw exception(CODEGEN_TABLE_NOT_EXISTS); |
| 143 | 145 | } |
| 146 | + String tableSchema = codegenProperties.getDbSchemas().iterator().next(); | |
| 144 | 147 | // 从数据库中,获得数据库表结构 |
| 145 | - List<ToolSchemaColumnDO> schemaColumns = schemaColumnMapper.selectListByTableName(table.getTableName()); | |
| 148 | + List<ToolSchemaColumnDO> schemaColumns = schemaColumnMapper.selectListByTableName(tableSchema, table.getTableName()); | |
| 146 | 149 | |
| 147 | 150 | // 执行同步 |
| 148 | 151 | this.syncCodegen0(tableId, schemaColumns); | ... | ... |
src/main/java/cn/iocoder/dashboard/util/collection/MapUtils.java
| ... | ... | @@ -2,6 +2,8 @@ package cn.iocoder.dashboard.util.collection; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.collection.CollectionUtil; |
| 5 | +import cn.iocoder.dashboard.common.core.KeyValue; | |
| 6 | +import com.google.common.collect.Maps; | |
| 5 | 7 | import com.google.common.collect.Multimap; |
| 6 | 8 | |
| 7 | 9 | import java.util.ArrayList; |
| ... | ... | @@ -55,4 +57,10 @@ public class MapUtils { |
| 55 | 57 | consumer.accept(value); |
| 56 | 58 | } |
| 57 | 59 | |
| 60 | + public static <K, V> Map<K, V> convertMap(List<KeyValue<K, V>> keyValues) { | |
| 61 | + Map<K, V> map = Maps.newLinkedHashMapWithExpectedSize(keyValues.size()); | |
| 62 | + keyValues.forEach(keyValue -> map.put(keyValue.getKey(), keyValue.getValue())); | |
| 63 | + return map; | |
| 64 | + } | |
| 65 | + | |
| 58 | 66 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/util/date/DateUtils.java
| ... | ... | @@ -9,6 +9,11 @@ import java.util.Date; |
| 9 | 9 | */ |
| 10 | 10 | public class DateUtils { |
| 11 | 11 | |
| 12 | + /** | |
| 13 | + * 时区 - 默认 | |
| 14 | + */ | |
| 15 | + public static final String TIME_ZONE_DEFAULT = "GMT+8"; | |
| 16 | + | |
| 12 | 17 | public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss"; |
| 13 | 18 | |
| 14 | 19 | public static Date addTime(Duration duration) { | ... | ... |
src/main/java/cn/iocoder/dashboard/util/json/JsonUtils.java
| ... | ... | @@ -7,7 +7,8 @@ import com.fasterxml.jackson.core.type.TypeReference; |
| 7 | 7 | import com.fasterxml.jackson.databind.ObjectMapper; |
| 8 | 8 | |
| 9 | 9 | import java.io.IOException; |
| 10 | -import java.util.Set; | |
| 10 | +import java.util.ArrayList; | |
| 11 | +import java.util.List; | |
| 11 | 12 | |
| 12 | 13 | /** |
| 13 | 14 | * JSON 工具类 |
| ... | ... | @@ -20,7 +21,7 @@ public class JsonUtils { |
| 20 | 21 | |
| 21 | 22 | /** |
| 22 | 23 | * 初始化 objectMapper 属性 |
| 23 | - * | |
| 24 | + * <p> | |
| 24 | 25 | * 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean |
| 25 | 26 | * |
| 26 | 27 | * @param objectMapper ObjectMapper 对象 |
| ... | ... | @@ -59,7 +60,7 @@ public class JsonUtils { |
| 59 | 60 | } |
| 60 | 61 | } |
| 61 | 62 | |
| 62 | - public static Object parseObject(String text, TypeReference<Set<Long>> typeReference) { | |
| 63 | + public static <T> T parseObject(String text, TypeReference<T> typeReference) { | |
| 63 | 64 | try { |
| 64 | 65 | return objectMapper.readValue(text, typeReference); |
| 65 | 66 | } catch (IOException e) { |
| ... | ... | @@ -67,4 +68,15 @@ public class JsonUtils { |
| 67 | 68 | } |
| 68 | 69 | } |
| 69 | 70 | |
| 71 | + public static <T> List<T> parseArray(String text, Class<T> clazz) { | |
| 72 | + if (StrUtil.isEmpty(text)) { | |
| 73 | + return new ArrayList<>(); | |
| 74 | + } | |
| 75 | + try { | |
| 76 | + return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz)); | |
| 77 | + } catch (IOException e) { | |
| 78 | + throw new RuntimeException(e); | |
| 79 | + } | |
| 80 | + } | |
| 81 | + | |
| 70 | 82 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/util/string/StrUtils.java
| 1 | 1 | package cn.iocoder.dashboard.util.string; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.util.ObjectUtil; | |
| 3 | 4 | import cn.hutool.core.util.StrUtil; |
| 4 | 5 | |
| 6 | +import java.util.Map; | |
| 7 | + | |
| 5 | 8 | /** |
| 6 | 9 | * 字符串工具类 |
| 7 | 10 | * |
| ... | ... | @@ -13,4 +16,22 @@ public class StrUtils { |
| 13 | 16 | return StrUtil.maxLength(str, maxLength - 3); // -3 的原因,是该方法会补充 ... 恰好 |
| 14 | 17 | } |
| 15 | 18 | |
| 19 | + /** | |
| 20 | + * 指定字符串的 | |
| 21 | + * @param str | |
| 22 | + * @param replaceMap | |
| 23 | + * @return | |
| 24 | + */ | |
| 25 | + public static String replace(String str, Map<String, String> replaceMap) { | |
| 26 | + assert StrUtil.isNotBlank(str); | |
| 27 | + if (ObjectUtil.isEmpty(replaceMap)) { | |
| 28 | + return str; | |
| 29 | + } | |
| 30 | + String result = null; | |
| 31 | + for (String key : replaceMap.keySet()) { | |
| 32 | + result = str.replace(key, replaceMap.get(key)); | |
| 33 | + } | |
| 34 | + return result; | |
| 35 | + } | |
| 36 | + | |
| 16 | 37 | } | ... | ... |
src/main/resources/application-dev.yaml
src/main/resources/application-local.yaml
src/main/resources/codegen/java/service/service.vm
| ... | ... | @@ -63,7 +63,7 @@ public interface ${table.className}Service { |
| 63 | 63 | * 获得${table.classComment}列表, 用于 Excel 导出 |
| 64 | 64 | * |
| 65 | 65 | * @param exportReqVO 查询条件 |
| 66 | - * @return ${table.classComment}分页 | |
| 66 | + * @return ${table.classComment}列表 | |
| 67 | 67 | */ |
| 68 | 68 | List<${table.className}DO> get${simpleClassName}List(${table.className}ExportReqVO exportReqVO); |
| 69 | 69 | ... | ... |
src/main/resources/codegen/vue/views/index.vue.vm
| ... | ... | @@ -76,7 +76,7 @@ |
| 76 | 76 | <template slot-scope="scope"> |
| 77 | 77 | <span>{{ getDictDataLabel(DICT_TYPE.$dictType.toUpperCase(), scope.row.${column.javaField}) }}</span> |
| 78 | 78 | </template> |
| 79 | - </el-table-column>> | |
| 79 | + </el-table-column> | |
| 80 | 80 | #else |
| 81 | 81 | <el-table-column label="${comment}" align="center" prop="${javaField}" /> |
| 82 | 82 | #end |
| ... | ... | @@ -137,7 +137,7 @@ |
| 137 | 137 | </el-select> |
| 138 | 138 | </el-form-item> |
| 139 | 139 | #elseif($column.htmlType == "checkbox")## 多选框 |
| 140 | - <el-form-item label="${comment}"> | |
| 140 | + <el-form-item label="${comment}" prop="${javaField}"> | |
| 141 | 141 | <el-checkbox-group v-model="form.${javaField}"> |
| 142 | 142 | #if ("" != $dictType)## 有数据字典 |
| 143 | 143 | <el-checkbox v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())" |
| ... | ... | @@ -148,7 +148,7 @@ |
| 148 | 148 | </el-checkbox-group> |
| 149 | 149 | </el-form-item> |
| 150 | 150 | #elseif($column.htmlType == "radio")## 单选框 |
| 151 | - <el-form-item label="${comment}"> | |
| 151 | + <el-form-item label="${comment}" prop="${javaField}"> | |
| 152 | 152 | <el-radio-group v-model="form.${javaField}"> |
| 153 | 153 | #if ("" != $dictType)## 有数据字典 |
| 154 | 154 | <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())" | ... | ... |
src/test-integration/java/cn/iocoder/dashboard/BaseDbAndRedisIntegrationTest.java
0 → 100644
| 1 | +package cn.iocoder.dashboard; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.datasource.config.DataSourceConfiguration; | |
| 4 | +import cn.iocoder.dashboard.framework.mybatis.config.MybatisConfiguration; | |
| 5 | +import cn.iocoder.dashboard.framework.redis.config.RedisConfig; | |
| 6 | +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure; | |
| 7 | +import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; | |
| 8 | +import org.redisson.spring.starter.RedissonAutoConfiguration; | |
| 9 | +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; | |
| 10 | +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; | |
| 11 | +import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; | |
| 12 | +import org.springframework.boot.test.context.SpringBootTest; | |
| 13 | +import org.springframework.context.annotation.Import; | |
| 14 | +import org.springframework.test.context.ActiveProfiles; | |
| 15 | + | |
| 16 | +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbAndRedisIntegrationTest.Application.class) | |
| 17 | +@ActiveProfiles("integration-test") // 设置使用 application-integration-test 配置文件 | |
| 18 | +public class BaseDbAndRedisIntegrationTest { | |
| 19 | + | |
| 20 | + @Import({ | |
| 21 | + // DB 配置类 | |
| 22 | + DataSourceConfiguration.class, // 自己的 DB 配置类 | |
| 23 | + DataSourceAutoConfiguration.class, // Spring DB 自动配置类 | |
| 24 | + DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类 | |
| 25 | + DruidDataSourceAutoConfigure.class, // Druid 自动配置类 | |
| 26 | + // MyBatis 配置类 | |
| 27 | + MybatisConfiguration.class, // 自己的 MyBatis 配置类 | |
| 28 | + MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类 | |
| 29 | + // Redis 配置类 | |
| 30 | + RedisAutoConfiguration.class, // Spring Redis 自动配置类 | |
| 31 | + RedisConfig.class, // 自己的 Redis 配置类 | |
| 32 | + RedissonAutoConfiguration.class, // Redisson 自动高配置类 | |
| 33 | + }) | |
| 34 | + public static class Application { | |
| 35 | + } | |
| 36 | + | |
| 37 | +} | ... | ... |
src/test-integration/java/cn/iocoder/dashboard/framework/redis/core/stream/RedisStreamTest.java
| ... | ... | @@ -43,7 +43,7 @@ public class RedisStreamTest { |
| 43 | 43 | for (int i = 0; i < 100; i++) { |
| 44 | 44 | // 创建消息 |
| 45 | 45 | SysSmsSendMessage message = new SysSmsSendMessage(); |
| 46 | - message.setMobile("15601691300").setTemplateCode("test:" + i); | |
| 46 | + message.setMobile("15601691300").setApiTemplateId("test:" + i); | |
| 47 | 47 | // 发送消息 |
| 48 | 48 | RedisMessageUtils.sendStreamMessage(stringRedisTemplate, message); |
| 49 | 49 | } | ... | ... |
src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClientIntegrationTest.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.common.core.KeyValue; | |
| 4 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; | |
| 5 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; | |
| 6 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsTemplateRespDTO; | |
| 7 | +import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum; | |
| 8 | +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; | |
| 9 | +import org.junit.jupiter.api.BeforeAll; | |
| 10 | +import org.junit.jupiter.api.Test; | |
| 11 | + | |
| 12 | +import java.util.ArrayList; | |
| 13 | +import java.util.List; | |
| 14 | + | |
| 15 | +/** | |
| 16 | + * {@link AliyunSmsClient} 的集成测试 | |
| 17 | + */ | |
| 18 | +public class AliyunSmsClientIntegrationTest { | |
| 19 | + | |
| 20 | + private static AliyunSmsClient smsClient; | |
| 21 | + | |
| 22 | + @BeforeAll | |
| 23 | + public static void before() { | |
| 24 | + // 创建配置类 | |
| 25 | + SmsChannelProperties properties = new SmsChannelProperties(); | |
| 26 | + properties.setId(1L); | |
| 27 | + properties.setSignature("Ballcat"); | |
| 28 | + properties.setCode(SmsChannelEnum.ALIYUN.getCode()); | |
| 29 | + properties.setApiKey(System.getenv("ALIYUN_ACCESS_KEY")); | |
| 30 | + properties.setApiSecret(System.getenv("ALIYUN_SECRET_KEY")); | |
| 31 | + // 创建客户端 | |
| 32 | + smsClient = new AliyunSmsClient(properties); | |
| 33 | + smsClient.init(); | |
| 34 | + } | |
| 35 | + | |
| 36 | + @Test | |
| 37 | + public void testSendSms() { | |
| 38 | + List<KeyValue<String, Object>> templateParams = new ArrayList<>(); | |
| 39 | + templateParams.add(new KeyValue<>("code", "1024")); | |
| 40 | +// templateParams.put("operation", "嘿嘿"); | |
| 41 | +// SmsResult result = smsClient.send(1L, "15601691399", "4372216", templateParams); | |
| 42 | + SmsCommonResult<SmsSendRespDTO> result = smsClient.sendSms(1L, "15601691399", | |
| 43 | + "SMS_207945135", templateParams); | |
| 44 | + System.out.println(result); | |
| 45 | + } | |
| 46 | + | |
| 47 | + @Test | |
| 48 | + public void testGetSmsTemplate() { | |
| 49 | + String apiTemplateId = "SMS_2079451351"; | |
| 50 | + SmsCommonResult<SmsTemplateRespDTO> result = smsClient.getSmsTemplate(apiTemplateId); | |
| 51 | + System.out.println(result); | |
| 52 | + } | |
| 53 | + | |
| 54 | +} | ... | ... |
src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/debug/DebugDingTalkSmsClientIntegrationTest.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client.impl.debug; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.common.core.KeyValue; | |
| 4 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; | |
| 5 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; | |
| 6 | +import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum; | |
| 7 | +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; | |
| 8 | +import org.junit.jupiter.api.BeforeAll; | |
| 9 | +import org.junit.jupiter.api.Test; | |
| 10 | + | |
| 11 | +import java.util.ArrayList; | |
| 12 | +import java.util.List; | |
| 13 | + | |
| 14 | +/** | |
| 15 | + * {@link DebugDingTalkSmsClient} 的集成测试 | |
| 16 | + */ | |
| 17 | +public class DebugDingTalkSmsClientIntegrationTest { | |
| 18 | + | |
| 19 | + private static DebugDingTalkSmsClient smsClient; | |
| 20 | + | |
| 21 | + @BeforeAll | |
| 22 | + public static void init() { | |
| 23 | + // 创建配置类 | |
| 24 | + SmsChannelProperties properties = new SmsChannelProperties(); | |
| 25 | + properties.setId(1L); | |
| 26 | + properties.setSignature("芋道"); | |
| 27 | + properties.setCode(SmsChannelEnum.DEBUG_DING_TALK.getCode()); | |
| 28 | + properties.setApiKey("696b5d8ead48071237e4aa5861ff08dbadb2b4ded1c688a7b7c9afc615579859"); | |
| 29 | + properties.setApiSecret("SEC5c4e5ff888bc8a9923ae47f59e7ccd30af1f14d93c55b4e2c9cb094e35aeed67"); | |
| 30 | + // 创建客户端 | |
| 31 | + smsClient = new DebugDingTalkSmsClient(properties); | |
| 32 | + smsClient.init(); | |
| 33 | + } | |
| 34 | + | |
| 35 | + @Test | |
| 36 | + public void testSendSms() { | |
| 37 | + List<KeyValue<String, Object>> templateParams = new ArrayList<>(); | |
| 38 | + templateParams.add(new KeyValue<>("code", "1024")); | |
| 39 | + templateParams.add(new KeyValue<>("operation", "嘿嘿")); | |
| 40 | +// SmsResult result = smsClient.send(1L, "15601691399", "4372216", templateParams); | |
| 41 | + SmsCommonResult<SmsSendRespDTO> result = smsClient.sendSms(1L, "15601691399", "4383920", templateParams); | |
| 42 | + System.out.println(result); | |
| 43 | + } | |
| 44 | + | |
| 45 | +} | ... | ... |
src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClientIntegrationTest.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.common.core.KeyValue; | |
| 4 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; | |
| 5 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; | |
| 6 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsTemplateRespDTO; | |
| 7 | +import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum; | |
| 8 | +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; | |
| 9 | +import org.junit.jupiter.api.BeforeAll; | |
| 10 | +import org.junit.jupiter.api.Test; | |
| 11 | + | |
| 12 | +import java.util.ArrayList; | |
| 13 | +import java.util.List; | |
| 14 | + | |
| 15 | +/** | |
| 16 | + * {@link YunpianSmsClient} 的集成测试 | |
| 17 | + */ | |
| 18 | +public class YunpianSmsClientIntegrationTest { | |
| 19 | + | |
| 20 | + private static YunpianSmsClient smsClient; | |
| 21 | + | |
| 22 | + @BeforeAll | |
| 23 | + public static void init() { | |
| 24 | + // 创建配置类 | |
| 25 | + SmsChannelProperties properties = new SmsChannelProperties(); | |
| 26 | + properties.setId(1L); | |
| 27 | + properties.setSignature("芋道"); | |
| 28 | + properties.setCode(SmsChannelEnum.YUN_PIAN.getCode()); | |
| 29 | + properties.setApiKey("1555a14277cb8a608cf45a9e6a80d510"); | |
| 30 | + // 创建客户端 | |
| 31 | + smsClient = new YunpianSmsClient(properties); | |
| 32 | + smsClient.init(); | |
| 33 | + } | |
| 34 | + | |
| 35 | + @Test | |
| 36 | + public void testSendSms() { | |
| 37 | + List<KeyValue<String, Object>> templateParams = new ArrayList<>(); | |
| 38 | + templateParams.add(new KeyValue<>("code", "1024")); | |
| 39 | + templateParams.add(new KeyValue<>("operation", "嘿嘿")); | |
| 40 | +// SmsResult result = smsClient.send(1L, "15601691399", "4372216", templateParams); | |
| 41 | + SmsCommonResult<SmsSendRespDTO> result = smsClient.sendSms(1L, "15601691399", "4383920", templateParams); | |
| 42 | + System.out.println(result); | |
| 43 | + } | |
| 44 | + | |
| 45 | + @Test | |
| 46 | + public void testGetSmsTemplate() { | |
| 47 | + String apiTemplateId = "4383920"; | |
| 48 | + SmsCommonResult<SmsTemplateRespDTO> result = smsClient.getSmsTemplate(apiTemplateId); | |
| 49 | + System.out.println(result); | |
| 50 | + } | |
| 51 | + | |
| 52 | +} | ... | ... |
src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/package-info.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client; | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/package-info.java renamed to src/test-integration/java/cn/iocoder/dashboard/modules/system/service/package-info.java
src/test-integration/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsServiceIntegrationTest.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.sms; | |
| 2 | + | |
| 3 | +import cn.hutool.core.map.MapUtil; | |
| 4 | +import cn.hutool.core.thread.ThreadUtil; | |
| 5 | +import cn.iocoder.dashboard.BaseDbAndRedisIntegrationTest; | |
| 6 | +import cn.iocoder.dashboard.common.enums.UserTypeEnum; | |
| 7 | +import cn.iocoder.dashboard.framework.sms.config.SmsConfiguration; | |
| 8 | +import cn.iocoder.dashboard.modules.system.mq.consumer.sms.SysSmsSendConsumer; | |
| 9 | +import cn.iocoder.dashboard.modules.system.mq.producer.sms.SysSmsProducer; | |
| 10 | +import cn.iocoder.dashboard.modules.system.service.sms.impl.SysSmsChannelServiceImpl; | |
| 11 | +import cn.iocoder.dashboard.modules.system.service.sms.impl.SysSmsLogServiceImpl; | |
| 12 | +import cn.iocoder.dashboard.modules.system.service.sms.impl.SysSmsServiceImpl; | |
| 13 | +import cn.iocoder.dashboard.modules.system.service.sms.impl.SysSmsTemplateServiceImpl; | |
| 14 | +import cn.iocoder.dashboard.modules.system.service.user.SysUserService; | |
| 15 | +import org.junit.jupiter.api.Test; | |
| 16 | +import org.springframework.boot.test.mock.mockito.MockBean; | |
| 17 | +import org.springframework.context.annotation.Import; | |
| 18 | + | |
| 19 | +import javax.annotation.Resource; | |
| 20 | +import java.util.Map; | |
| 21 | +import java.util.concurrent.TimeUnit; | |
| 22 | + | |
| 23 | +@Import({SmsConfiguration.class, | |
| 24 | + SysSmsChannelServiceImpl.class, SysSmsServiceImpl.class, SysSmsTemplateServiceImpl.class, SysSmsLogServiceImpl.class, | |
| 25 | + SysSmsProducer.class, SysSmsSendConsumer.class}) | |
| 26 | +public class SysSmsServiceIntegrationTest extends BaseDbAndRedisIntegrationTest { | |
| 27 | + | |
| 28 | + @Resource | |
| 29 | + private SysSmsServiceImpl smsService; | |
| 30 | + @Resource | |
| 31 | + private SysSmsChannelServiceImpl smsChannelService; | |
| 32 | + | |
| 33 | + @MockBean | |
| 34 | + private SysUserService userService; | |
| 35 | + | |
| 36 | + @Test | |
| 37 | + public void testSendSingleSms_yunpianSuccess() { | |
| 38 | + // 参数准备 | |
| 39 | + String mobile = "15601691399"; | |
| 40 | + Long userId = 1L; | |
| 41 | + Integer userType = UserTypeEnum.ADMIN.getValue(); | |
| 42 | + String templateCode = "test_01"; | |
| 43 | + Map<String, Object> templateParams = MapUtil.<String, Object>builder() | |
| 44 | + .put("operation", "登陆").put("code", "1234").build(); | |
| 45 | + // 调用 | |
| 46 | + smsService.sendSingleSms(mobile, userId, userType, templateCode, templateParams); | |
| 47 | + | |
| 48 | + // 等待 MQ 消费 | |
| 49 | + ThreadUtil.sleep(1, TimeUnit.HOURS); | |
| 50 | + } | |
| 51 | + | |
| 52 | + @Test | |
| 53 | + public void testSendSingleSms_aliyunSuccess() { | |
| 54 | + // 参数准备 | |
| 55 | + String mobile = "15601691399"; | |
| 56 | + Long userId = 1L; | |
| 57 | + Integer userType = UserTypeEnum.ADMIN.getValue(); | |
| 58 | + String templateCode = "test_02"; | |
| 59 | + Map<String, Object> templateParams = MapUtil.<String, Object>builder() | |
| 60 | + .put("code", "1234").build(); | |
| 61 | + // 调用 | |
| 62 | + smsService.sendSingleSms(mobile, userId, userType, templateCode, templateParams); | |
| 63 | + | |
| 64 | + // 等待 MQ 消费 | |
| 65 | + ThreadUtil.sleep(1, TimeUnit.HOURS); | |
| 66 | + } | |
| 67 | + | |
| 68 | +// @Test | |
| 69 | +// public void testDoSendSms() { | |
| 70 | +// // 等待 MQ 消费 | |
| 71 | +// ThreadUtil.sleep(1, TimeUnit.HOURS); | |
| 72 | +// } | |
| 73 | + | |
| 74 | +} | ... | ... |
src/test-integration/java/cn/iocoder/dashboard/modules/tool/dal/mysql/codegen/ToolInformationSchemaColumnMapperTest.java
| ... | ... | @@ -17,7 +17,7 @@ public class ToolInformationSchemaColumnMapperTest extends BaseDbUnitTest { |
| 17 | 17 | @Test |
| 18 | 18 | public void testSelectListByTableName() { |
| 19 | 19 | List<ToolSchemaColumnDO> columns = toolInformationSchemaColumnMapper |
| 20 | - .selectListByTableName("inf_config"); | |
| 20 | + .selectListByTableName("", "inf_config"); | |
| 21 | 21 | assertTrue(columns.size() > 0); |
| 22 | 22 | } |
| 23 | 23 | ... | ... |
src/test-integration/resources/application-integration-test.yaml
| ... | ... | @@ -9,19 +9,15 @@ spring: |
| 9 | 9 | # 数据源配置项 |
| 10 | 10 | datasource: |
| 11 | 11 | name: ruoyi-vue-pro |
| 12 | - url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写 | |
| 13 | - driver-class-name: org.h2.Driver | |
| 14 | - username: sa | |
| 15 | - password: | |
| 16 | - schema: classpath:sql/create_tables.sql # MySQL 转 H2 的语句,使用 https://www.jooq.org/translate/ 工具 | |
| 17 | - druid: | |
| 18 | - async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度 | |
| 19 | - initial-size: 1 # 单元测试,配置为 1,提升启动速度 | |
| 12 | + url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.name}?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT | |
| 13 | + driver-class-name: com.mysql.jdbc.Driver | |
| 14 | + username: root | |
| 15 | + password: 123456 | |
| 20 | 16 | |
| 21 | 17 | # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 |
| 22 | 18 | redis: |
| 23 | 19 | host: 127.0.0.1 # 地址 |
| 24 | - port: 6379 # 端口(单元测试,使用 16379 端口) | |
| 20 | + port: 6379 # 端口 | |
| 25 | 21 | database: 0 # 数据库索引 |
| 26 | 22 | |
| 27 | 23 | mybatis: | ... | ... |
src/test/java/cn/iocoder/dashboard/BaseMockitoUnitTest.java
0 → 100644
| 1 | +package cn.iocoder.dashboard; | |
| 2 | + | |
| 3 | +import org.junit.jupiter.api.extension.ExtendWith; | |
| 4 | +import org.mockito.junit.jupiter.MockitoExtension; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * 纯 Mockito 的单元测试 | |
| 8 | + * | |
| 9 | + * @author 芋道源码 | |
| 10 | + */ | |
| 11 | +@ExtendWith(MockitoExtension.class) | |
| 12 | +public class BaseMockitoUnitTest { | |
| 13 | +} | ... | ... |
src/test/java/cn/iocoder/dashboard/framework/package-info.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework; | ... | ... |
src/test/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClientTest.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.ReflectUtil; | |
| 4 | +import cn.iocoder.dashboard.BaseMockitoUnitTest; | |
| 5 | +import cn.iocoder.dashboard.common.core.KeyValue; | |
| 6 | +import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; | |
| 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.enums.SmsTemplateAuditStatusEnum; | |
| 12 | +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; | |
| 13 | +import cn.iocoder.dashboard.util.collection.MapUtils; | |
| 14 | +import cn.iocoder.dashboard.util.date.DateUtils; | |
| 15 | +import com.aliyuncs.AcsRequest; | |
| 16 | +import com.aliyuncs.IAcsClient; | |
| 17 | +import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateRequest; | |
| 18 | +import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateResponse; | |
| 19 | +import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; | |
| 20 | +import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse; | |
| 21 | +import com.aliyuncs.exceptions.ClientException; | |
| 22 | +import com.google.common.collect.Lists; | |
| 23 | +import org.junit.jupiter.api.Test; | |
| 24 | +import org.mockito.ArgumentMatcher; | |
| 25 | +import org.mockito.InjectMocks; | |
| 26 | +import org.mockito.Mock; | |
| 27 | + | |
| 28 | +import java.util.List; | |
| 29 | +import java.util.function.Function; | |
| 30 | + | |
| 31 | +import static cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR; | |
| 32 | +import static cn.iocoder.dashboard.util.RandomUtils.*; | |
| 33 | +import static cn.iocoder.dashboard.util.json.JsonUtils.toJsonString; | |
| 34 | +import static org.junit.jupiter.api.Assertions.*; | |
| 35 | +import static org.mockito.ArgumentMatchers.any; | |
| 36 | +import static org.mockito.ArgumentMatchers.argThat; | |
| 37 | +import static org.mockito.Mockito.when; | |
| 38 | + | |
| 39 | +/** | |
| 40 | + * {@link AliyunSmsClient} 的单元测试 | |
| 41 | + * | |
| 42 | + * @author 芋道源码 | |
| 43 | + */ | |
| 44 | +public class AliyunSmsClientTest extends BaseMockitoUnitTest { | |
| 45 | + | |
| 46 | + private final SmsChannelProperties properties = new SmsChannelProperties() | |
| 47 | + .setApiKey(randomString()) // 随机一个 apiKey,避免构建报错 | |
| 48 | + .setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错 | |
| 49 | + .setSignature("芋道源码"); | |
| 50 | + | |
| 51 | + @InjectMocks | |
| 52 | + private final AliyunSmsClient smsClient = new AliyunSmsClient(properties); | |
| 53 | + | |
| 54 | + @Mock | |
| 55 | + private IAcsClient client; | |
| 56 | + | |
| 57 | + @Test | |
| 58 | + public void testDoInit() { | |
| 59 | + // 准备参数 | |
| 60 | + // mock 方法 | |
| 61 | + | |
| 62 | + // 调用 | |
| 63 | + smsClient.doInit(); | |
| 64 | + // 断言 | |
| 65 | + assertNotSame(client, ReflectUtil.getFieldValue(smsClient, "acsClient")); | |
| 66 | + } | |
| 67 | + | |
| 68 | + @Test | |
| 69 | + @SuppressWarnings("unchecked") | |
| 70 | + public void testDoSendSms() throws ClientException { | |
| 71 | + // 准备参数 | |
| 72 | + Long sendLogId = randomLongId(); | |
| 73 | + String mobile = randomString(); | |
| 74 | + String apiTemplateId = randomString(); | |
| 75 | + List<KeyValue<String, Object>> templateParams = Lists.newArrayList( | |
| 76 | + new KeyValue<>("code", 1234), new KeyValue<>("op", "login")); | |
| 77 | + // mock 方法 | |
| 78 | + SendSmsResponse response = randomPojo(SendSmsResponse.class, o -> o.setCode("OK")); | |
| 79 | + when(client.getAcsResponse(argThat((ArgumentMatcher<SendSmsRequest>) acsRequest -> { | |
| 80 | + assertEquals(mobile, acsRequest.getPhoneNumbers()); | |
| 81 | + assertEquals(properties.getSignature(), acsRequest.getSignName()); | |
| 82 | + assertEquals(apiTemplateId, acsRequest.getTemplateCode()); | |
| 83 | + assertEquals(toJsonString(MapUtils.convertMap(templateParams)), acsRequest.getTemplateParam()); | |
| 84 | + assertEquals(sendLogId.toString(), acsRequest.getOutId()); | |
| 85 | + return true; | |
| 86 | + }))).thenReturn(response); | |
| 87 | + | |
| 88 | + // 调用 | |
| 89 | + SmsCommonResult<SmsSendRespDTO> result = smsClient.doSendSms(sendLogId, mobile, | |
| 90 | + apiTemplateId, templateParams); | |
| 91 | + // 断言 | |
| 92 | + assertEquals(response.getCode(), result.getApiCode()); | |
| 93 | + assertEquals(response.getMessage(), result.getApiMsg()); | |
| 94 | + assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode()); | |
| 95 | + assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg()); | |
| 96 | + assertEquals(response.getRequestId(), result.getApiRequestId()); | |
| 97 | + // 断言结果 | |
| 98 | + assertEquals(response.getBizId(), result.getData().getSerialNo()); | |
| 99 | + } | |
| 100 | + | |
| 101 | + @Test | |
| 102 | + public void testDoTParseSmsReceiveStatus() throws Throwable { | |
| 103 | + // 准备参数 | |
| 104 | + String text = "[\n" + | |
| 105 | + " {\n" + | |
| 106 | + " \"phone_number\" : \"13900000001\",\n" + | |
| 107 | + " \"send_time\" : \"2017-01-01 11:12:13\",\n" + | |
| 108 | + " \"report_time\" : \"2017-02-02 22:23:24\",\n" + | |
| 109 | + " \"success\" : true,\n" + | |
| 110 | + " \"err_code\" : \"DELIVERED\",\n" + | |
| 111 | + " \"err_msg\" : \"用户接收成功\",\n" + | |
| 112 | + " \"sms_size\" : \"1\",\n" + | |
| 113 | + " \"biz_id\" : \"12345\",\n" + | |
| 114 | + " \"out_id\" : \"67890\"\n" + | |
| 115 | + " }\n" + | |
| 116 | + "]"; | |
| 117 | + // mock 方法 | |
| 118 | + | |
| 119 | + // 调用 | |
| 120 | + List<SmsReceiveRespDTO> statuses = smsClient.doParseSmsReceiveStatus(text); | |
| 121 | + // 断言 | |
| 122 | + assertEquals(1, statuses.size()); | |
| 123 | + assertTrue(statuses.get(0).getSuccess()); | |
| 124 | + assertEquals("DELIVERED", statuses.get(0).getErrorCode()); | |
| 125 | + assertEquals("用户接收成功", statuses.get(0).getErrorMsg()); | |
| 126 | + assertEquals("13900000001", statuses.get(0).getMobile()); | |
| 127 | + assertEquals(DateUtils.buildTime(2017, 2, 2, 22, 23, 24), statuses.get(0).getReceiveTime()); | |
| 128 | + assertEquals("12345", statuses.get(0).getSerialNo()); | |
| 129 | + assertEquals(67890L, statuses.get(0).getLogId()); | |
| 130 | + } | |
| 131 | + | |
| 132 | + @Test | |
| 133 | + public void testDoGetSmsTemplate() throws ClientException { | |
| 134 | + // 准备参数 | |
| 135 | + String apiTemplateId = randomString(); | |
| 136 | + // mock 方法 | |
| 137 | + QuerySmsTemplateResponse response = randomPojo(QuerySmsTemplateResponse.class, o -> { | |
| 138 | + o.setCode("OK"); | |
| 139 | + o.setTemplateStatus(1); // 设置模板通过 | |
| 140 | + }); | |
| 141 | + when(client.getAcsResponse(argThat((ArgumentMatcher<QuerySmsTemplateRequest>) acsRequest -> { | |
| 142 | + assertEquals(apiTemplateId, acsRequest.getTemplateCode()); | |
| 143 | + return true; | |
| 144 | + }))).thenReturn(response); | |
| 145 | + | |
| 146 | + // 调用 | |
| 147 | + SmsCommonResult<SmsTemplateRespDTO> result = smsClient.doGetSmsTemplate(apiTemplateId); | |
| 148 | + // 断言 | |
| 149 | + assertEquals(response.getCode(), result.getApiCode()); | |
| 150 | + assertEquals(response.getMessage(), result.getApiMsg()); | |
| 151 | + assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode()); | |
| 152 | + assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg()); | |
| 153 | + assertEquals(response.getRequestId(), result.getApiRequestId()); | |
| 154 | + // 断言结果 | |
| 155 | + assertEquals(response.getTemplateCode(), result.getData().getId()); | |
| 156 | + assertEquals(response.getTemplateContent(), result.getData().getContent()); | |
| 157 | + assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getData().getAuditStatus()); | |
| 158 | + assertEquals(response.getReason(), result.getData().getAuditReason()); | |
| 159 | + } | |
| 160 | + | |
| 161 | + @Test | |
| 162 | + public void testConvertSmsTemplateAuditStatus() { | |
| 163 | + assertEquals(SmsTemplateAuditStatusEnum.CHECKING.getStatus(), | |
| 164 | + smsClient.convertSmsTemplateAuditStatus(0)); | |
| 165 | + assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), | |
| 166 | + smsClient.convertSmsTemplateAuditStatus(1)); | |
| 167 | + assertEquals(SmsTemplateAuditStatusEnum.FAIL.getStatus(), | |
| 168 | + smsClient.convertSmsTemplateAuditStatus(2)); | |
| 169 | + assertThrows(IllegalArgumentException.class, () -> smsClient.convertSmsTemplateAuditStatus(3), | |
| 170 | + "未知审核状态(3)"); | |
| 171 | + } | |
| 172 | + | |
| 173 | + @Test | |
| 174 | + @SuppressWarnings("unchecked") | |
| 175 | + public void testInvoke_throwable() throws ClientException { | |
| 176 | + // 准备参数 | |
| 177 | + QuerySmsTemplateRequest request = new QuerySmsTemplateRequest(); | |
| 178 | + // mock 方法 | |
| 179 | + ClientException ex = new ClientException("isv.INVALID_PARAMETERS", "参数不正确", randomString()); | |
| 180 | + when(client.getAcsResponse(any(AcsRequest.class))).thenThrow(ex); | |
| 181 | + | |
| 182 | + // 调用,并断言异常 | |
| 183 | + SmsCommonResult<?> result = smsClient.invoke(request,null); | |
| 184 | + // 断言 | |
| 185 | + assertEquals(ex.getErrCode(), result.getApiCode()); | |
| 186 | + assertEquals(ex.getErrMsg(), result.getApiMsg()); | |
| 187 | + assertEquals(SMS_API_PARAM_ERROR.getCode(), result.getCode()); | |
| 188 | + assertEquals(SMS_API_PARAM_ERROR.getMsg(), result.getMsg()); | |
| 189 | + assertEquals(ex.getRequestId(), result.getApiRequestId()); | |
| 190 | + } | |
| 191 | + | |
| 192 | + @Test | |
| 193 | + public void testInvoke_success() throws ClientException { | |
| 194 | + // 准备参数 | |
| 195 | + QuerySmsTemplateRequest request = new QuerySmsTemplateRequest(); | |
| 196 | + Function<QuerySmsTemplateResponse, SmsTemplateRespDTO> responseConsumer = response -> { | |
| 197 | + SmsTemplateRespDTO data = new SmsTemplateRespDTO(); | |
| 198 | + data.setId(response.getTemplateCode()).setContent(response.getTemplateContent()); | |
| 199 | + data.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason(response.getReason()); | |
| 200 | + return data; | |
| 201 | + }; | |
| 202 | + // mock 方法 | |
| 203 | + QuerySmsTemplateResponse response = randomPojo(QuerySmsTemplateResponse.class, o -> { | |
| 204 | + o.setCode("OK"); | |
| 205 | + o.setTemplateStatus(1); // 设置模板通过 | |
| 206 | + }); | |
| 207 | + when(client.getAcsResponse(any(AcsRequest.class))).thenReturn(response); | |
| 208 | + | |
| 209 | + // 调用 | |
| 210 | + SmsCommonResult<SmsTemplateRespDTO> result = smsClient.invoke(request, responseConsumer); | |
| 211 | + // 断言 | |
| 212 | + assertEquals(response.getCode(), result.getApiCode()); | |
| 213 | + assertEquals(response.getMessage(), result.getApiMsg()); | |
| 214 | + assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode()); | |
| 215 | + assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg()); | |
| 216 | + assertEquals(response.getRequestId(), result.getApiRequestId()); | |
| 217 | + // 断言结果 | |
| 218 | + assertEquals(response.getTemplateCode(), result.getData().getId()); | |
| 219 | + assertEquals(response.getTemplateContent(), result.getData().getContent()); | |
| 220 | + assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getData().getAuditStatus()); | |
| 221 | + assertEquals(response.getReason(), result.getData().getAuditReason()); | |
| 222 | + } | |
| 223 | + | |
| 224 | +} | ... | ... |
src/test/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsCodeMappingTest.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.BaseMockitoUnitTest; | |
| 4 | +import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; | |
| 5 | +import cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants; | |
| 6 | +import org.junit.jupiter.api.Test; | |
| 7 | +import org.mockito.InjectMocks; | |
| 8 | + | |
| 9 | +import static org.junit.jupiter.api.Assertions.*; | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * {@link AliyunSmsCodeMapping} 的单元测试 | |
| 13 | + * | |
| 14 | + * @author 芋道源码 | |
| 15 | + */ | |
| 16 | +public class AliyunSmsCodeMappingTest extends BaseMockitoUnitTest { | |
| 17 | + | |
| 18 | + @InjectMocks | |
| 19 | + private AliyunSmsCodeMapping codeMapping; | |
| 20 | + | |
| 21 | + @Test | |
| 22 | + public void testApply() { | |
| 23 | + assertEquals(GlobalErrorCodeConstants.SUCCESS, codeMapping.apply("OK")); | |
| 24 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID, codeMapping.apply("MissingAccessKeyId")); | |
| 25 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID, codeMapping.apply("isv.ACCOUNT_NOT_EXISTS")); | |
| 26 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID, codeMapping.apply("isv.ACCOUNT_ABNORMAL")); | |
| 27 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_DAY_LIMIT_CONTROL, codeMapping.apply("isv.DAY_LIMIT_CONTROL")); | |
| 28 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_CONTENT_INVALID, codeMapping.apply("isv.SMS_CONTENT_ILLEGAL")); | |
| 29 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply("isv.SMS_SIGN_ILLEGAL")); | |
| 30 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply("isv.SIGN_NAME_ILLEGAL")); | |
| 31 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY, codeMapping.apply("isp.RAM_PERMISSION_DENY")); | |
| 32 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_MONEY_NOT_ENOUGH, codeMapping.apply("isv.OUT_OF_SERVICE")); | |
| 33 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_MONEY_NOT_ENOUGH, codeMapping.apply("isv.AMOUNT_NOT_ENOUGH")); | |
| 34 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_INVALID, codeMapping.apply("isv.SMS_TEMPLATE_ILLEGAL")); | |
| 35 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply("isv.SMS_SIGNATURE_ILLEGAL")); | |
| 36 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR, codeMapping.apply("isv.INVALID_PARAMETERS")); | |
| 37 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR, codeMapping.apply("isv.INVALID_JSON_PARAM")); | |
| 38 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_MOBILE_INVALID, codeMapping.apply("isv.MOBILE_NUMBER_ILLEGAL")); | |
| 39 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_PARAM_ERROR, codeMapping.apply("isv.TEMPLATE_MISSING_PARAMETERS")); | |
| 40 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply("isv.BUSINESS_LIMIT_CONTROL")); | |
| 41 | + } | |
| 42 | + | |
| 43 | +} | ... | ... |
src/test/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClientTest.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.ReflectUtil; | |
| 4 | +import cn.iocoder.dashboard.BaseMockitoUnitTest; | |
| 5 | +import cn.iocoder.dashboard.common.core.KeyValue; | |
| 6 | +import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; | |
| 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.enums.SmsTemplateAuditStatusEnum; | |
| 12 | +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; | |
| 13 | +import cn.iocoder.dashboard.util.date.DateUtils; | |
| 14 | +import com.google.common.collect.Lists; | |
| 15 | +import com.yunpian.sdk.YunpianClient; | |
| 16 | +import com.yunpian.sdk.api.SmsApi; | |
| 17 | +import com.yunpian.sdk.api.TplApi; | |
| 18 | +import com.yunpian.sdk.constant.YunpianConstant; | |
| 19 | +import com.yunpian.sdk.model.Result; | |
| 20 | +import com.yunpian.sdk.model.SmsSingleSend; | |
| 21 | +import com.yunpian.sdk.model.Template; | |
| 22 | +import org.junit.jupiter.api.Test; | |
| 23 | +import org.mockito.InjectMocks; | |
| 24 | +import org.mockito.Mock; | |
| 25 | + | |
| 26 | +import java.util.HashMap; | |
| 27 | +import java.util.List; | |
| 28 | +import java.util.Map; | |
| 29 | +import java.util.function.Function; | |
| 30 | +import java.util.function.Supplier; | |
| 31 | + | |
| 32 | +import static cn.iocoder.dashboard.util.RandomUtils.*; | |
| 33 | +import static com.yunpian.sdk.constant.Code.OK; | |
| 34 | +import static org.junit.jupiter.api.Assertions.*; | |
| 35 | +import static org.mockito.Mockito.*; | |
| 36 | + | |
| 37 | +/** | |
| 38 | + * 对 {@link YunpianSmsClient} 的单元测试 | |
| 39 | + * | |
| 40 | + * @author 芋道源码 | |
| 41 | + */ | |
| 42 | +public class YunpianSmsClientTest extends BaseMockitoUnitTest { | |
| 43 | + | |
| 44 | + private final SmsChannelProperties properties = new SmsChannelProperties() | |
| 45 | + .setApiKey(randomString()); // 随机一个 apiKey,避免构建报错 | |
| 46 | + | |
| 47 | + @InjectMocks | |
| 48 | + private final YunpianSmsClient smsClient = new YunpianSmsClient(properties); | |
| 49 | + | |
| 50 | + @Mock | |
| 51 | + private YunpianClient client; | |
| 52 | + | |
| 53 | + @Test | |
| 54 | + public void testDoInit() { | |
| 55 | + // 准备参数 | |
| 56 | + // mock 方法 | |
| 57 | + | |
| 58 | + // 调用 | |
| 59 | + smsClient.doInit(); | |
| 60 | + // 断言 | |
| 61 | + assertNotEquals(client, ReflectUtil.getFieldValue(smsClient, "client")); | |
| 62 | + verify(client, times(1)).close(); | |
| 63 | + } | |
| 64 | + | |
| 65 | + @Test | |
| 66 | + @SuppressWarnings("unchecked") | |
| 67 | + public void testDoSendSms() throws Throwable { | |
| 68 | + // 准备参数 | |
| 69 | + Long sendLogId = randomLongId(); | |
| 70 | + String mobile = randomString(); | |
| 71 | + String apiTemplateId = randomString(); | |
| 72 | + List<KeyValue<String, Object>> templateParams = Lists.newArrayList( | |
| 73 | + new KeyValue<>("code", 1234), new KeyValue<>("op", "login")); | |
| 74 | + // mock sms 方法 | |
| 75 | + SmsApi smsApi = mock(SmsApi.class); | |
| 76 | + when(client.sms()).thenReturn(smsApi); | |
| 77 | + // mock tpl_single_send 方法 | |
| 78 | + Map<String, String> request = new HashMap<>(); | |
| 79 | + request.put(YunpianConstant.MOBILE, mobile); | |
| 80 | + request.put(YunpianConstant.TPL_ID, apiTemplateId); | |
| 81 | + request.put(YunpianConstant.TPL_VALUE, "#code#=1234&#op#=login"); | |
| 82 | + request.put(YunpianConstant.UID, String.valueOf(sendLogId)); | |
| 83 | + request.put(YunpianConstant.CALLBACK_URL, properties.getCallbackUrl()); | |
| 84 | + Result<SmsSingleSend> responseResult = randomPojo(Result.class, SmsSingleSend.class, | |
| 85 | + o -> o.setCode(OK)); // API 发送成功的 code | |
| 86 | + when(smsApi.tpl_single_send(eq(request))).thenReturn(responseResult); | |
| 87 | + | |
| 88 | + // 调用 | |
| 89 | + SmsCommonResult<SmsSendRespDTO> result = smsClient.doSendSms(sendLogId, mobile, | |
| 90 | + apiTemplateId, templateParams); | |
| 91 | + // 断言 | |
| 92 | + assertEquals(String.valueOf(responseResult.getCode()), result.getApiCode()); | |
| 93 | + assertEquals(responseResult.getMsg() + " => " + responseResult.getDetail(), result.getApiMsg()); | |
| 94 | + assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode()); | |
| 95 | + assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg()); | |
| 96 | + assertNull(result.getApiRequestId()); | |
| 97 | + // 断言结果 | |
| 98 | + assertEquals(String.valueOf(responseResult.getData().getSid()), result.getData().getSerialNo()); | |
| 99 | + } | |
| 100 | + | |
| 101 | + @Test | |
| 102 | + public void testDoParseSmsReceiveStatus() throws Throwable { | |
| 103 | + // 准备参数 | |
| 104 | + String text = "[{\"sid\":9527,\"uid\":1024,\"user_receive_time\":\"2014-03-17 22:55:21\",\"error_msg\":\"\",\"mobile\":\"15205201314\",\"report_status\":\"SUCCESS\"}]"; | |
| 105 | + // mock 方法 | |
| 106 | + | |
| 107 | + // 调用 | |
| 108 | + | |
| 109 | + // 断言 | |
| 110 | + // 调用 | |
| 111 | + List<SmsReceiveRespDTO> statuses = smsClient.doParseSmsReceiveStatus(text); | |
| 112 | + // 断言 | |
| 113 | + assertEquals(1, statuses.size()); | |
| 114 | + assertTrue(statuses.get(0).getSuccess()); | |
| 115 | + assertEquals("", statuses.get(0).getErrorCode()); | |
| 116 | + assertNull(statuses.get(0).getErrorMsg()); | |
| 117 | + assertEquals("15205201314", statuses.get(0).getMobile()); | |
| 118 | + assertEquals(DateUtils.buildTime(2014, 3, 17, 22, 55, 21), statuses.get(0).getReceiveTime()); | |
| 119 | + assertEquals("9527", statuses.get(0).getSerialNo()); | |
| 120 | + assertEquals(1024L, statuses.get(0).getLogId()); | |
| 121 | + } | |
| 122 | + | |
| 123 | + @Test | |
| 124 | + @SuppressWarnings("unchecked") | |
| 125 | + public void testDoGetSmsTemplate() throws Throwable { | |
| 126 | + // 准备参数 | |
| 127 | + String apiTemplateId = randomString(); | |
| 128 | + // mock tpl 方法 | |
| 129 | + TplApi tplApi = mock(TplApi.class); | |
| 130 | + when(client.tpl()).thenReturn(tplApi); | |
| 131 | + // mock get 方法 | |
| 132 | + Map<String, String> request = new HashMap<>(); | |
| 133 | + request.put(YunpianConstant.APIKEY, properties.getApiKey()); | |
| 134 | + request.put(YunpianConstant.TPL_ID, apiTemplateId); | |
| 135 | + Result<List<Template>> responseResult = randomPojo(Result.class, List.class, o -> { | |
| 136 | + o.setCode(OK); // API 发送成功的 code | |
| 137 | + o.setData(randomPojoList(Template.class, t -> t.setCheck_status("SUCCESS"))); | |
| 138 | + }); | |
| 139 | + when(tplApi.get(eq(request))).thenReturn(responseResult); | |
| 140 | + | |
| 141 | + // 调用 | |
| 142 | + SmsCommonResult<SmsTemplateRespDTO> result = smsClient.doGetSmsTemplate(apiTemplateId); | |
| 143 | + // 断言 | |
| 144 | + assertEquals(String.valueOf(responseResult.getCode()), result.getApiCode()); | |
| 145 | + assertEquals(responseResult.getMsg() + " => " + responseResult.getDetail(), result.getApiMsg()); | |
| 146 | + assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode()); | |
| 147 | + assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg()); | |
| 148 | + assertNull(result.getApiRequestId()); | |
| 149 | + // 断言结果 | |
| 150 | + Template template = responseResult.getData().get(0); | |
| 151 | + assertEquals(template.getTpl_id().toString(), result.getData().getId()); | |
| 152 | + assertEquals(template.getTpl_content(), result.getData().getContent()); | |
| 153 | + assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getData().getAuditStatus()); | |
| 154 | + assertEquals(template.getReason(), result.getData().getAuditReason()); | |
| 155 | + } | |
| 156 | + | |
| 157 | + @Test | |
| 158 | + public void testConvertSmsTemplateAuditStatus() { | |
| 159 | + assertEquals(SmsTemplateAuditStatusEnum.CHECKING.getStatus(), | |
| 160 | + smsClient.convertSmsTemplateAuditStatus("CHECKING")); | |
| 161 | + assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), | |
| 162 | + smsClient.convertSmsTemplateAuditStatus("SUCCESS")); | |
| 163 | + assertEquals(SmsTemplateAuditStatusEnum.FAIL.getStatus(), | |
| 164 | + smsClient.convertSmsTemplateAuditStatus("FAIL")); | |
| 165 | + assertThrows(IllegalArgumentException.class, () -> smsClient.convertSmsTemplateAuditStatus("test"), | |
| 166 | + "未知审核状态(test)"); | |
| 167 | + } | |
| 168 | + | |
| 169 | + @Test | |
| 170 | + public void testInvoke_throwable() { | |
| 171 | + // 准备参数 | |
| 172 | + Supplier<Result<Object>> requestConsumer = | |
| 173 | + () -> new Result<>().setThrowable(new NullPointerException()); | |
| 174 | + // mock 方法 | |
| 175 | + | |
| 176 | + // 调用,并断言异常 | |
| 177 | + assertThrows(NullPointerException.class, | |
| 178 | + () -> smsClient.invoke(requestConsumer, null)); | |
| 179 | + } | |
| 180 | + | |
| 181 | + @Test | |
| 182 | + @SuppressWarnings("unchecked") | |
| 183 | + public void testInvoke_success() throws Throwable { | |
| 184 | + // 准备参数 | |
| 185 | + Result<SmsSingleSend> responseResult = randomPojo(Result.class, SmsSingleSend.class, o -> o.setCode(OK)); | |
| 186 | + Supplier<Result<SmsSingleSend>> requestConsumer = () -> responseResult; | |
| 187 | + Function<SmsSingleSend, SmsSendRespDTO> responseConsumer = | |
| 188 | + smsSingleSend -> new SmsSendRespDTO().setSerialNo(String.valueOf(responseResult.getData().getSid())); | |
| 189 | + // mock 方法 | |
| 190 | + | |
| 191 | + // 调用 | |
| 192 | + SmsCommonResult<SmsSendRespDTO> result = smsClient.invoke(requestConsumer, responseConsumer); | |
| 193 | + // 断言 | |
| 194 | + assertEquals(String.valueOf(responseResult.getCode()), result.getApiCode()); | |
| 195 | + assertEquals(responseResult.getMsg() + " => " + responseResult.getDetail(), result.getApiMsg()); | |
| 196 | + assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode()); | |
| 197 | + assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg()); | |
| 198 | + assertNull(result.getApiRequestId()); | |
| 199 | + assertEquals(String.valueOf(responseResult.getData().getSid()), result.getData().getSerialNo()); | |
| 200 | + } | |
| 201 | + | |
| 202 | +} | ... | ... |
src/test/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsCodeMappingTest.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.BaseMockitoUnitTest; | |
| 4 | +import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; | |
| 5 | +import cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants; | |
| 6 | +import org.junit.jupiter.api.Test; | |
| 7 | +import org.mockito.InjectMocks; | |
| 8 | + | |
| 9 | +import static com.yunpian.sdk.constant.Code.*; | |
| 10 | +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| 11 | + | |
| 12 | +/** | |
| 13 | + * {@link YunpianSmsCodeMapping} 的单元测试 | |
| 14 | + * | |
| 15 | + * @author 芋道源码 | |
| 16 | + */ | |
| 17 | +class YunpianSmsCodeMappingTest extends BaseMockitoUnitTest { | |
| 18 | + | |
| 19 | + @InjectMocks | |
| 20 | + private YunpianSmsCodeMapping codeMapping; | |
| 21 | + | |
| 22 | + @Test | |
| 23 | + public void testApply() { | |
| 24 | + assertEquals(GlobalErrorCodeConstants.SUCCESS, codeMapping.apply(String.valueOf(OK))); | |
| 25 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR, codeMapping.apply(String.valueOf(ARGUMENT_MISSING))); | |
| 26 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_PARAM_ERROR, codeMapping.apply(String.valueOf(BAD_ARGUMENT_FORMAT))); | |
| 27 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_MONEY_NOT_ENOUGH, codeMapping.apply(String.valueOf(MONEY_NOT_ENOUGH))); | |
| 28 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_INVALID, codeMapping.apply(String.valueOf(TPL_NOT_FOUND))); | |
| 29 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_INVALID, codeMapping.apply(String.valueOf(TPL_NOT_VALID))); | |
| 30 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply(String.valueOf(DUP_IN_SHORT_TIME))); | |
| 31 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply(String.valueOf(TOO_MANY_TIME_IN_5))); | |
| 32 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply(String.valueOf(DAY_LIMIT_PER_MOBILE))); | |
| 33 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply(String.valueOf(HOUR_LIMIT_PER_MOBILE))); | |
| 34 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_MOBILE_BLACK, codeMapping.apply(String.valueOf(BLACK_PHONE_FILTER))); | |
| 35 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply(String.valueOf(SIGN_NOT_MATCH))); | |
| 36 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply(String.valueOf(SIGN_NOT_VALID))); | |
| 37 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply(String.valueOf(BAD_SIGN_FORMAT))); | |
| 38 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID, codeMapping.apply(String.valueOf(BAD_API_KEY))); | |
| 39 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY, codeMapping.apply(String.valueOf(API_NOT_ALLOWED))); | |
| 40 | + assertEquals(SmsFrameworkErrorCodeConstants.SMS_IP_DENY, codeMapping.apply(String.valueOf(IP_NOT_ALLOWED))); | |
| 41 | + } | |
| 42 | + | |
| 43 | +} | ... | ... |
src/test/java/cn/iocoder/dashboard/modules/system/service/auth/SysUserSessionServiceImplTest.java
| 1 | 1 | package cn.iocoder.dashboard.modules.system.service.auth; |
| 2 | 2 | |
| 3 | 3 | import static cn.hutool.core.util.RandomUtil.randomEle; |
| 4 | -import static cn.iocoder.dashboard.modules.system.dal.redis.SysRedisKeyConstants.LOGIN_USER; | |
| 5 | 4 | import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals; |
| 6 | 5 | import static cn.iocoder.dashboard.util.RandomUtils.randomDate; |
| 7 | 6 | import static cn.iocoder.dashboard.util.RandomUtils.randomLongId; |
| ... | ... | @@ -25,7 +24,6 @@ import javax.annotation.Resource; |
| 25 | 24 | import org.junit.jupiter.api.Test; |
| 26 | 25 | import org.springframework.boot.test.mock.mockito.MockBean; |
| 27 | 26 | import org.springframework.context.annotation.Import; |
| 28 | -import org.springframework.data.redis.core.StringRedisTemplate; | |
| 29 | 27 | |
| 30 | 28 | import cn.hutool.core.date.DateUtil; |
| 31 | 29 | import cn.iocoder.dashboard.BaseDbAndRedisUnitTest; |
| ... | ... | @@ -33,10 +31,6 @@ import cn.iocoder.dashboard.common.enums.CommonStatusEnum; |
| 33 | 31 | import cn.iocoder.dashboard.common.pojo.PageResult; |
| 34 | 32 | import cn.iocoder.dashboard.framework.security.config.SecurityProperties; |
| 35 | 33 | import cn.iocoder.dashboard.framework.security.core.LoginUser; |
| 36 | -import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobPageReqVO; | |
| 37 | -import cn.iocoder.dashboard.modules.infra.dal.dataobject.job.InfJobDO; | |
| 38 | -import cn.iocoder.dashboard.modules.infra.enums.config.InfConfigTypeEnum; | |
| 39 | -import cn.iocoder.dashboard.modules.infra.enums.job.InfJobStatusEnum; | |
| 40 | 34 | import cn.iocoder.dashboard.modules.system.controller.auth.vo.session.SysUserSessionPageReqVO; |
| 41 | 35 | import cn.iocoder.dashboard.modules.system.dal.dataobject.auth.SysUserSessionDO; |
| 42 | 36 | import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO; |
| ... | ... | @@ -50,7 +44,6 @@ import cn.iocoder.dashboard.modules.system.service.logger.impl.SysLoginLogServic |
| 50 | 44 | import cn.iocoder.dashboard.modules.system.service.user.SysUserServiceImpl; |
| 51 | 45 | import cn.iocoder.dashboard.util.AssertUtils; |
| 52 | 46 | import cn.iocoder.dashboard.util.RandomUtils; |
| 53 | -import cn.iocoder.dashboard.util.json.JsonUtils; | |
| 54 | 47 | import cn.iocoder.dashboard.util.object.ObjectUtils; |
| 55 | 48 | |
| 56 | 49 | /** |
| ... | ... | @@ -167,12 +160,12 @@ public class SysUserSessionServiceImplTest extends BaseDbAndRedisUnitTest { |
| 167 | 160 | String userIp = randomString(); |
| 168 | 161 | SysUserDO dbUser1 = randomPojo(SysUserDO.class, o -> { |
| 169 | 162 | o.setUsername("testUsername1"); |
| 170 | - o.setSex(randomEle(SysSexEnum.values()).getSEX()); | |
| 163 | + o.setSex(randomEle(SysSexEnum.values()).getSex()); | |
| 171 | 164 | o.setStatus(CommonStatusEnum.ENABLE.getStatus()); |
| 172 | 165 | }); |
| 173 | 166 | SysUserDO dbUser2 = randomPojo(SysUserDO.class, o -> { |
| 174 | 167 | o.setUsername("testUsername2"); |
| 175 | - o.setSex(randomEle(SysSexEnum.values()).getSEX()); | |
| 168 | + o.setSex(randomEle(SysSexEnum.values()).getSex()); | |
| 176 | 169 | o.setStatus(CommonStatusEnum.ENABLE.getStatus()); |
| 177 | 170 | }); |
| 178 | 171 | SysUserSessionDO dbSession = randomPojo(SysUserSessionDO.class, o -> { | ... | ... |
src/test/java/cn/iocoder/dashboard/modules/system/service/dept/SysDeptServiceTest.java
src/test/java/cn/iocoder/dashboard/modules/system/service/logger/SysOperateLogServiceImplTest.java
| ... | ... | @@ -68,7 +68,7 @@ public class SysOperateLogServiceImplTest extends BaseDbUnitTest { |
| 68 | 68 | // 先构造用户 |
| 69 | 69 | SysUserDO user = RandomUtils.randomPojo(SysUserDO.class, o -> { |
| 70 | 70 | o.setNickname("wangkai"); |
| 71 | - o.setSex(SysSexEnum.MALE.getSEX()); | |
| 71 | + o.setSex(SysSexEnum.MALE.getSex()); | |
| 72 | 72 | o.setStatus(CommonStatusEnum.ENABLE.getStatus()); |
| 73 | 73 | }); |
| 74 | 74 | when(userService.getUsersByNickname("wangkai")).thenReturn(Collections.singletonList(user)); |
| ... | ... | @@ -119,7 +119,7 @@ public class SysOperateLogServiceImplTest extends BaseDbUnitTest { |
| 119 | 119 | // 先构造用户 |
| 120 | 120 | SysUserDO user = RandomUtils.randomPojo(SysUserDO.class, o -> { |
| 121 | 121 | o.setNickname("wangkai"); |
| 122 | - o.setSex(SysSexEnum.MALE.getSEX()); | |
| 122 | + o.setSex(SysSexEnum.MALE.getSex()); | |
| 123 | 123 | o.setStatus(CommonStatusEnum.ENABLE.getStatus()); |
| 124 | 124 | }); |
| 125 | 125 | when(userService.getUsersByNickname("wangkai")).thenReturn(Collections.singletonList(user)); | ... | ... |
src/test/java/cn/iocoder/dashboard/modules/system/service/permission/SysMenuServiceTest.java
| ... | ... | @@ -3,6 +3,7 @@ package cn.iocoder.dashboard.modules.system.service.permission; |
| 3 | 3 | import cn.hutool.core.bean.BeanUtil; |
| 4 | 4 | import cn.hutool.core.lang.Assert; |
| 5 | 5 | import cn.iocoder.dashboard.BaseDbUnitTest; |
| 6 | +import cn.iocoder.dashboard.common.enums.CommonStatusEnum; | |
| 6 | 7 | import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuCreateReqVO; |
| 7 | 8 | import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuListReqVO; |
| 8 | 9 | import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuUpdateReqVO; |
| ... | ... | @@ -12,7 +13,6 @@ import cn.iocoder.dashboard.modules.system.enums.permission.MenuTypeEnum; |
| 12 | 13 | import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysMenuProducer; |
| 13 | 14 | import cn.iocoder.dashboard.modules.system.service.permission.impl.SysMenuServiceImpl; |
| 14 | 15 | import cn.iocoder.dashboard.util.AopTargetUtils; |
| 15 | -import cn.iocoder.dashboard.util.AssertUtils; | |
| 16 | 16 | import cn.iocoder.dashboard.util.RandomUtils; |
| 17 | 17 | import cn.iocoder.dashboard.util.object.ObjectUtils; |
| 18 | 18 | import com.google.common.collect.Multimap; |
| ... | ... | @@ -22,7 +22,6 @@ import org.springframework.boot.test.mock.mockito.MockBean; |
| 22 | 22 | import org.springframework.context.annotation.Import; |
| 23 | 23 | |
| 24 | 24 | import javax.annotation.Resource; |
| 25 | - | |
| 26 | 25 | import java.util.*; |
| 27 | 26 | |
| 28 | 27 | import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; |
| ... | ... | @@ -54,18 +53,20 @@ public class SysMenuServiceTest extends BaseDbUnitTest { |
| 54 | 53 | SysMenuDO menuDO2 = createMenuDO(MenuTypeEnum.MENU, "xxxx", 0L); |
| 55 | 54 | menuMapper.insert(menuDO2); |
| 56 | 55 | |
| 57 | - //调用 | |
| 56 | + // 调用 | |
| 58 | 57 | sysMenuService.initLocalCache(); |
| 59 | 58 | |
| 60 | 59 | // 获取代理对象 |
| 61 | 60 | SysMenuServiceImpl target = (SysMenuServiceImpl) AopTargetUtils.getTarget(sysMenuService); |
| 62 | 61 | |
| 63 | - Map<Long, SysMenuDO> menuCache = (Map<Long, SysMenuDO>) BeanUtil.getFieldValue(target, "menuCache"); | |
| 62 | + Map<Long, SysMenuDO> menuCache = | |
| 63 | + (Map<Long, SysMenuDO>) BeanUtil.getFieldValue(target, "menuCache"); | |
| 64 | 64 | Assert.isTrue(menuCache.size() == 2); |
| 65 | 65 | assertPojoEquals(menuDO1, menuCache.get(menuDO1.getId())); |
| 66 | 66 | assertPojoEquals(menuDO2, menuCache.get(menuDO2.getId())); |
| 67 | 67 | |
| 68 | - Multimap<String, SysMenuDO> permissionMenuCache = (Multimap<String, SysMenuDO>) BeanUtil.getFieldValue(target, "permissionMenuCache"); | |
| 68 | + Multimap<String, SysMenuDO> permissionMenuCache = | |
| 69 | + (Multimap<String, SysMenuDO>) BeanUtil.getFieldValue(target, "permissionMenuCache"); | |
| 69 | 70 | Assert.isTrue(permissionMenuCache.size() == 2); |
| 70 | 71 | assertPojoEquals(menuDO1, permissionMenuCache.get(menuDO1.getPermission())); |
| 71 | 72 | assertPojoEquals(menuDO2, permissionMenuCache.get(menuDO2.getPermission())); |
| ... | ... | @@ -186,10 +187,9 @@ public class SysMenuServiceTest extends BaseDbUnitTest { |
| 186 | 187 | |
| 187 | 188 | //断言 |
| 188 | 189 | Assert.isTrue(menuDOS.size() == idMenuMap.size()); |
| 189 | - menuDOS.stream().forEach(m -> assertPojoEquals(idMenuMap.get(m.getId()), m)); | |
| 190 | + menuDOS.forEach(m -> assertPojoEquals(idMenuMap.get(m.getId()), m)); | |
| 190 | 191 | } |
| 191 | 192 | |
| 192 | - | |
| 193 | 193 | @Test |
| 194 | 194 | public void testGetMenusReqVo_success() { |
| 195 | 195 | Map<Long, SysMenuDO> idMenuMap = new HashMap<>(); |
| ... | ... | @@ -220,7 +220,7 @@ public class SysMenuServiceTest extends BaseDbUnitTest { |
| 220 | 220 | |
| 221 | 221 | //断言 |
| 222 | 222 | Assert.isTrue(menuDOS.size() == idMenuMap.size()); |
| 223 | - menuDOS.stream().forEach(m -> assertPojoEquals(idMenuMap.get(m.getId()), m)); | |
| 223 | + menuDOS.forEach(m -> assertPojoEquals(idMenuMap.get(m.getId()), m)); | |
| 224 | 224 | } |
| 225 | 225 | |
| 226 | 226 | @Test |
| ... | ... | @@ -246,12 +246,12 @@ public class SysMenuServiceTest extends BaseDbUnitTest { |
| 246 | 246 | menuDO = createMenuDO(4L, MenuTypeEnum.MENU, "name", 0L, 2); |
| 247 | 247 | mockCacheMap.put(menuDO.getId(), menuDO); |
| 248 | 248 | |
| 249 | - List<SysMenuDO> menuDOS = sysMenuService.listMenusFromCache(Arrays.asList(MenuTypeEnum.MENU.getType()), Arrays.asList(1)); | |
| 249 | + List<SysMenuDO> menuDOS = sysMenuService.listMenusFromCache(Collections.singletonList(MenuTypeEnum.MENU.getType()), | |
| 250 | + Collections.singletonList(CommonStatusEnum.DISABLE.getStatus())); | |
| 250 | 251 | Assert.isTrue(menuDOS.size() == idMenuMap.size()); |
| 251 | - menuDOS.stream().forEach(m -> assertPojoEquals(idMenuMap.get(m.getId()), m)); | |
| 252 | + menuDOS.forEach(m -> assertPojoEquals(idMenuMap.get(m.getId()), m)); | |
| 252 | 253 | } |
| 253 | 254 | |
| 254 | - | |
| 255 | 255 | @Test |
| 256 | 256 | public void testListMenusFromCache2_success() throws Exception { |
| 257 | 257 | Map<Long, SysMenuDO> mockCacheMap = new HashMap<>(); |
| ... | ... | @@ -273,10 +273,10 @@ public class SysMenuServiceTest extends BaseDbUnitTest { |
| 273 | 273 | menuDO = createMenuDO(4L, MenuTypeEnum.MENU, "name", 0L, 2); |
| 274 | 274 | mockCacheMap.put(menuDO.getId(), menuDO); |
| 275 | 275 | |
| 276 | - List<SysMenuDO> menuDOS = sysMenuService.listMenusFromCache(Arrays.asList(1L), | |
| 277 | - Arrays.asList(MenuTypeEnum.MENU.getType()), Arrays.asList(1)); | |
| 276 | + List<SysMenuDO> menuDOS = sysMenuService.listMenusFromCache(Collections.singletonList(1L), | |
| 277 | + Collections.singletonList(MenuTypeEnum.MENU.getType()), Collections.singletonList(1)); | |
| 278 | 278 | Assert.isTrue(menuDOS.size() == idMenuMap.size()); |
| 279 | - menuDOS.stream().forEach(menu -> assertPojoEquals(idMenuMap.get(menu.getId()), menu)); | |
| 279 | + menuDOS.forEach(menu -> assertPojoEquals(idMenuMap.get(menu.getId()), menu)); | |
| 280 | 280 | } |
| 281 | 281 | |
| 282 | 282 | @Test |
| ... | ... | @@ -308,14 +308,14 @@ public class SysMenuServiceTest extends BaseDbUnitTest { |
| 308 | 308 | } |
| 309 | 309 | |
| 310 | 310 | @Test |
| 311 | - public void testCheckResource_success(){ | |
| 312 | - SysMenuDO sonMenu=initParentAndSonMenuDO(); | |
| 313 | - Long parentId=sonMenu.getParentId(); | |
| 311 | + public void testCheckResource_success() { | |
| 312 | + SysMenuDO sonMenu = initParentAndSonMenuDO(); | |
| 313 | + Long parentId = sonMenu.getParentId(); | |
| 314 | 314 | |
| 315 | - Long otherSonMenuId=randomLongId(); | |
| 316 | - String otherSonMenuName=randomString(); | |
| 315 | + Long otherSonMenuId = randomLongId(); | |
| 316 | + String otherSonMenuName = randomString(); | |
| 317 | 317 | |
| 318 | - sysMenuService.checkResource(parentId,otherSonMenuName,otherSonMenuId); | |
| 318 | + sysMenuService.checkResource(parentId, otherSonMenuName, otherSonMenuId); | |
| 319 | 319 | } |
| 320 | 320 | |
| 321 | 321 | @Test |
| ... | ... | @@ -326,7 +326,7 @@ public class SysMenuServiceTest extends BaseDbUnitTest { |
| 326 | 326 | Long otherSonMenuId=randomLongId(); |
| 327 | 327 | String otherSonMenuName=sonMenu.getName(); //相同名称 |
| 328 | 328 | |
| 329 | - assertServiceException(()->sysMenuService.checkResource(parentId,otherSonMenuName,otherSonMenuId), MENU_NAME_DUPLICATE); | |
| 329 | + assertServiceException(() -> sysMenuService.checkResource(parentId, otherSonMenuName, otherSonMenuId), MENU_NAME_DUPLICATE); | |
| 330 | 330 | } |
| 331 | 331 | |
| 332 | 332 | /** |
| ... | ... | @@ -354,15 +354,13 @@ public class SysMenuServiceTest extends BaseDbUnitTest { |
| 354 | 354 | } |
| 355 | 355 | |
| 356 | 356 | private SysMenuDO createMenuDO(Long id, MenuTypeEnum typeEnum, String menuName, Long parentId, Integer status) { |
| 357 | - SysMenuDO sonMenuDO = RandomUtils.randomPojo(SysMenuDO.class, o -> { | |
| 357 | + return RandomUtils.randomPojo(SysMenuDO.class, o -> { | |
| 358 | 358 | o.setId(id); |
| 359 | 359 | o.setParentId(parentId); |
| 360 | 360 | o.setType(typeEnum.getType()); |
| 361 | 361 | o.setStatus(status); |
| 362 | 362 | o.setName(menuName); |
| 363 | 363 | }); |
| 364 | - return sonMenuDO; | |
| 365 | 364 | } |
| 366 | 365 | |
| 367 | - | |
| 368 | 366 | } | ... | ... |
src/test/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsChannelServiceTest.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.sms; | |
| 2 | + | |
| 3 | +import cn.hutool.core.bean.BeanUtil; | |
| 4 | +import cn.iocoder.dashboard.BaseDbUnitTest; | |
| 5 | +import cn.iocoder.dashboard.common.enums.CommonStatusEnum; | |
| 6 | +import cn.iocoder.dashboard.common.pojo.PageResult; | |
| 7 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory; | |
| 8 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelCreateReqVO; | |
| 9 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelPageReqVO; | |
| 10 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelUpdateReqVO; | |
| 11 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO; | |
| 12 | +import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsChannelMapper; | |
| 13 | +import cn.iocoder.dashboard.modules.system.mq.producer.sms.SysSmsProducer; | |
| 14 | +import cn.iocoder.dashboard.modules.system.service.sms.impl.SysSmsChannelServiceImpl; | |
| 15 | +import cn.iocoder.dashboard.util.collection.ArrayUtils; | |
| 16 | +import cn.iocoder.dashboard.util.object.ObjectUtils; | |
| 17 | +import org.junit.jupiter.api.Test; | |
| 18 | +import org.springframework.boot.test.mock.mockito.MockBean; | |
| 19 | +import org.springframework.context.annotation.Import; | |
| 20 | + | |
| 21 | +import javax.annotation.Resource; | |
| 22 | +import java.util.Date; | |
| 23 | +import java.util.function.Consumer; | |
| 24 | + | |
| 25 | +import static cn.hutool.core.util.RandomUtil.randomEle; | |
| 26 | +import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN; | |
| 27 | +import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS; | |
| 28 | +import static cn.iocoder.dashboard.util.AssertUtils.*; | |
| 29 | +import static cn.iocoder.dashboard.util.RandomUtils.*; | |
| 30 | +import static cn.iocoder.dashboard.util.date.DateUtils.buildTime; | |
| 31 | +import static cn.iocoder.dashboard.util.object.ObjectUtils.max; | |
| 32 | +import static org.junit.jupiter.api.Assertions.*; | |
| 33 | +import static org.mockito.ArgumentMatchers.eq; | |
| 34 | +import static org.mockito.Mockito.*; | |
| 35 | + | |
| 36 | +/** | |
| 37 | +* {@link SysSmsChannelServiceImpl} 的单元测试类 | |
| 38 | +* | |
| 39 | +* @author 芋道源码 | |
| 40 | +*/ | |
| 41 | +@Import(SysSmsChannelServiceImpl.class) | |
| 42 | +public class SysSmsChannelServiceTest extends BaseDbUnitTest { | |
| 43 | + | |
| 44 | + @Resource | |
| 45 | + private SysSmsChannelServiceImpl smsChannelService; | |
| 46 | + | |
| 47 | + @Resource | |
| 48 | + private SysSmsChannelMapper smsChannelMapper; | |
| 49 | + | |
| 50 | + @MockBean | |
| 51 | + private SmsClientFactory smsClientFactory; | |
| 52 | + @MockBean | |
| 53 | + private SysSmsTemplateService smsTemplateService; | |
| 54 | + @MockBean | |
| 55 | + private SysSmsProducer smsProducer; | |
| 56 | + | |
| 57 | + @Test | |
| 58 | + public void testInitLocalCache_success() { | |
| 59 | + // mock 数据 | |
| 60 | + SysSmsChannelDO smsChannelDO01 = randomSmsChannelDO(); | |
| 61 | + smsChannelMapper.insert(smsChannelDO01); | |
| 62 | + SysSmsChannelDO smsChannelDO02 = randomSmsChannelDO(); | |
| 63 | + smsChannelMapper.insert(smsChannelDO02); | |
| 64 | + | |
| 65 | + // 调用 | |
| 66 | + smsChannelService.initSmsClients(); | |
| 67 | + // 校验 maxUpdateTime 属性 | |
| 68 | + Date maxUpdateTime = (Date) BeanUtil.getFieldValue(smsChannelService, "maxUpdateTime"); | |
| 69 | + assertEquals(max(smsChannelDO01.getUpdateTime(), smsChannelDO02.getUpdateTime()), maxUpdateTime); | |
| 70 | + // 校验调用 | |
| 71 | + verify(smsClientFactory, times(1)).createOrUpdateSmsClient( | |
| 72 | + argThat(properties -> isPojoEquals(smsChannelDO01, properties))); | |
| 73 | + verify(smsClientFactory, times(1)).createOrUpdateSmsClient( | |
| 74 | + argThat(properties -> isPojoEquals(smsChannelDO02, properties))); | |
| 75 | + } | |
| 76 | + | |
| 77 | + @Test | |
| 78 | + public void testCreateSmsChannel_success() { | |
| 79 | + // 准备参数 | |
| 80 | + SysSmsChannelCreateReqVO reqVO = randomPojo(SysSmsChannelCreateReqVO.class, o -> o.setStatus(randomCommonStatus())); | |
| 81 | + | |
| 82 | + // 调用 | |
| 83 | + Long smsChannelId = smsChannelService.createSmsChannel(reqVO); | |
| 84 | + // 断言 | |
| 85 | + assertNotNull(smsChannelId); | |
| 86 | + // 校验记录的属性是否正确 | |
| 87 | + SysSmsChannelDO smsChannel = smsChannelMapper.selectById(smsChannelId); | |
| 88 | + assertPojoEquals(reqVO, smsChannel); | |
| 89 | + // 校验调用 | |
| 90 | + verify(smsProducer, times(1)).sendSmsChannelRefreshMessage(); | |
| 91 | + } | |
| 92 | + | |
| 93 | + @Test | |
| 94 | + public void testUpdateSmsChannel_success() { | |
| 95 | + // mock 数据 | |
| 96 | + SysSmsChannelDO dbSmsChannel = randomSmsChannelDO(); | |
| 97 | + smsChannelMapper.insert(dbSmsChannel);// @Sql: 先插入出一条存在的数据 | |
| 98 | + // 准备参数 | |
| 99 | + SysSmsChannelUpdateReqVO reqVO = randomPojo(SysSmsChannelUpdateReqVO.class, o -> { | |
| 100 | + o.setId(dbSmsChannel.getId()); // 设置更新的 ID | |
| 101 | + o.setStatus(randomCommonStatus()); | |
| 102 | + o.setCallbackUrl(randomString()); | |
| 103 | + }); | |
| 104 | + | |
| 105 | + // 调用 | |
| 106 | + smsChannelService.updateSmsChannel(reqVO); | |
| 107 | + // 校验是否更新正确 | |
| 108 | + SysSmsChannelDO smsChannel = smsChannelMapper.selectById(reqVO.getId()); // 获取最新的 | |
| 109 | + assertPojoEquals(reqVO, smsChannel); | |
| 110 | + // 校验调用 | |
| 111 | + verify(smsProducer, times(1)).sendSmsChannelRefreshMessage(); | |
| 112 | + } | |
| 113 | + | |
| 114 | + @Test | |
| 115 | + public void testUpdateSmsChannel_notExists() { | |
| 116 | + // 准备参数 | |
| 117 | + SysSmsChannelUpdateReqVO reqVO = randomPojo(SysSmsChannelUpdateReqVO.class); | |
| 118 | + | |
| 119 | + // 调用, 并断言异常 | |
| 120 | + assertServiceException(() -> smsChannelService.updateSmsChannel(reqVO), SMS_CHANNEL_NOT_EXISTS); | |
| 121 | + } | |
| 122 | + | |
| 123 | + @Test | |
| 124 | + public void testDeleteSmsChannel_success() { | |
| 125 | + // mock 数据 | |
| 126 | + SysSmsChannelDO dbSmsChannel = randomSmsChannelDO(); | |
| 127 | + smsChannelMapper.insert(dbSmsChannel);// @Sql: 先插入出一条存在的数据 | |
| 128 | + // 准备参数 | |
| 129 | + Long id = dbSmsChannel.getId(); | |
| 130 | + | |
| 131 | + // 调用 | |
| 132 | + smsChannelService.deleteSmsChannel(id); | |
| 133 | + // 校验数据不存在了 | |
| 134 | + assertNull(smsChannelMapper.selectById(id)); | |
| 135 | + // 校验调用 | |
| 136 | + verify(smsProducer, times(1)).sendSmsChannelRefreshMessage(); | |
| 137 | + } | |
| 138 | + | |
| 139 | + @Test | |
| 140 | + public void testDeleteSmsChannel_notExists() { | |
| 141 | + // 准备参数 | |
| 142 | + Long id = randomLongId(); | |
| 143 | + | |
| 144 | + // 调用, 并断言异常 | |
| 145 | + assertServiceException(() -> smsChannelService.deleteSmsChannel(id), SMS_CHANNEL_NOT_EXISTS); | |
| 146 | + } | |
| 147 | + | |
| 148 | + @Test | |
| 149 | + public void testDeleteSmsChannel_hasChildren() { | |
| 150 | + // mock 数据 | |
| 151 | + SysSmsChannelDO dbSmsChannel = randomSmsChannelDO(); | |
| 152 | + smsChannelMapper.insert(dbSmsChannel);// @Sql: 先插入出一条存在的数据 | |
| 153 | + // 准备参数 | |
| 154 | + Long id = dbSmsChannel.getId(); | |
| 155 | + // mock 方法 | |
| 156 | + when(smsTemplateService.countByChannelId(eq(id))).thenReturn(10); | |
| 157 | + | |
| 158 | + // 调用, 并断言异常 | |
| 159 | + assertServiceException(() -> smsChannelService.deleteSmsChannel(id), SMS_CHANNEL_HAS_CHILDREN); | |
| 160 | + } | |
| 161 | + | |
| 162 | + @Test | |
| 163 | + public void testGetSmsChannelPage() { | |
| 164 | + // mock 数据 | |
| 165 | + SysSmsChannelDO dbSmsChannel = randomPojo(SysSmsChannelDO.class, o -> { // 等会查询到 | |
| 166 | + o.setSignature("芋道源码"); | |
| 167 | + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 168 | + o.setCreateTime(buildTime(2020, 12, 12)); | |
| 169 | + }); | |
| 170 | + smsChannelMapper.insert(dbSmsChannel); | |
| 171 | + // 测试 signature 不匹配 | |
| 172 | + smsChannelMapper.insert(ObjectUtils.clone(dbSmsChannel, o -> o.setSignature("源码"))); | |
| 173 | + // 测试 status 不匹配 | |
| 174 | + smsChannelMapper.insert(ObjectUtils.clone(dbSmsChannel, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | |
| 175 | + // 测试 createTime 不匹配 | |
| 176 | + smsChannelMapper.insert(ObjectUtils.clone(dbSmsChannel, o -> o.setCreateTime(buildTime(2020, 11, 11)))); | |
| 177 | + // 准备参数 | |
| 178 | + SysSmsChannelPageReqVO reqVO = new SysSmsChannelPageReqVO(); | |
| 179 | + reqVO.setSignature("芋道"); | |
| 180 | + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 181 | + reqVO.setBeginCreateTime(buildTime(2020, 12, 1)); | |
| 182 | + reqVO.setEndCreateTime(buildTime(2020, 12, 24)); | |
| 183 | + | |
| 184 | + // 调用 | |
| 185 | + PageResult<SysSmsChannelDO> pageResult = smsChannelService.getSmsChannelPage(reqVO); | |
| 186 | + // 断言 | |
| 187 | + assertEquals(1, pageResult.getTotal()); | |
| 188 | + assertEquals(1, pageResult.getList().size()); | |
| 189 | + assertPojoEquals(dbSmsChannel, pageResult.getList().get(0)); | |
| 190 | + } | |
| 191 | + | |
| 192 | + // ========== 随机对象 ========== | |
| 193 | + | |
| 194 | + @SafeVarargs | |
| 195 | + private static SysSmsChannelDO randomSmsChannelDO(Consumer<SysSmsChannelDO>... consumers) { | |
| 196 | + Consumer<SysSmsChannelDO> consumer = (o) -> { | |
| 197 | + o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 | |
| 198 | + }; | |
| 199 | + return randomPojo(SysSmsChannelDO.class, ArrayUtils.append(consumer, consumers)); | |
| 200 | + } | |
| 201 | + | |
| 202 | +} | ... | ... |
src/test/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsLogServiceTest.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.sms; | |
| 2 | + | |
| 3 | +import cn.hutool.core.map.MapUtil; | |
| 4 | +import cn.iocoder.dashboard.BaseDbUnitTest; | |
| 5 | +import cn.iocoder.dashboard.common.enums.UserTypeEnum; | |
| 6 | +import cn.iocoder.dashboard.common.pojo.CommonResult; | |
| 7 | +import cn.iocoder.dashboard.common.pojo.PageResult; | |
| 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.dal.dataobject.sms.SysSmsLogDO; | |
| 11 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; | |
| 12 | +import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsLogMapper; | |
| 13 | +import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsReceiveStatusEnum; | |
| 14 | +import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum; | |
| 15 | +import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsTemplateTypeEnum; | |
| 16 | +import cn.iocoder.dashboard.modules.system.service.sms.impl.SysSmsLogServiceImpl; | |
| 17 | +import cn.iocoder.dashboard.util.collection.ArrayUtils; | |
| 18 | +import cn.iocoder.dashboard.util.object.ObjectUtils; | |
| 19 | +import org.junit.jupiter.api.Test; | |
| 20 | +import org.springframework.context.annotation.Import; | |
| 21 | + | |
| 22 | +import javax.annotation.Resource; | |
| 23 | +import java.util.Date; | |
| 24 | +import java.util.List; | |
| 25 | +import java.util.Map; | |
| 26 | +import java.util.function.Consumer; | |
| 27 | + | |
| 28 | +import static cn.hutool.core.util.RandomUtil.randomBoolean; | |
| 29 | +import static cn.hutool.core.util.RandomUtil.randomEle; | |
| 30 | +import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals; | |
| 31 | +import static cn.iocoder.dashboard.util.RandomUtils.*; | |
| 32 | +import static cn.iocoder.dashboard.util.date.DateUtils.buildTime; | |
| 33 | +import static org.junit.jupiter.api.Assertions.*; | |
| 34 | + | |
| 35 | +/** | |
| 36 | +* {@link SysSmsLogServiceImpl} 的单元测试类 | |
| 37 | +* | |
| 38 | +* @author 芋道源码 | |
| 39 | +*/ | |
| 40 | +@Import(SysSmsLogServiceImpl.class) | |
| 41 | +public class SysSmsLogServiceTest extends BaseDbUnitTest { | |
| 42 | + | |
| 43 | + @Resource | |
| 44 | + private SysSmsLogServiceImpl smsLogService; | |
| 45 | + | |
| 46 | + @Resource | |
| 47 | + private SysSmsLogMapper smsLogMapper; | |
| 48 | + | |
| 49 | + @Test | |
| 50 | + public void testCreateSmsLog() { | |
| 51 | + // 准备参数 | |
| 52 | + String mobile = randomString(); | |
| 53 | + Long userId = randomLongId(); | |
| 54 | + Integer userType = randomEle(UserTypeEnum.values()).getValue(); | |
| 55 | + Boolean isSend = randomBoolean(); | |
| 56 | + SysSmsTemplateDO templateDO = randomPojo(SysSmsTemplateDO.class, | |
| 57 | + o -> o.setType(randomEle(SysSmsTemplateTypeEnum.values()).getType())); | |
| 58 | + String templateContent = randomString(); | |
| 59 | + Map<String, Object> templateParams = randomTemplateParams(); | |
| 60 | + // mock 方法 | |
| 61 | + | |
| 62 | + // 调用 | |
| 63 | + Long logId = smsLogService.createSmsLog(mobile, userId, userType, isSend, | |
| 64 | + templateDO, templateContent, templateParams); | |
| 65 | + // 断言 | |
| 66 | + SysSmsLogDO logDO = smsLogMapper.selectById(logId); | |
| 67 | + assertEquals(isSend ? SysSmsSendStatusEnum.INIT.getStatus() : SysSmsSendStatusEnum.IGNORE.getStatus(), | |
| 68 | + logDO.getSendStatus()); | |
| 69 | + assertEquals(mobile, logDO.getMobile()); | |
| 70 | + assertEquals(userType, logDO.getUserType()); | |
| 71 | + assertEquals(userId, logDO.getUserId()); | |
| 72 | + assertEquals(templateDO.getId(), logDO.getTemplateId()); | |
| 73 | + assertEquals(templateDO.getCode(), logDO.getTemplateCode()); | |
| 74 | + assertEquals(templateDO.getType(), logDO.getTemplateType()); | |
| 75 | + assertEquals(templateDO.getChannelId(), logDO.getChannelId()); | |
| 76 | + assertEquals(templateDO.getChannelCode(), logDO.getChannelCode()); | |
| 77 | + assertEquals(templateContent, logDO.getTemplateContent()); | |
| 78 | + assertEquals(templateParams, logDO.getTemplateParams()); | |
| 79 | + assertEquals(SysSmsReceiveStatusEnum.INIT.getStatus(), logDO.getReceiveStatus()); | |
| 80 | + } | |
| 81 | + | |
| 82 | + @Test | |
| 83 | + public void testUpdateSmsSendResult() { | |
| 84 | + // mock 数据 | |
| 85 | + SysSmsLogDO dbSmsLog = randomSmsLogDO( | |
| 86 | + o -> o.setSendStatus(SysSmsSendStatusEnum.IGNORE.getStatus())); | |
| 87 | + smsLogMapper.insert(dbSmsLog); | |
| 88 | + // 准备参数 | |
| 89 | + Long id = dbSmsLog.getId(); | |
| 90 | + Integer sendCode = randomInteger(); | |
| 91 | + String sendMsg = randomString(); | |
| 92 | + String apiSendCode = randomString(); | |
| 93 | + String apiSendMsg = randomString(); | |
| 94 | + String apiRequestId = randomString(); | |
| 95 | + String apiSerialNo = randomString(); | |
| 96 | + | |
| 97 | + // 调用 | |
| 98 | + smsLogService.updateSmsSendResult(id, sendCode, sendMsg, | |
| 99 | + apiSendCode, apiSendMsg, apiRequestId, apiSerialNo); | |
| 100 | + // 断言 | |
| 101 | + dbSmsLog = smsLogMapper.selectById(id); | |
| 102 | + assertEquals(CommonResult.isSuccess(sendCode) ? SysSmsSendStatusEnum.SUCCESS.getStatus() | |
| 103 | + : SysSmsSendStatusEnum.FAILURE.getStatus(), dbSmsLog.getSendStatus()); | |
| 104 | + assertNotNull(dbSmsLog.getSendTime()); | |
| 105 | + assertEquals(sendMsg, dbSmsLog.getSendMsg()); | |
| 106 | + assertEquals(apiSendCode, dbSmsLog.getApiSendCode()); | |
| 107 | + assertEquals(apiSendMsg, dbSmsLog.getApiSendMsg()); | |
| 108 | + assertEquals(apiRequestId, dbSmsLog.getApiRequestId()); | |
| 109 | + assertEquals(apiSerialNo, dbSmsLog.getApiSerialNo()); | |
| 110 | + } | |
| 111 | + | |
| 112 | + @Test | |
| 113 | + public void testUpdateSmsReceiveResult() { | |
| 114 | + // mock 数据 | |
| 115 | + SysSmsLogDO dbSmsLog = randomSmsLogDO( | |
| 116 | + o -> o.setReceiveStatus(SysSmsReceiveStatusEnum.INIT.getStatus())); | |
| 117 | + smsLogMapper.insert(dbSmsLog); | |
| 118 | + // 准备参数 | |
| 119 | + Long id = dbSmsLog.getId(); | |
| 120 | + Boolean success = randomBoolean(); | |
| 121 | + Date receiveTime = randomDate(); | |
| 122 | + String apiReceiveCode = randomString(); | |
| 123 | + String apiReceiveMsg = randomString(); | |
| 124 | + | |
| 125 | + // 调用 | |
| 126 | + smsLogService.updateSmsReceiveResult(id, success, receiveTime, apiReceiveCode, apiReceiveMsg); | |
| 127 | + // 断言 | |
| 128 | + dbSmsLog = smsLogMapper.selectById(id); | |
| 129 | + assertEquals(success ? SysSmsReceiveStatusEnum.SUCCESS.getStatus() | |
| 130 | + : SysSmsReceiveStatusEnum.FAILURE.getStatus(), dbSmsLog.getReceiveStatus()); | |
| 131 | + assertEquals(receiveTime, dbSmsLog.getReceiveTime()); | |
| 132 | + assertEquals(apiReceiveCode, dbSmsLog.getApiReceiveCode()); | |
| 133 | + assertEquals(apiReceiveMsg, dbSmsLog.getApiReceiveMsg()); | |
| 134 | + } | |
| 135 | + | |
| 136 | + @Test | |
| 137 | + public void testGetSmsLogPage() { | |
| 138 | + // mock 数据 | |
| 139 | + SysSmsLogDO dbSmsLog = randomSmsLogDO(o -> { // 等会查询到 | |
| 140 | + o.setChannelId(1L); | |
| 141 | + o.setTemplateId(10L); | |
| 142 | + o.setMobile("15601691300"); | |
| 143 | + o.setSendStatus(SysSmsSendStatusEnum.INIT.getStatus()); | |
| 144 | + o.setSendTime(buildTime(2020, 11, 11)); | |
| 145 | + o.setReceiveStatus(SysSmsReceiveStatusEnum.INIT.getStatus()); | |
| 146 | + o.setReceiveTime(buildTime(2021, 11, 11)); | |
| 147 | + }); | |
| 148 | + smsLogMapper.insert(dbSmsLog); | |
| 149 | + // 测试 channelId 不匹配 | |
| 150 | + smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setChannelId(2L))); | |
| 151 | + // 测试 templateId 不匹配 | |
| 152 | + smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setTemplateId(20L))); | |
| 153 | + // 测试 mobile 不匹配 | |
| 154 | + smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setMobile("18818260999"))); | |
| 155 | + // 测试 sendStatus 不匹配 | |
| 156 | + smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setSendStatus(SysSmsSendStatusEnum.IGNORE.getStatus()))); | |
| 157 | + // 测试 sendTime 不匹配 | |
| 158 | + smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setSendTime(buildTime(2020, 12, 12)))); | |
| 159 | + // 测试 receiveStatus 不匹配 | |
| 160 | + smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setReceiveStatus(SysSmsReceiveStatusEnum.SUCCESS.getStatus()))); | |
| 161 | + // 测试 receiveTime 不匹配 | |
| 162 | + smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setReceiveTime(buildTime(2021, 12, 12)))); | |
| 163 | + // 准备参数 | |
| 164 | + SysSmsLogPageReqVO reqVO = new SysSmsLogPageReqVO(); | |
| 165 | + reqVO.setChannelId(1L); | |
| 166 | + reqVO.setTemplateId(10L); | |
| 167 | + reqVO.setMobile("156"); | |
| 168 | + reqVO.setSendStatus(SysSmsSendStatusEnum.INIT.getStatus()); | |
| 169 | + reqVO.setBeginSendTime(buildTime(2020, 11, 1)); | |
| 170 | + reqVO.setEndSendTime(buildTime(2020, 11, 30)); | |
| 171 | + reqVO.setReceiveStatus(SysSmsReceiveStatusEnum.INIT.getStatus()); | |
| 172 | + reqVO.setBeginReceiveTime(buildTime(2021, 11, 1)); | |
| 173 | + reqVO.setEndReceiveTime(buildTime(2021, 11, 30)); | |
| 174 | + | |
| 175 | + // 调用 | |
| 176 | + PageResult<SysSmsLogDO> pageResult = smsLogService.getSmsLogPage(reqVO); | |
| 177 | + // 断言 | |
| 178 | + assertEquals(1, pageResult.getTotal()); | |
| 179 | + assertEquals(1, pageResult.getList().size()); | |
| 180 | + assertPojoEquals(dbSmsLog, pageResult.getList().get(0)); | |
| 181 | + } | |
| 182 | + | |
| 183 | + @Test | |
| 184 | + public void testGetSmsLogList() { | |
| 185 | + // mock 数据 | |
| 186 | + SysSmsLogDO dbSmsLog = randomSmsLogDO(o -> { // 等会查询到 | |
| 187 | + o.setChannelId(1L); | |
| 188 | + o.setTemplateId(10L); | |
| 189 | + o.setMobile("15601691300"); | |
| 190 | + o.setSendStatus(SysSmsSendStatusEnum.INIT.getStatus()); | |
| 191 | + o.setSendTime(buildTime(2020, 11, 11)); | |
| 192 | + o.setReceiveStatus(SysSmsReceiveStatusEnum.INIT.getStatus()); | |
| 193 | + o.setReceiveTime(buildTime(2021, 11, 11)); | |
| 194 | + }); | |
| 195 | + smsLogMapper.insert(dbSmsLog); | |
| 196 | + // 测试 channelId 不匹配 | |
| 197 | + smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setChannelId(2L))); | |
| 198 | + // 测试 templateId 不匹配 | |
| 199 | + smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setTemplateId(20L))); | |
| 200 | + // 测试 mobile 不匹配 | |
| 201 | + smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setMobile("18818260999"))); | |
| 202 | + // 测试 sendStatus 不匹配 | |
| 203 | + smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setSendStatus(SysSmsSendStatusEnum.IGNORE.getStatus()))); | |
| 204 | + // 测试 sendTime 不匹配 | |
| 205 | + smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setSendTime(buildTime(2020, 12, 12)))); | |
| 206 | + // 测试 receiveStatus 不匹配 | |
| 207 | + smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setReceiveStatus(SysSmsReceiveStatusEnum.SUCCESS.getStatus()))); | |
| 208 | + // 测试 receiveTime 不匹配 | |
| 209 | + smsLogMapper.insert(ObjectUtils.clone(dbSmsLog, o -> o.setReceiveTime(buildTime(2021, 12, 12)))); | |
| 210 | + // 准备参数 | |
| 211 | + SysSmsLogExportReqVO reqVO = new SysSmsLogExportReqVO(); | |
| 212 | + reqVO.setChannelId(1L); | |
| 213 | + reqVO.setTemplateId(10L); | |
| 214 | + reqVO.setMobile("156"); | |
| 215 | + reqVO.setSendStatus(SysSmsSendStatusEnum.INIT.getStatus()); | |
| 216 | + reqVO.setBeginSendTime(buildTime(2020, 11, 1)); | |
| 217 | + reqVO.setEndSendTime(buildTime(2020, 11, 30)); | |
| 218 | + reqVO.setReceiveStatus(SysSmsReceiveStatusEnum.INIT.getStatus()); | |
| 219 | + reqVO.setBeginReceiveTime(buildTime(2021, 11, 1)); | |
| 220 | + reqVO.setEndReceiveTime(buildTime(2021, 11, 30)); | |
| 221 | + | |
| 222 | + // 调用 | |
| 223 | + List<SysSmsLogDO> list = smsLogService.getSmsLogList(reqVO); | |
| 224 | + // 断言 | |
| 225 | + assertEquals(1, list.size()); | |
| 226 | + assertPojoEquals(dbSmsLog, list.get(0)); | |
| 227 | + } | |
| 228 | + | |
| 229 | + // ========== 随机对象 ========== | |
| 230 | + | |
| 231 | + @SafeVarargs | |
| 232 | + private static SysSmsLogDO randomSmsLogDO(Consumer<SysSmsLogDO>... consumers) { | |
| 233 | + Consumer<SysSmsLogDO> consumer = (o) -> { | |
| 234 | + o.setTemplateParams(randomTemplateParams()); | |
| 235 | + o.setTemplateType(randomEle(SysSmsTemplateTypeEnum.values()).getType()); // 保证 templateType 的范围 | |
| 236 | + o.setUserType(randomEle(UserTypeEnum.values()).getValue()); // 保证 userType 的范围 | |
| 237 | + o.setSendStatus(randomEle(SysSmsSendStatusEnum.values()).getStatus()); // 保证 sendStatus 的范围 | |
| 238 | + o.setReceiveStatus(randomEle(SysSmsReceiveStatusEnum.values()).getStatus()); // 保证 receiveStatus 的范围 | |
| 239 | + }; | |
| 240 | + return randomPojo(SysSmsLogDO.class, ArrayUtils.append(consumer, consumers)); | |
| 241 | + } | |
| 242 | + | |
| 243 | + | |
| 244 | + private static Map<String, Object> randomTemplateParams() { | |
| 245 | + return MapUtil.<String, Object>builder().put(randomString(), randomString()) | |
| 246 | + .put(randomString(), randomString()).build(); | |
| 247 | + } | |
| 248 | +} | ... | ... |
src/test/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsServiceTest.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.sms; | |
| 2 | + | |
| 3 | +import cn.hutool.core.map.MapUtil; | |
| 4 | +import cn.iocoder.dashboard.BaseMockitoUnitTest; | |
| 5 | +import cn.iocoder.dashboard.common.core.KeyValue; | |
| 6 | +import cn.iocoder.dashboard.common.enums.CommonStatusEnum; | |
| 7 | +import cn.iocoder.dashboard.common.enums.UserTypeEnum; | |
| 8 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsClient; | |
| 9 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory; | |
| 10 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; | |
| 11 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO; | |
| 12 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; | |
| 13 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; | |
| 14 | +import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage; | |
| 15 | +import cn.iocoder.dashboard.modules.system.mq.producer.sms.SysSmsProducer; | |
| 16 | +import cn.iocoder.dashboard.modules.system.service.sms.impl.SysSmsServiceImpl; | |
| 17 | +import org.assertj.core.util.Lists; | |
| 18 | +import org.junit.jupiter.api.Test; | |
| 19 | +import org.mockito.InjectMocks; | |
| 20 | +import org.mockito.Mock; | |
| 21 | + | |
| 22 | +import java.util.HashMap; | |
| 23 | +import java.util.List; | |
| 24 | +import java.util.Map; | |
| 25 | + | |
| 26 | +import static cn.hutool.core.util.RandomUtil.randomEle; | |
| 27 | +import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; | |
| 28 | +import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException; | |
| 29 | +import static cn.iocoder.dashboard.util.RandomUtils.*; | |
| 30 | +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| 31 | +import static org.mockito.ArgumentMatchers.eq; | |
| 32 | +import static org.mockito.Mockito.*; | |
| 33 | + | |
| 34 | +/** | |
| 35 | + * {@link SysSmsServiceImpl} 的单元测试类 | |
| 36 | + * | |
| 37 | + * @author 芋道源码 | |
| 38 | + */ | |
| 39 | +public class SysSmsServiceTest extends BaseMockitoUnitTest { | |
| 40 | + | |
| 41 | + @InjectMocks | |
| 42 | + private SysSmsServiceImpl smsService; | |
| 43 | + | |
| 44 | + @Mock | |
| 45 | + private SysSmsTemplateService smsTemplateService; | |
| 46 | + @Mock | |
| 47 | + private SysSmsLogService smsLogService; | |
| 48 | + @Mock | |
| 49 | + private SysSmsProducer smsProducer; | |
| 50 | + @Mock | |
| 51 | + private SmsClientFactory smsClientFactory; | |
| 52 | + | |
| 53 | + /** | |
| 54 | + * 发送成功,当短信模板开启时 | |
| 55 | + */ | |
| 56 | + @Test | |
| 57 | + public void testSendSingleSms_successWhenSmsTemplateEnable() { | |
| 58 | + // 准备参数 | |
| 59 | + String mobile = randomString(); | |
| 60 | + Long userId = randomLongId(); | |
| 61 | + Integer userType = randomEle(UserTypeEnum.values()).getValue(); | |
| 62 | + String templateCode = randomString(); | |
| 63 | + Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234") | |
| 64 | + .put("op", "login").build(); | |
| 65 | + // mock SmsTemplateService 的方法 | |
| 66 | + SysSmsTemplateDO template = randomPojo(SysSmsTemplateDO.class, o -> { | |
| 67 | + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 68 | + o.setContent("验证码为{code}, 操作为{op}"); | |
| 69 | + o.setParams(Lists.newArrayList("code", "op")); | |
| 70 | + }); | |
| 71 | + when(smsTemplateService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); | |
| 72 | + String content = randomString(); | |
| 73 | + when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams))) | |
| 74 | + .thenReturn(content); | |
| 75 | + // mock SmsLogService 的方法 | |
| 76 | + Long smsLogId = randomLongId(); | |
| 77 | + when(smsLogService.createSmsLog(eq(mobile), eq(userId), eq(userType), eq(Boolean.TRUE), eq(template), | |
| 78 | + eq(content), eq(templateParams))).thenReturn(smsLogId); | |
| 79 | + | |
| 80 | + // 调用 | |
| 81 | + Long resultSmsLogId = smsService.sendSingleSms(mobile, userId, userType, templateCode, templateParams); | |
| 82 | + // 断言 | |
| 83 | + assertEquals(smsLogId, resultSmsLogId); | |
| 84 | + // 断言调用 | |
| 85 | + verify(smsProducer, times(1)).sendSmsSendMessage(eq(smsLogId), eq(mobile), | |
| 86 | + eq(template.getChannelId()), eq(template.getApiTemplateId()), | |
| 87 | + eq(Lists.newArrayList(new KeyValue<>("code", "1234"), new KeyValue<>("op", "login")))); | |
| 88 | + } | |
| 89 | + | |
| 90 | + /** | |
| 91 | + * 发送成功,当短信模板关闭时 | |
| 92 | + */ | |
| 93 | + @Test | |
| 94 | + public void testSendSingleSms_successWhenSmsTemplateDisable() { | |
| 95 | + // 准备参数 | |
| 96 | + String mobile = randomString(); | |
| 97 | + Long userId = randomLongId(); | |
| 98 | + Integer userType = randomEle(UserTypeEnum.values()).getValue(); | |
| 99 | + String templateCode = randomString(); | |
| 100 | + Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234") | |
| 101 | + .put("op", "login").build(); | |
| 102 | + // mock SmsTemplateService 的方法 | |
| 103 | + SysSmsTemplateDO template = randomPojo(SysSmsTemplateDO.class, o -> { | |
| 104 | + o.setStatus(CommonStatusEnum.DISABLE.getStatus()); | |
| 105 | + o.setContent("验证码为{code}, 操作为{op}"); | |
| 106 | + o.setParams(Lists.newArrayList("code", "op")); | |
| 107 | + }); | |
| 108 | + when(smsTemplateService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); | |
| 109 | + String content = randomString(); | |
| 110 | + when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams))) | |
| 111 | + .thenReturn(content); | |
| 112 | + // mock SmsLogService 的方法 | |
| 113 | + Long smsLogId = randomLongId(); | |
| 114 | + when(smsLogService.createSmsLog(eq(mobile), eq(userId), eq(userType), eq(Boolean.FALSE), eq(template), | |
| 115 | + eq(content), eq(templateParams))).thenReturn(smsLogId); | |
| 116 | + | |
| 117 | + // 调用 | |
| 118 | + Long resultSmsLogId = smsService.sendSingleSms(mobile, userId, userType, templateCode, templateParams); | |
| 119 | + // 断言 | |
| 120 | + assertEquals(smsLogId, resultSmsLogId); | |
| 121 | + // 断言调用 | |
| 122 | + verify(smsProducer, times(0)).sendSmsSendMessage(anyLong(), anyString(), | |
| 123 | + anyLong(), any(), anyList()); | |
| 124 | + } | |
| 125 | + | |
| 126 | + @Test | |
| 127 | + public void testCheckSmsTemplateValid_notExists() { | |
| 128 | + // 准备参数 | |
| 129 | + String templateCode = randomString(); | |
| 130 | + // mock 方法 | |
| 131 | + | |
| 132 | + // 调用,并断言异常 | |
| 133 | + assertServiceException(() -> smsService.checkSmsTemplateValid(templateCode), | |
| 134 | + SMS_TEMPLATE_NOT_EXISTS); | |
| 135 | + } | |
| 136 | + | |
| 137 | + @Test | |
| 138 | + public void testBuildTemplateParams_paramMiss() { | |
| 139 | + // 准备参数 | |
| 140 | + SysSmsTemplateDO template = randomPojo(SysSmsTemplateDO.class, | |
| 141 | + o -> o.setParams(Lists.newArrayList("code"))); | |
| 142 | + Map<String, Object> templateParams = new HashMap<>(); | |
| 143 | + // mock 方法 | |
| 144 | + | |
| 145 | + // 调用,并断言异常 | |
| 146 | + assertServiceException(() -> smsService.buildTemplateParams(template, templateParams), | |
| 147 | + SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS, "code"); | |
| 148 | + } | |
| 149 | + | |
| 150 | + @Test | |
| 151 | + public void testCheckMobile_notExists() { | |
| 152 | + // 准备参数 | |
| 153 | + // mock 方法 | |
| 154 | + | |
| 155 | + // 调用,并断言异常 | |
| 156 | + assertServiceException(() -> smsService.checkMobile(null), | |
| 157 | + SMS_SEND_MOBILE_NOT_EXISTS); | |
| 158 | + } | |
| 159 | + | |
| 160 | + @Test | |
| 161 | + @SuppressWarnings("unchecked") | |
| 162 | + public void testDoSendSms() { | |
| 163 | + // 准备参数 | |
| 164 | + SysSmsSendMessage message = randomPojo(SysSmsSendMessage.class); | |
| 165 | + // mock SmsClientFactory 的方法 | |
| 166 | + SmsClient smsClient = spy(SmsClient.class); | |
| 167 | + when(smsClientFactory.getSmsClient(eq(message.getChannelId()))).thenReturn(smsClient); | |
| 168 | + // mock SmsClient 的方法 | |
| 169 | + SmsCommonResult<SmsSendRespDTO> sendResult = randomPojo(SmsCommonResult.class, SmsSendRespDTO.class); | |
| 170 | + when(smsClient.sendSms(eq(message.getLogId()), eq(message.getMobile()), eq(message.getApiTemplateId()), | |
| 171 | + eq(message.getTemplateParams()))).thenReturn(sendResult); | |
| 172 | + | |
| 173 | + // 调用 | |
| 174 | + smsService.doSendSms(message); | |
| 175 | + // 断言 | |
| 176 | + verify(smsLogService, times(1)).updateSmsSendResult(eq(message.getLogId()), | |
| 177 | + eq(sendResult.getCode()), eq(sendResult.getMsg()), eq(sendResult.getApiCode()), | |
| 178 | + eq(sendResult.getApiMsg()), eq(sendResult.getApiRequestId()), eq(sendResult.getData().getSerialNo())); | |
| 179 | + } | |
| 180 | + | |
| 181 | + @Test | |
| 182 | + public void testReceiveSmsStatus() throws Throwable { | |
| 183 | + // 准备参数 | |
| 184 | + String channelCode = randomString(); | |
| 185 | + String text = randomString(); | |
| 186 | + // mock SmsClientFactory 的方法 | |
| 187 | + SmsClient smsClient = spy(SmsClient.class); | |
| 188 | + when(smsClientFactory.getSmsClient(eq(channelCode))).thenReturn(smsClient); | |
| 189 | + // mock SmsClient 的方法 | |
| 190 | + List<SmsReceiveRespDTO> receiveResults = randomPojoList(SmsReceiveRespDTO.class); | |
| 191 | + | |
| 192 | + // 调用 | |
| 193 | + smsService.receiveSmsStatus(channelCode, text); | |
| 194 | + // 断言 | |
| 195 | + receiveResults.forEach(result -> { | |
| 196 | + smsLogService.updateSmsReceiveResult(eq(result.getLogId()), eq(result.getSuccess()), | |
| 197 | + eq(result.getReceiveTime()), eq(result.getErrorCode()), eq(result.getErrorCode())); | |
| 198 | + }); | |
| 199 | + } | |
| 200 | + | |
| 201 | +} | ... | ... |
src/test/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsTemplateServiceTest.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.sms; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.BaseDbUnitTest; | |
| 4 | +import cn.iocoder.dashboard.common.enums.CommonStatusEnum; | |
| 5 | +import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; | |
| 6 | +import cn.iocoder.dashboard.common.pojo.PageResult; | |
| 7 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsClient; | |
| 8 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory; | |
| 9 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; | |
| 10 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsTemplateRespDTO; | |
| 11 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplateCreateReqVO; | |
| 12 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplateExportReqVO; | |
| 13 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplatePageReqVO; | |
| 14 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplateUpdateReqVO; | |
| 15 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO; | |
| 16 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; | |
| 17 | +import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsTemplateMapper; | |
| 18 | +import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsTemplateTypeEnum; | |
| 19 | +import cn.iocoder.dashboard.modules.system.mq.producer.sms.SysSmsProducer; | |
| 20 | +import cn.iocoder.dashboard.modules.system.service.sms.impl.SysSmsTemplateServiceImpl; | |
| 21 | +import cn.iocoder.dashboard.util.collection.ArrayUtils; | |
| 22 | +import cn.iocoder.dashboard.util.object.ObjectUtils; | |
| 23 | +import com.google.common.collect.Lists; | |
| 24 | +import org.junit.jupiter.api.Test; | |
| 25 | +import org.springframework.boot.test.mock.mockito.MockBean; | |
| 26 | +import org.springframework.context.annotation.Import; | |
| 27 | + | |
| 28 | +import javax.annotation.Resource; | |
| 29 | +import java.util.Date; | |
| 30 | +import java.util.List; | |
| 31 | +import java.util.Map; | |
| 32 | +import java.util.function.Consumer; | |
| 33 | + | |
| 34 | +import static cn.hutool.core.bean.BeanUtil.getFieldValue; | |
| 35 | +import static cn.hutool.core.util.RandomUtil.randomEle; | |
| 36 | +import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; | |
| 37 | +import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals; | |
| 38 | +import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException; | |
| 39 | +import static cn.iocoder.dashboard.util.RandomUtils.*; | |
| 40 | +import static cn.iocoder.dashboard.util.date.DateUtils.buildTime; | |
| 41 | +import static cn.iocoder.dashboard.util.object.ObjectUtils.max; | |
| 42 | +import static org.junit.jupiter.api.Assertions.*; | |
| 43 | +import static org.mockito.ArgumentMatchers.eq; | |
| 44 | +import static org.mockito.Mockito.*; | |
| 45 | + | |
| 46 | +/** | |
| 47 | +* {@link SysSmsTemplateServiceImpl} 的单元测试类 | |
| 48 | +* | |
| 49 | +* @author 芋道源码 | |
| 50 | +*/ | |
| 51 | +@Import(SysSmsTemplateServiceImpl.class) | |
| 52 | +public class SysSmsTemplateServiceTest extends BaseDbUnitTest { | |
| 53 | + | |
| 54 | + @Resource | |
| 55 | + private SysSmsTemplateServiceImpl smsTemplateService; | |
| 56 | + | |
| 57 | + @Resource | |
| 58 | + private SysSmsTemplateMapper smsTemplateMapper; | |
| 59 | + | |
| 60 | + @MockBean | |
| 61 | + private SysSmsChannelService smsChannelService; | |
| 62 | + @MockBean | |
| 63 | + private SmsClientFactory smsClientFactory; | |
| 64 | + @MockBean | |
| 65 | + private SmsClient smsClient; | |
| 66 | + @MockBean | |
| 67 | + private SysSmsProducer smsProducer; | |
| 68 | + | |
| 69 | + @Test | |
| 70 | + @SuppressWarnings("unchecked") | |
| 71 | + void testInitLocalCache() { | |
| 72 | + // mock 数据 | |
| 73 | + SysSmsTemplateDO smsTemplate01 = randomSmsTemplateDO(); | |
| 74 | + smsTemplateMapper.insert(smsTemplate01); | |
| 75 | + SysSmsTemplateDO smsTemplate02 = randomSmsTemplateDO(); | |
| 76 | + smsTemplateMapper.insert(smsTemplate02); | |
| 77 | + | |
| 78 | + // 调用 | |
| 79 | + smsTemplateService.initLocalCache(); | |
| 80 | + // 断言 deptCache 缓存 | |
| 81 | + Map<String, SysSmsTemplateDO> smsTemplateCache = (Map<String, SysSmsTemplateDO>) getFieldValue(smsTemplateService, "smsTemplateCache"); | |
| 82 | + assertEquals(2, smsTemplateCache.size()); | |
| 83 | + assertPojoEquals(smsTemplate01, smsTemplateCache.get(smsTemplate01.getCode())); | |
| 84 | + assertPojoEquals(smsTemplate02, smsTemplateCache.get(smsTemplate02.getCode())); | |
| 85 | + // 断言 maxUpdateTime 缓存 | |
| 86 | + Date maxUpdateTime = (Date) getFieldValue(smsTemplateService, "maxUpdateTime"); | |
| 87 | + assertEquals(max(smsTemplate01.getUpdateTime(), smsTemplate02.getUpdateTime()), maxUpdateTime); | |
| 88 | + } | |
| 89 | + | |
| 90 | + @Test | |
| 91 | + public void testParseTemplateContentParams() { | |
| 92 | + // 准备参数 | |
| 93 | + String content = "正在进行登录操作{operation},您的验证码是{code}"; | |
| 94 | + // mock 方法 | |
| 95 | + | |
| 96 | + // 调用 | |
| 97 | + List<String> params = smsTemplateService.parseTemplateContentParams(content); | |
| 98 | + // 断言 | |
| 99 | + assertEquals(Lists.newArrayList("operation", "code"), params); | |
| 100 | + } | |
| 101 | + | |
| 102 | + @Test | |
| 103 | + @SuppressWarnings("unchecked") | |
| 104 | + public void testCreateSmsTemplate_success() { | |
| 105 | + // 准备参数 | |
| 106 | + SysSmsTemplateCreateReqVO reqVO = randomPojo(SysSmsTemplateCreateReqVO.class, o -> { | |
| 107 | + o.setContent("正在进行登录操作{operation},您的验证码是{code}"); | |
| 108 | + o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 | |
| 109 | + o.setType(randomEle(SysSmsTemplateTypeEnum.values()).getType()); // 保证 type 的 范围 | |
| 110 | + }); | |
| 111 | + // mock Channel 的方法 | |
| 112 | + SysSmsChannelDO channelDO = randomPojo(SysSmsChannelDO.class, o -> { | |
| 113 | + o.setId(reqVO.getChannelId()); | |
| 114 | + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 开启,创建必须处于这个状态 | |
| 115 | + }); | |
| 116 | + when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO); | |
| 117 | + // mock 获得 API 短信模板成功 | |
| 118 | + when(smsClientFactory.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient); | |
| 119 | + when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn(randomPojo(SmsCommonResult.class, SmsTemplateRespDTO.class, | |
| 120 | + o -> o.setCode(GlobalErrorCodeConstants.SUCCESS.getCode()))); | |
| 121 | + | |
| 122 | + // 调用 | |
| 123 | + Long smsTemplateId = smsTemplateService.createSmsTemplate(reqVO); | |
| 124 | + // 断言 | |
| 125 | + assertNotNull(smsTemplateId); | |
| 126 | + // 校验记录的属性是否正确 | |
| 127 | + SysSmsTemplateDO smsTemplate = smsTemplateMapper.selectById(smsTemplateId); | |
| 128 | + assertPojoEquals(reqVO, smsTemplate); | |
| 129 | + assertEquals(Lists.newArrayList("operation", "code"), smsTemplate.getParams()); | |
| 130 | + assertEquals(channelDO.getCode(), smsTemplate.getChannelCode()); | |
| 131 | + // 校验调用 | |
| 132 | + verify(smsProducer, times(1)).sendSmsTemplateRefreshMessage(); | |
| 133 | + } | |
| 134 | + | |
| 135 | + @Test | |
| 136 | + @SuppressWarnings("unchecked") | |
| 137 | + public void testUpdateSmsTemplate_success() { | |
| 138 | + // mock 数据 | |
| 139 | + SysSmsTemplateDO dbSmsTemplate = randomSmsTemplateDO(); | |
| 140 | + smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据 | |
| 141 | + // 准备参数 | |
| 142 | + SysSmsTemplateUpdateReqVO reqVO = randomPojo(SysSmsTemplateUpdateReqVO.class, o -> { | |
| 143 | + o.setId(dbSmsTemplate.getId()); // 设置更新的 ID | |
| 144 | + o.setContent("正在进行登录操作{operation},您的验证码是{code}"); | |
| 145 | + o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 | |
| 146 | + o.setType(randomEle(SysSmsTemplateTypeEnum.values()).getType()); // 保证 type 的 范围 | |
| 147 | + }); | |
| 148 | + // mock 方法 | |
| 149 | + SysSmsChannelDO channelDO = randomPojo(SysSmsChannelDO.class, o -> { | |
| 150 | + o.setId(reqVO.getChannelId()); | |
| 151 | + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 开启,创建必须处于这个状态 | |
| 152 | + }); | |
| 153 | + when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO); | |
| 154 | + // mock 获得 API 短信模板成功 | |
| 155 | + when(smsClientFactory.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient); | |
| 156 | + when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn(randomPojo(SmsCommonResult.class, SmsTemplateRespDTO.class, | |
| 157 | + o -> o.setCode(GlobalErrorCodeConstants.SUCCESS.getCode()))); | |
| 158 | + | |
| 159 | + // 调用 | |
| 160 | + smsTemplateService.updateSmsTemplate(reqVO); | |
| 161 | + // 校验是否更新正确 | |
| 162 | + SysSmsTemplateDO smsTemplate = smsTemplateMapper.selectById(reqVO.getId()); // 获取最新的 | |
| 163 | + assertPojoEquals(reqVO, smsTemplate); | |
| 164 | + assertEquals(Lists.newArrayList("operation", "code"), smsTemplate.getParams()); | |
| 165 | + assertEquals(channelDO.getCode(), smsTemplate.getChannelCode()); | |
| 166 | + // 校验调用 | |
| 167 | + verify(smsProducer, times(1)).sendSmsTemplateRefreshMessage(); | |
| 168 | + } | |
| 169 | + | |
| 170 | + @Test | |
| 171 | + public void testUpdateSmsTemplate_notExists() { | |
| 172 | + // 准备参数 | |
| 173 | + SysSmsTemplateUpdateReqVO reqVO = randomPojo(SysSmsTemplateUpdateReqVO.class); | |
| 174 | + | |
| 175 | + // 调用, 并断言异常 | |
| 176 | + assertServiceException(() -> smsTemplateService.updateSmsTemplate(reqVO), SMS_TEMPLATE_NOT_EXISTS); | |
| 177 | + } | |
| 178 | + | |
| 179 | + @Test | |
| 180 | + public void testDeleteSmsTemplate_success() { | |
| 181 | + // mock 数据 | |
| 182 | + SysSmsTemplateDO dbSmsTemplate = randomSmsTemplateDO(); | |
| 183 | + smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据 | |
| 184 | + // 准备参数 | |
| 185 | + Long id = dbSmsTemplate.getId(); | |
| 186 | + | |
| 187 | + // 调用 | |
| 188 | + smsTemplateService.deleteSmsTemplate(id); | |
| 189 | + // 校验数据不存在了 | |
| 190 | + assertNull(smsTemplateMapper.selectById(id)); | |
| 191 | + // 校验调用 | |
| 192 | + verify(smsProducer, times(1)).sendSmsTemplateRefreshMessage(); | |
| 193 | + } | |
| 194 | + | |
| 195 | + @Test | |
| 196 | + public void testDeleteSmsTemplate_notExists() { | |
| 197 | + // 准备参数 | |
| 198 | + Long id = randomLongId(); | |
| 199 | + | |
| 200 | + // 调用, 并断言异常 | |
| 201 | + assertServiceException(() -> smsTemplateService.deleteSmsTemplate(id), SMS_TEMPLATE_NOT_EXISTS); | |
| 202 | + } | |
| 203 | + | |
| 204 | + @Test | |
| 205 | + public void testGetSmsTemplatePage() { | |
| 206 | + // mock 数据 | |
| 207 | + SysSmsTemplateDO dbSmsTemplate = randomPojo(SysSmsTemplateDO.class, o -> { // 等会查询到 | |
| 208 | + o.setType(SysSmsTemplateTypeEnum.PROMOTION.getType()); | |
| 209 | + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 210 | + o.setCode("yudaoyuanma"); | |
| 211 | + o.setContent("芋道源码"); | |
| 212 | + o.setApiTemplateId("yunai"); | |
| 213 | + o.setChannelId(1L); | |
| 214 | + o.setCreateTime(buildTime(2021, 11, 11)); | |
| 215 | + }); | |
| 216 | + smsTemplateMapper.insert(dbSmsTemplate); | |
| 217 | + // 测试 type 不匹配 | |
| 218 | + smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setType(SysSmsTemplateTypeEnum.VERIFICATION_CODE.getType()))); | |
| 219 | + // 测试 status 不匹配 | |
| 220 | + smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | |
| 221 | + // 测试 code 不匹配 | |
| 222 | + smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setCode("yuanma"))); | |
| 223 | + // 测试 content 不匹配 | |
| 224 | + smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setContent("源码"))); | |
| 225 | + // 测试 apiTemplateId 不匹配 | |
| 226 | + smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setApiTemplateId("nai"))); | |
| 227 | + // 测试 channelId 不匹配 | |
| 228 | + smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setChannelId(2L))); | |
| 229 | + // 测试 createTime 不匹配 | |
| 230 | + smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setCreateTime(buildTime(2021, 12, 12)))); | |
| 231 | + // 准备参数 | |
| 232 | + SysSmsTemplatePageReqVO reqVO = new SysSmsTemplatePageReqVO(); | |
| 233 | + reqVO.setType(SysSmsTemplateTypeEnum.PROMOTION.getType()); | |
| 234 | + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 235 | + reqVO.setCode("yudao"); | |
| 236 | + reqVO.setContent("芋道"); | |
| 237 | + reqVO.setApiTemplateId("yu"); | |
| 238 | + reqVO.setChannelId(1L); | |
| 239 | + reqVO.setBeginCreateTime(buildTime(2021, 11, 1)); | |
| 240 | + reqVO.setEndCreateTime(buildTime(2021, 12, 1)); | |
| 241 | + | |
| 242 | + // 调用 | |
| 243 | + PageResult<SysSmsTemplateDO> pageResult = smsTemplateService.getSmsTemplatePage(reqVO); | |
| 244 | + // 断言 | |
| 245 | + assertEquals(1, pageResult.getTotal()); | |
| 246 | + assertEquals(1, pageResult.getList().size()); | |
| 247 | + assertPojoEquals(dbSmsTemplate, pageResult.getList().get(0)); | |
| 248 | + } | |
| 249 | + | |
| 250 | + @Test | |
| 251 | + public void testGetSmsTemplateList() { | |
| 252 | + // mock 数据 | |
| 253 | + SysSmsTemplateDO dbSmsTemplate = randomPojo(SysSmsTemplateDO.class, o -> { // 等会查询到 | |
| 254 | + o.setType(SysSmsTemplateTypeEnum.PROMOTION.getType()); | |
| 255 | + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 256 | + o.setCode("yudaoyuanma"); | |
| 257 | + o.setContent("芋道源码"); | |
| 258 | + o.setApiTemplateId("yunai"); | |
| 259 | + o.setChannelId(1L); | |
| 260 | + o.setCreateTime(buildTime(2021, 11, 11)); | |
| 261 | + }); | |
| 262 | + smsTemplateMapper.insert(dbSmsTemplate); | |
| 263 | + // 测试 type 不匹配 | |
| 264 | + smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setType(SysSmsTemplateTypeEnum.VERIFICATION_CODE.getType()))); | |
| 265 | + // 测试 status 不匹配 | |
| 266 | + smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | |
| 267 | + // 测试 code 不匹配 | |
| 268 | + smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setCode("yuanma"))); | |
| 269 | + // 测试 content 不匹配 | |
| 270 | + smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setContent("源码"))); | |
| 271 | + // 测试 apiTemplateId 不匹配 | |
| 272 | + smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setApiTemplateId("nai"))); | |
| 273 | + // 测试 channelId 不匹配 | |
| 274 | + smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setChannelId(2L))); | |
| 275 | + // 测试 createTime 不匹配 | |
| 276 | + smsTemplateMapper.insert(ObjectUtils.clone(dbSmsTemplate, o -> o.setCreateTime(buildTime(2021, 12, 12)))); | |
| 277 | + // 准备参数 | |
| 278 | + SysSmsTemplateExportReqVO reqVO = new SysSmsTemplateExportReqVO(); | |
| 279 | + reqVO.setType(SysSmsTemplateTypeEnum.PROMOTION.getType()); | |
| 280 | + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 281 | + reqVO.setCode("yudao"); | |
| 282 | + reqVO.setContent("芋道"); | |
| 283 | + reqVO.setApiTemplateId("yu"); | |
| 284 | + reqVO.setChannelId(1L); | |
| 285 | + reqVO.setBeginCreateTime(buildTime(2021, 11, 1)); | |
| 286 | + reqVO.setEndCreateTime(buildTime(2021, 12, 1)); | |
| 287 | + | |
| 288 | + // 调用 | |
| 289 | + List<SysSmsTemplateDO> list = smsTemplateService.getSmsTemplateList(reqVO); | |
| 290 | + // 断言 | |
| 291 | + assertEquals(1, list.size()); | |
| 292 | + assertPojoEquals(dbSmsTemplate, list.get(0)); | |
| 293 | + } | |
| 294 | + | |
| 295 | + @Test | |
| 296 | + public void testCheckSmsChannel_success() { | |
| 297 | + // 准备参数 | |
| 298 | + Long channelId = randomLongId(); | |
| 299 | + // mock 方法 | |
| 300 | + SysSmsChannelDO channelDO = randomPojo(SysSmsChannelDO.class, o -> { | |
| 301 | + o.setId(channelId); | |
| 302 | + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 开启,创建必须处于这个状态 | |
| 303 | + }); | |
| 304 | + when(smsChannelService.getSmsChannel(eq(channelId))).thenReturn(channelDO); | |
| 305 | + | |
| 306 | + // 调用 | |
| 307 | + SysSmsChannelDO returnChannelDO = smsTemplateService.checkSmsChannel(channelId); | |
| 308 | + // 断言 | |
| 309 | + assertPojoEquals(returnChannelDO, channelDO); | |
| 310 | + } | |
| 311 | + | |
| 312 | + @Test | |
| 313 | + public void testCheckSmsChannel_notExists() { | |
| 314 | + // 准备参数 | |
| 315 | + Long channelId = randomLongId(); | |
| 316 | + | |
| 317 | + // 调用,校验异常 | |
| 318 | + assertServiceException(() -> smsTemplateService.checkSmsChannel(channelId), | |
| 319 | + SMS_CHANNEL_NOT_EXISTS); | |
| 320 | + } | |
| 321 | + | |
| 322 | + @Test | |
| 323 | + public void testCheckSmsChannel_disable() { | |
| 324 | + // 准备参数 | |
| 325 | + Long channelId = randomLongId(); | |
| 326 | + // mock 方法 | |
| 327 | + SysSmsChannelDO channelDO = randomPojo(SysSmsChannelDO.class, o -> { | |
| 328 | + o.setId(channelId); | |
| 329 | + o.setStatus(CommonStatusEnum.DISABLE.getStatus()); // 保证 status 禁用,触发失败 | |
| 330 | + }); | |
| 331 | + when(smsChannelService.getSmsChannel(eq(channelId))).thenReturn(channelDO); | |
| 332 | + | |
| 333 | + // 调用,校验异常 | |
| 334 | + assertServiceException(() -> smsTemplateService.checkSmsChannel(channelId), | |
| 335 | + SMS_CHANNEL_DISABLE); | |
| 336 | + } | |
| 337 | + | |
| 338 | + @Test | |
| 339 | + public void testCheckDictDataValueUnique_success() { | |
| 340 | + // 调用,成功 | |
| 341 | + smsTemplateService.checkSmsTemplateCodeDuplicate(randomLongId(), randomString()); | |
| 342 | + } | |
| 343 | + | |
| 344 | + @Test | |
| 345 | + public void testCheckSmsTemplateCodeDuplicate_valueDuplicateForCreate() { | |
| 346 | + // 准备参数 | |
| 347 | + String code = randomString(); | |
| 348 | + // mock 数据 | |
| 349 | + smsTemplateMapper.insert(randomSmsTemplateDO(o -> o.setCode(code))); | |
| 350 | + | |
| 351 | + // 调用,校验异常 | |
| 352 | + assertServiceException(() -> smsTemplateService.checkSmsTemplateCodeDuplicate(null, code), | |
| 353 | + SMS_TEMPLATE_CODE_DUPLICATE, code); | |
| 354 | + } | |
| 355 | + | |
| 356 | + @Test | |
| 357 | + public void testCheckDictDataValueUnique_valueDuplicateForUpdate() { | |
| 358 | + // 准备参数 | |
| 359 | + Long id = randomLongId(); | |
| 360 | + String code = randomString(); | |
| 361 | + // mock 数据 | |
| 362 | + smsTemplateMapper.insert(randomSmsTemplateDO(o -> o.setCode(code))); | |
| 363 | + | |
| 364 | + // 调用,校验异常 | |
| 365 | + assertServiceException(() -> smsTemplateService.checkSmsTemplateCodeDuplicate(id, code), | |
| 366 | + SMS_TEMPLATE_CODE_DUPLICATE, code); | |
| 367 | + } | |
| 368 | + | |
| 369 | + // ========== 随机对象 ========== | |
| 370 | + | |
| 371 | + @SafeVarargs | |
| 372 | + private static SysSmsTemplateDO randomSmsTemplateDO(Consumer<SysSmsTemplateDO>... consumers) { | |
| 373 | + Consumer<SysSmsTemplateDO> consumer = (o) -> { | |
| 374 | + o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 | |
| 375 | + o.setType(randomEle(SysSmsTemplateTypeEnum.values()).getType()); // 保证 type 的 范围 | |
| 376 | + }; | |
| 377 | + return randomPojo(SysSmsTemplateDO.class, ArrayUtils.append(consumer, consumers)); | |
| 378 | + } | |
| 379 | + | |
| 380 | +} | ... | ... |
src/test/java/cn/iocoder/dashboard/modules/system/service/user/SysUserServiceImplTest.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.user; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.util.RandomUtil; | |
| 5 | +import cn.iocoder.dashboard.BaseDbUnitTest; | |
| 6 | +import cn.iocoder.dashboard.common.enums.CommonStatusEnum; | |
| 7 | +import cn.iocoder.dashboard.common.pojo.PageResult; | |
| 8 | +import cn.iocoder.dashboard.modules.infra.service.file.InfFileService; | |
| 9 | +import cn.iocoder.dashboard.modules.system.controller.user.vo.profile.SysUserProfileUpdatePasswordReqVO; | |
| 10 | +import cn.iocoder.dashboard.modules.system.controller.user.vo.profile.SysUserProfileUpdateReqVO; | |
| 11 | +import cn.iocoder.dashboard.modules.system.controller.user.vo.user.*; | |
| 12 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.dept.SysDeptDO; | |
| 13 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.dept.SysPostDO; | |
| 14 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO; | |
| 15 | +import cn.iocoder.dashboard.modules.system.dal.mysql.user.SysUserMapper; | |
| 16 | +import cn.iocoder.dashboard.modules.system.enums.common.SysSexEnum; | |
| 17 | +import cn.iocoder.dashboard.modules.system.service.dept.SysDeptService; | |
| 18 | +import cn.iocoder.dashboard.modules.system.service.dept.SysPostService; | |
| 19 | +import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService; | |
| 20 | +import cn.iocoder.dashboard.util.collection.ArrayUtils; | |
| 21 | +import cn.iocoder.dashboard.util.collection.CollectionUtils; | |
| 22 | +import cn.iocoder.dashboard.util.object.ObjectUtils; | |
| 23 | +import org.junit.jupiter.api.Test; | |
| 24 | +import org.mockito.stubbing.Answer; | |
| 25 | +import org.springframework.boot.test.mock.mockito.MockBean; | |
| 26 | +import org.springframework.context.annotation.Import; | |
| 27 | +import org.springframework.security.crypto.password.PasswordEncoder; | |
| 28 | + | |
| 29 | +import javax.annotation.Resource; | |
| 30 | +import java.io.ByteArrayInputStream; | |
| 31 | +import java.util.List; | |
| 32 | +import java.util.Set; | |
| 33 | +import java.util.function.Consumer; | |
| 34 | + | |
| 35 | +import static cn.hutool.core.util.RandomUtil.randomBytes; | |
| 36 | +import static cn.hutool.core.util.RandomUtil.randomEle; | |
| 37 | +import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; | |
| 38 | +import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals; | |
| 39 | +import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException; | |
| 40 | +import static cn.iocoder.dashboard.util.RandomUtils.*; | |
| 41 | +import static cn.iocoder.dashboard.util.date.DateUtils.buildTime; | |
| 42 | +import static org.assertj.core.util.Lists.newArrayList; | |
| 43 | +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| 44 | +import static org.junit.jupiter.api.Assertions.assertNull; | |
| 45 | +import static org.mockito.ArgumentMatchers.*; | |
| 46 | +import static org.mockito.Mockito.*; | |
| 47 | + | |
| 48 | +/** | |
| 49 | + * {@link SysUserService} 的单元测试类 | |
| 50 | + * | |
| 51 | + * @author zxl | |
| 52 | + */ | |
| 53 | +@Import(SysUserServiceImpl.class) | |
| 54 | +public class SysUserServiceImplTest extends BaseDbUnitTest { | |
| 55 | + | |
| 56 | + @Resource | |
| 57 | + private SysUserServiceImpl userService; | |
| 58 | + | |
| 59 | + @Resource | |
| 60 | + private SysUserMapper userMapper; | |
| 61 | + | |
| 62 | + @MockBean | |
| 63 | + private SysDeptService deptService; | |
| 64 | + @MockBean | |
| 65 | + private SysPostService postService; | |
| 66 | + @MockBean | |
| 67 | + private SysPermissionService permissionService; | |
| 68 | + @MockBean | |
| 69 | + private PasswordEncoder passwordEncoder; | |
| 70 | + @MockBean | |
| 71 | + private InfFileService fileService; | |
| 72 | + | |
| 73 | + @Test | |
| 74 | + public void testCreatUser_success() { | |
| 75 | + // 准备参数 | |
| 76 | + SysUserCreateReqVO reqVO = randomPojo(SysUserCreateReqVO.class, o -> { | |
| 77 | + o.setSex(RandomUtil.randomEle(SysSexEnum.values()).getSex()); | |
| 78 | + o.setMobile(randomString()); | |
| 79 | + }); | |
| 80 | + // mock deptService 的方法 | |
| 81 | + SysDeptDO dept = randomPojo(SysDeptDO.class, o -> { | |
| 82 | + o.setId(reqVO.getDeptId()); | |
| 83 | + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 84 | + }); | |
| 85 | + when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); | |
| 86 | + // mock postService 的方法 | |
| 87 | + List<SysPostDO> posts = CollectionUtils.convertList(reqVO.getPostIds(), postId -> | |
| 88 | + randomPojo(SysPostDO.class, o -> { | |
| 89 | + o.setId(postId); | |
| 90 | + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 91 | + })); | |
| 92 | + when(postService.getPosts(eq(reqVO.getPostIds()), isNull())).thenReturn(posts); | |
| 93 | + // mock passwordEncoder 的方法 | |
| 94 | + when(passwordEncoder.encode(eq(reqVO.getPassword()))).thenReturn("yudaoyuanma"); | |
| 95 | + | |
| 96 | + // 调用 | |
| 97 | + Long userId = userService.createUser(reqVO); | |
| 98 | + // 断言 | |
| 99 | + SysUserDO user = userMapper.selectById(userId); | |
| 100 | + assertPojoEquals(reqVO, user, "password"); | |
| 101 | + assertEquals("yudaoyuanma", user.getPassword()); | |
| 102 | + assertEquals(CommonStatusEnum.ENABLE.getStatus(), user.getStatus()); | |
| 103 | + } | |
| 104 | + | |
| 105 | + @Test | |
| 106 | + public void testUpdateUser_success() { | |
| 107 | + // mock 数据 | |
| 108 | + SysUserDO dbUser = randomSysUserDO(); | |
| 109 | + userMapper.insert(dbUser); | |
| 110 | + // 准备参数 | |
| 111 | + SysUserUpdateReqVO reqVO = randomPojo(SysUserUpdateReqVO.class, o -> { | |
| 112 | + o.setId(dbUser.getId()); | |
| 113 | + o.setSex(RandomUtil.randomEle(SysSexEnum.values()).getSex()); | |
| 114 | + o.setMobile(randomString()); | |
| 115 | + }); | |
| 116 | + // mock deptService 的方法 | |
| 117 | + SysDeptDO dept = randomPojo(SysDeptDO.class, o -> { | |
| 118 | + o.setId(reqVO.getDeptId()); | |
| 119 | + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 120 | + }); | |
| 121 | + when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); | |
| 122 | + // mock postService 的方法 | |
| 123 | + List<SysPostDO> posts = CollectionUtils.convertList(reqVO.getPostIds(), postId -> | |
| 124 | + randomPojo(SysPostDO.class, o -> { | |
| 125 | + o.setId(postId); | |
| 126 | + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 127 | + })); | |
| 128 | + when(postService.getPosts(eq(reqVO.getPostIds()), isNull())).thenReturn(posts); | |
| 129 | + | |
| 130 | + // 调用 | |
| 131 | + userService.updateUser(reqVO); | |
| 132 | + // 断言 | |
| 133 | + SysUserDO user = userMapper.selectById(reqVO.getId()); | |
| 134 | + assertPojoEquals(reqVO, user); | |
| 135 | + } | |
| 136 | + | |
| 137 | + @Test | |
| 138 | + public void testUpdateUserProfile_success() { | |
| 139 | + // mock 数据 | |
| 140 | + SysUserDO dbUser = randomSysUserDO(); | |
| 141 | + userMapper.insert(dbUser); | |
| 142 | + // 准备参数 | |
| 143 | + Long userId = dbUser.getId(); | |
| 144 | + SysUserProfileUpdateReqVO reqVO = randomPojo(SysUserProfileUpdateReqVO.class, o -> { | |
| 145 | + o.setMobile(randomString()); | |
| 146 | + o.setSex(RandomUtil.randomEle(SysSexEnum.values()).getSex()); | |
| 147 | + }); | |
| 148 | + | |
| 149 | + // 调用 | |
| 150 | + userService.updateUserProfile(userId, reqVO); | |
| 151 | + // 断言 | |
| 152 | + SysUserDO user = userMapper.selectById(userId); | |
| 153 | + assertPojoEquals(reqVO, user); | |
| 154 | + } | |
| 155 | + | |
| 156 | + @Test | |
| 157 | + public void testUpdateUserPassword_success() { | |
| 158 | + // mock 数据 | |
| 159 | + SysUserDO dbUser = randomSysUserDO(o -> o.setPassword("encode:yudao")); | |
| 160 | + userMapper.insert(dbUser); | |
| 161 | + // 准备参数 | |
| 162 | + Long userId = dbUser.getId(); | |
| 163 | + SysUserProfileUpdatePasswordReqVO reqVO = randomPojo(SysUserProfileUpdatePasswordReqVO.class, o -> { | |
| 164 | + o.setOldPassword("yudao"); | |
| 165 | + o.setNewPassword("yuanma"); | |
| 166 | + }); | |
| 167 | + // mock 方法 | |
| 168 | + when(passwordEncoder.encode(anyString())).then( | |
| 169 | + (Answer<String>) invocationOnMock -> "encode:" + invocationOnMock.getArgument(0)); | |
| 170 | + when(passwordEncoder.matches(eq(reqVO.getOldPassword()), eq(dbUser.getPassword()))).thenReturn(true); | |
| 171 | + | |
| 172 | + // 调用 | |
| 173 | + userService.updateUserPassword(userId, reqVO); | |
| 174 | + // 断言 | |
| 175 | + SysUserDO user = userMapper.selectById(userId); | |
| 176 | + assertEquals("encode:yuanma", user.getPassword()); | |
| 177 | + } | |
| 178 | + | |
| 179 | + @Test | |
| 180 | + public void testUpdateUserAvatar_success() { | |
| 181 | + // mock 数据 | |
| 182 | + SysUserDO dbUser = randomSysUserDO(); | |
| 183 | + userMapper.insert(dbUser); | |
| 184 | + // 准备参数 | |
| 185 | + Long userId = dbUser.getId(); | |
| 186 | + byte[] avatarFileBytes = randomBytes(10); | |
| 187 | + ByteArrayInputStream avatarFile = new ByteArrayInputStream(avatarFileBytes); | |
| 188 | + // mock 方法 | |
| 189 | + String avatar = randomString(); | |
| 190 | + when(fileService.createFile(anyString(), eq(avatarFileBytes))).thenReturn(avatar); | |
| 191 | + | |
| 192 | + // 调用 | |
| 193 | + userService.updateUserAvatar(userId, avatarFile); | |
| 194 | + // 断言 | |
| 195 | + SysUserDO user = userMapper.selectById(userId); | |
| 196 | + assertEquals(avatar, user.getAvatar()); | |
| 197 | + } | |
| 198 | + | |
| 199 | + @Test | |
| 200 | + public void testUpdateUserPassword02_success() { | |
| 201 | + // mock 数据 | |
| 202 | + SysUserDO dbUser = randomSysUserDO(); | |
| 203 | + userMapper.insert(dbUser); | |
| 204 | + // 准备参数 | |
| 205 | + Long userId = dbUser.getId(); | |
| 206 | + String password = "yudao"; | |
| 207 | + // mock 方法 | |
| 208 | + when(passwordEncoder.encode(anyString())).then( | |
| 209 | + (Answer<String>) invocationOnMock -> "encode:" + invocationOnMock.getArgument(0)); | |
| 210 | + | |
| 211 | + // 调用 | |
| 212 | + userService.updateUserPassword(userId, password); | |
| 213 | + // 断言 | |
| 214 | + SysUserDO user = userMapper.selectById(userId); | |
| 215 | + assertEquals("encode:" + password, user.getPassword()); | |
| 216 | + } | |
| 217 | + | |
| 218 | + @Test | |
| 219 | + public void testUpdateUserStatus() { | |
| 220 | + // mock 数据 | |
| 221 | + SysUserDO dbUser = randomSysUserDO(); | |
| 222 | + userMapper.insert(dbUser); | |
| 223 | + // 准备参数 | |
| 224 | + Long userId = dbUser.getId(); | |
| 225 | + Integer status = randomCommonStatus(); | |
| 226 | + | |
| 227 | + // 调用 | |
| 228 | + userService.updateUserStatus(userId, status); | |
| 229 | + // 断言 | |
| 230 | + SysUserDO user = userMapper.selectById(userId); | |
| 231 | + assertEquals(status, user.getStatus()); | |
| 232 | + } | |
| 233 | + | |
| 234 | + @Test | |
| 235 | + public void testDeleteUser_success(){ | |
| 236 | + // mock 数据 | |
| 237 | + SysUserDO dbUser = randomSysUserDO(); | |
| 238 | + userMapper.insert(dbUser); | |
| 239 | + // 准备参数 | |
| 240 | + Long userId = dbUser.getId(); | |
| 241 | + | |
| 242 | + // 调用数据 | |
| 243 | + userService.deleteUser(userId); | |
| 244 | + // 校验结果 | |
| 245 | + assertNull(userService.getUser(userId)); | |
| 246 | + // 校验调用次数 | |
| 247 | + verify(permissionService, times(1)).processUserDeleted(eq(userId)); | |
| 248 | + } | |
| 249 | + | |
| 250 | + @Test | |
| 251 | + public void testGetUserPage() { | |
| 252 | + // mock 数据 | |
| 253 | + SysUserDO dbUser = initGetUserPageData(); | |
| 254 | + // 准备参数 | |
| 255 | + SysUserPageReqVO reqVO = new SysUserPageReqVO(); | |
| 256 | + reqVO.setUsername("yudao"); | |
| 257 | + reqVO.setMobile("1560"); | |
| 258 | + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 259 | + reqVO.setBeginTime(buildTime(2020, 12, 1)); | |
| 260 | + reqVO.setEndTime(buildTime(2020, 12, 24)); | |
| 261 | + reqVO.setDeptId(1L); // 其中,1L 是 2L 的父部门 | |
| 262 | + // mock 方法 | |
| 263 | + List<SysDeptDO> deptList = newArrayList(randomPojo(SysDeptDO.class, o -> o.setId(2L))); | |
| 264 | + when(deptService.getDeptsByParentIdFromCache(eq(reqVO.getDeptId()), eq(true))).thenReturn(deptList); | |
| 265 | + | |
| 266 | + // 调用 | |
| 267 | + PageResult<SysUserDO> pageResult = userService.getUserPage(reqVO); | |
| 268 | + // 断言 | |
| 269 | + assertEquals(1, pageResult.getTotal()); | |
| 270 | + assertEquals(1, pageResult.getList().size()); | |
| 271 | + assertPojoEquals(dbUser, pageResult.getList().get(0)); | |
| 272 | + } | |
| 273 | + | |
| 274 | + @Test | |
| 275 | + public void testGetUsers() { | |
| 276 | + // mock 数据 | |
| 277 | + SysUserDO dbUser = initGetUserPageData(); | |
| 278 | + // 准备参数 | |
| 279 | + SysUserExportReqVO reqVO = new SysUserExportReqVO(); | |
| 280 | + reqVO.setUsername("yudao"); | |
| 281 | + reqVO.setMobile("1560"); | |
| 282 | + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 283 | + reqVO.setBeginTime(buildTime(2020, 12, 1)); | |
| 284 | + reqVO.setEndTime(buildTime(2020, 12, 24)); | |
| 285 | + reqVO.setDeptId(1L); // 其中,1L 是 2L 的父部门 | |
| 286 | + // mock 方法 | |
| 287 | + List<SysDeptDO> deptList = newArrayList(randomPojo(SysDeptDO.class, o -> o.setId(2L))); | |
| 288 | + when(deptService.getDeptsByParentIdFromCache(eq(reqVO.getDeptId()), eq(true))).thenReturn(deptList); | |
| 289 | + | |
| 290 | + // 调用 | |
| 291 | + List<SysUserDO> list = userService.getUsers(reqVO); | |
| 292 | + // 断言 | |
| 293 | + assertEquals(1, list.size()); | |
| 294 | + assertPojoEquals(dbUser, list.get(0)); | |
| 295 | + } | |
| 296 | + | |
| 297 | + /** | |
| 298 | + * 初始化 getUserPage 方法的测试数据 | |
| 299 | + */ | |
| 300 | + private SysUserDO initGetUserPageData() { | |
| 301 | + // mock 数据 | |
| 302 | + SysUserDO dbUser = randomSysUserDO(o -> { // 等会查询到 | |
| 303 | + o.setUsername("yudaoyuanma"); | |
| 304 | + o.setMobile("15601691300"); | |
| 305 | + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 306 | + o.setCreateTime(buildTime(2020, 12, 12)); | |
| 307 | + o.setDeptId(2L); | |
| 308 | + }); | |
| 309 | + userMapper.insert(dbUser); | |
| 310 | + // 测试 username 不匹配 | |
| 311 | + userMapper.insert(ObjectUtils.clone(dbUser, o -> o.setUsername("yuanma"))); | |
| 312 | + // 测试 mobile 不匹配 | |
| 313 | + userMapper.insert(ObjectUtils.clone(dbUser, o -> o.setMobile("18818260888"))); | |
| 314 | + // 测试 status 不匹配 | |
| 315 | + userMapper.insert(ObjectUtils.clone(dbUser, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | |
| 316 | + // 测试 createTime 不匹配 | |
| 317 | + userMapper.insert(ObjectUtils.clone(dbUser, o -> o.setCreateTime(buildTime(2020, 11, 11)))); | |
| 318 | + // 测试 dept 不匹配 | |
| 319 | + userMapper.insert(ObjectUtils.clone(dbUser, o -> o.setDeptId(0L))); | |
| 320 | + return dbUser; | |
| 321 | + } | |
| 322 | + | |
| 323 | + /** | |
| 324 | + * 情况一,校验不通过,导致插入失败 | |
| 325 | + */ | |
| 326 | + @Test | |
| 327 | + public void testImportUsers_01() { | |
| 328 | + // 准备参数 | |
| 329 | + SysUserImportExcelVO importUser = randomPojo(SysUserImportExcelVO.class); | |
| 330 | + // mock 方法 | |
| 331 | + | |
| 332 | + // 调用 | |
| 333 | + SysUserImportRespVO respVO = userService.importUsers(newArrayList(importUser), true); | |
| 334 | + // 断言 | |
| 335 | + assertEquals(0, respVO.getCreateUsernames().size()); | |
| 336 | + assertEquals(0, respVO.getUpdateUsernames().size()); | |
| 337 | + assertEquals(1, respVO.getFailureUsernames().size()); | |
| 338 | + assertEquals(DEPT_NOT_FOUND.getMsg(), respVO.getFailureUsernames().get(importUser.getUsername())); | |
| 339 | + } | |
| 340 | + | |
| 341 | + /** | |
| 342 | + * 情况二,不存在,进行插入 | |
| 343 | + */ | |
| 344 | + @Test | |
| 345 | + public void testImportUsers_02() { | |
| 346 | + // 准备参数 | |
| 347 | + SysUserImportExcelVO importUser = randomPojo(SysUserImportExcelVO.class, o -> { | |
| 348 | + o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 | |
| 349 | + o.setSex(randomEle(SysSexEnum.values()).getSex()); // 保证 sex 的范围 | |
| 350 | + }); | |
| 351 | + // mock deptService 的方法 | |
| 352 | + SysDeptDO dept = randomPojo(SysDeptDO.class, o -> { | |
| 353 | + o.setId(importUser.getDeptId()); | |
| 354 | + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 355 | + }); | |
| 356 | + when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); | |
| 357 | + // mock passwordEncoder 的方法 | |
| 358 | + when(passwordEncoder.encode(eq("yudaoyuanma"))).thenReturn("java"); | |
| 359 | + | |
| 360 | + // 调用 | |
| 361 | + SysUserImportRespVO respVO = userService.importUsers(newArrayList(importUser), true); | |
| 362 | + // 断言 | |
| 363 | + assertEquals(1, respVO.getCreateUsernames().size()); | |
| 364 | + SysUserDO user = userMapper.selectByUsername(respVO.getCreateUsernames().get(0)); | |
| 365 | + assertPojoEquals(importUser, user); | |
| 366 | + assertEquals("java", user.getPassword()); | |
| 367 | + assertEquals(0, respVO.getUpdateUsernames().size()); | |
| 368 | + assertEquals(0, respVO.getFailureUsernames().size()); | |
| 369 | + } | |
| 370 | + | |
| 371 | + /** | |
| 372 | + * 情况三,存在,但是不强制更新 | |
| 373 | + */ | |
| 374 | + @Test | |
| 375 | + public void testImportUsers_03() { | |
| 376 | + // mock 数据 | |
| 377 | + SysUserDO dbUser = randomSysUserDO(); | |
| 378 | + userMapper.insert(dbUser); | |
| 379 | + // 准备参数 | |
| 380 | + SysUserImportExcelVO importUser = randomPojo(SysUserImportExcelVO.class, o -> { | |
| 381 | + o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 | |
| 382 | + o.setSex(randomEle(SysSexEnum.values()).getSex()); // 保证 sex 的范围 | |
| 383 | + o.setUsername(dbUser.getUsername()); | |
| 384 | + }); | |
| 385 | + // mock deptService 的方法 | |
| 386 | + SysDeptDO dept = randomPojo(SysDeptDO.class, o -> { | |
| 387 | + o.setId(importUser.getDeptId()); | |
| 388 | + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 389 | + }); | |
| 390 | + when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); | |
| 391 | + | |
| 392 | + // 调用 | |
| 393 | + SysUserImportRespVO respVO = userService.importUsers(newArrayList(importUser), false); | |
| 394 | + // 断言 | |
| 395 | + assertEquals(0, respVO.getCreateUsernames().size()); | |
| 396 | + assertEquals(0, respVO.getUpdateUsernames().size()); | |
| 397 | + assertEquals(1, respVO.getFailureUsernames().size()); | |
| 398 | + assertEquals(USER_USERNAME_EXISTS.getMsg(), respVO.getFailureUsernames().get(importUser.getUsername())); | |
| 399 | + } | |
| 400 | + | |
| 401 | + /** | |
| 402 | + * 情况四,存在,强制更新 | |
| 403 | + */ | |
| 404 | + @Test | |
| 405 | + public void testImportUsers_04() { | |
| 406 | + // mock 数据 | |
| 407 | + SysUserDO dbUser = randomSysUserDO(); | |
| 408 | + userMapper.insert(dbUser); | |
| 409 | + // 准备参数 | |
| 410 | + SysUserImportExcelVO importUser = randomPojo(SysUserImportExcelVO.class, o -> { | |
| 411 | + o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 | |
| 412 | + o.setSex(randomEle(SysSexEnum.values()).getSex()); // 保证 sex 的范围 | |
| 413 | + o.setUsername(dbUser.getUsername()); | |
| 414 | + }); | |
| 415 | + // mock deptService 的方法 | |
| 416 | + SysDeptDO dept = randomPojo(SysDeptDO.class, o -> { | |
| 417 | + o.setId(importUser.getDeptId()); | |
| 418 | + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 419 | + }); | |
| 420 | + when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); | |
| 421 | + | |
| 422 | + // 调用 | |
| 423 | + SysUserImportRespVO respVO = userService.importUsers(newArrayList(importUser), true); | |
| 424 | + // 断言 | |
| 425 | + assertEquals(0, respVO.getCreateUsernames().size()); | |
| 426 | + assertEquals(1, respVO.getUpdateUsernames().size()); | |
| 427 | + SysUserDO user = userMapper.selectByUsername(respVO.getUpdateUsernames().get(0)); | |
| 428 | + assertPojoEquals(importUser, user); | |
| 429 | + assertEquals(0, respVO.getFailureUsernames().size()); | |
| 430 | + } | |
| 431 | + | |
| 432 | + @Test | |
| 433 | + public void testCheckUserExists_notExists() { | |
| 434 | + assertServiceException(() -> userService.checkUserExists(randomLongId()), USER_NOT_EXISTS); | |
| 435 | + } | |
| 436 | + | |
| 437 | + @Test | |
| 438 | + public void testCheckUsernameUnique_usernameExistsForCreate() { | |
| 439 | + // 准备参数 | |
| 440 | + String username = randomString(); | |
| 441 | + // mock 数据 | |
| 442 | + userMapper.insert(randomSysUserDO(o -> o.setUsername(username))); | |
| 443 | + | |
| 444 | + // 调用,校验异常 | |
| 445 | + assertServiceException(() -> userService.checkUsernameUnique(null, username), | |
| 446 | + USER_USERNAME_EXISTS); | |
| 447 | + } | |
| 448 | + | |
| 449 | + @Test | |
| 450 | + public void testCheckUsernameUnique_usernameExistsForUpdate() { | |
| 451 | + // 准备参数 | |
| 452 | + Long id = randomLongId(); | |
| 453 | + String username = randomString(); | |
| 454 | + // mock 数据 | |
| 455 | + userMapper.insert(randomSysUserDO(o -> o.setUsername(username))); | |
| 456 | + | |
| 457 | + // 调用,校验异常 | |
| 458 | + assertServiceException(() -> userService.checkUsernameUnique(id, username), | |
| 459 | + USER_USERNAME_EXISTS); | |
| 460 | + } | |
| 461 | + | |
| 462 | + @Test | |
| 463 | + public void testCheckEmailUnique_emailExistsForCreate() { | |
| 464 | + // 准备参数 | |
| 465 | + String email = randomString(); | |
| 466 | + // mock 数据 | |
| 467 | + userMapper.insert(randomSysUserDO(o -> o.setEmail(email))); | |
| 468 | + | |
| 469 | + // 调用,校验异常 | |
| 470 | + assertServiceException(() -> userService.checkEmailUnique(null, email), | |
| 471 | + USER_EMAIL_EXISTS); | |
| 472 | + } | |
| 473 | + | |
| 474 | + @Test | |
| 475 | + public void testCheckEmailUnique_emailExistsForUpdate() { | |
| 476 | + // 准备参数 | |
| 477 | + Long id = randomLongId(); | |
| 478 | + String email = randomString(); | |
| 479 | + // mock 数据 | |
| 480 | + userMapper.insert(randomSysUserDO(o -> o.setEmail(email))); | |
| 481 | + | |
| 482 | + // 调用,校验异常 | |
| 483 | + assertServiceException(() -> userService.checkEmailUnique(id, email), | |
| 484 | + USER_EMAIL_EXISTS); | |
| 485 | + } | |
| 486 | + | |
| 487 | + @Test | |
| 488 | + public void testCheckMobileUnique_mobileExistsForCreate() { | |
| 489 | + // 准备参数 | |
| 490 | + String mobile = randomString(); | |
| 491 | + // mock 数据 | |
| 492 | + userMapper.insert(randomSysUserDO(o -> o.setMobile(mobile))); | |
| 493 | + | |
| 494 | + // 调用,校验异常 | |
| 495 | + assertServiceException(() -> userService.checkMobileUnique(null, mobile), | |
| 496 | + USER_MOBILE_EXISTS); | |
| 497 | + } | |
| 498 | + | |
| 499 | + @Test | |
| 500 | + public void testCheckMobileUnique_mobileExistsForUpdate() { | |
| 501 | + // 准备参数 | |
| 502 | + Long id = randomLongId(); | |
| 503 | + String mobile = randomString(); | |
| 504 | + // mock 数据 | |
| 505 | + userMapper.insert(randomSysUserDO(o -> o.setMobile(mobile))); | |
| 506 | + | |
| 507 | + // 调用,校验异常 | |
| 508 | + assertServiceException(() -> userService.checkMobileUnique(id, mobile), | |
| 509 | + USER_MOBILE_EXISTS); | |
| 510 | + } | |
| 511 | + | |
| 512 | + @Test | |
| 513 | + public void testCheckDeptEnable_notFound() { | |
| 514 | + assertServiceException(() -> userService.checkDeptEnable(randomLongId()), | |
| 515 | + DEPT_NOT_FOUND); | |
| 516 | + } | |
| 517 | + | |
| 518 | + @Test | |
| 519 | + public void testCheckDeptEnable_notEnable() { | |
| 520 | + // 准备参数 | |
| 521 | + Long deptId = randomLongId(); | |
| 522 | + // mock deptService 的方法 | |
| 523 | + SysDeptDO dept = randomPojo(SysDeptDO.class, o -> { | |
| 524 | + o.setId(deptId); | |
| 525 | + o.setStatus(CommonStatusEnum.DISABLE.getStatus()); | |
| 526 | + }); | |
| 527 | + when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); | |
| 528 | + | |
| 529 | + // 调用,校验异常 | |
| 530 | + assertServiceException(() -> userService.checkDeptEnable(deptId), | |
| 531 | + DEPT_NOT_ENABLE); | |
| 532 | + } | |
| 533 | + | |
| 534 | + @Test | |
| 535 | + public void testCheckPostEnable_notFound() { | |
| 536 | + assertServiceException(() -> userService.checkPostEnable(randomSet(Long.class)), | |
| 537 | + POST_NOT_FOUND); | |
| 538 | + } | |
| 539 | + | |
| 540 | + @Test | |
| 541 | + public void testCheckPostEnable_notEnable() { | |
| 542 | + // 准备参数 | |
| 543 | + Set<Long> postIds = randomSet(Long.class); | |
| 544 | + // mock postService 的方法 | |
| 545 | + List<SysPostDO> posts = CollectionUtils.convertList(postIds, postId -> | |
| 546 | + randomPojo(SysPostDO.class, o -> { | |
| 547 | + o.setId(postId); | |
| 548 | + o.setStatus(CommonStatusEnum.DISABLE.getStatus()); | |
| 549 | + })); | |
| 550 | + when(postService.getPosts(eq(postIds), isNull())).thenReturn(posts); | |
| 551 | + | |
| 552 | + // 调用,校验异常 | |
| 553 | + assertServiceException(() -> userService.checkPostEnable(postIds), | |
| 554 | + POST_NOT_ENABLE, CollUtil.getFirst(posts).getName()); | |
| 555 | + } | |
| 556 | + | |
| 557 | + @Test | |
| 558 | + public void testCheckOldPassword_notExists() { | |
| 559 | + assertServiceException(() -> userService.checkOldPassword(randomLongId(), randomString()), | |
| 560 | + USER_NOT_EXISTS); | |
| 561 | + } | |
| 562 | + | |
| 563 | + @Test | |
| 564 | + public void testCheckOldPassword_passwordFailed() { | |
| 565 | + // mock 数据 | |
| 566 | + SysUserDO user = randomSysUserDO(); | |
| 567 | + userMapper.insert(user); | |
| 568 | + // 准备参数 | |
| 569 | + Long id = user.getId(); | |
| 570 | + String oldPassword = user.getPassword(); | |
| 571 | + | |
| 572 | + // 调用,校验异常 | |
| 573 | + assertServiceException(() -> userService.checkOldPassword(id, oldPassword), | |
| 574 | + USER_PASSWORD_FAILED); | |
| 575 | + // 校验调用 | |
| 576 | + verify(passwordEncoder, times(1)).matches(eq(oldPassword), eq(user.getPassword())); | |
| 577 | + } | |
| 578 | + | |
| 579 | + // ========== 随机对象 ========== | |
| 580 | + | |
| 581 | + @SafeVarargs | |
| 582 | + private static SysUserDO randomSysUserDO(Consumer<SysUserDO>... consumers) { | |
| 583 | + Consumer<SysUserDO> consumer = (o) -> { | |
| 584 | + o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 | |
| 585 | + o.setSex(randomEle(SysSexEnum.values()).getSex()); // 保证 sex 的范围 | |
| 586 | + }; | |
| 587 | + return randomPojo(SysUserDO.class, ArrayUtils.append(consumer, consumers)); | |
| 588 | + } | |
| 589 | + | |
| 590 | +} | ... | ... |
src/test/java/cn/iocoder/dashboard/util/AopTargetUtils.java
| ... | ... | @@ -5,43 +5,42 @@ import org.springframework.aop.framework.AdvisedSupport; |
| 5 | 5 | import org.springframework.aop.framework.AopProxy; |
| 6 | 6 | import org.springframework.aop.support.AopUtils; |
| 7 | 7 | |
| 8 | -import java.lang.reflect.Field; | |
| 9 | - | |
| 10 | 8 | /** |
| 11 | - * http://www.bubuko.com/infodetail-3471885.html | |
| 9 | + * Spring AOP 工具类 | |
| 10 | + * | |
| 11 | + * 参考波克尔 http://www.bubuko.com/infodetail-3471885.html 实现 | |
| 12 | 12 | */ |
| 13 | 13 | public class AopTargetUtils { |
| 14 | 14 | |
| 15 | 15 | /** |
| 16 | - * 获取 目标对象 | |
| 16 | + * 获取代理的目标对象 | |
| 17 | 17 | * |
| 18 | 18 | * @param proxy 代理对象 |
| 19 | - * @return | |
| 20 | - * @throws Exception | |
| 19 | + * @return 目标对象 | |
| 21 | 20 | */ |
| 22 | 21 | public static Object getTarget(Object proxy) throws Exception { |
| 22 | + // 不是代理对象 | |
| 23 | 23 | if (!AopUtils.isAopProxy(proxy)) { |
| 24 | - return proxy; //不是代理对象 | |
| 24 | + return proxy; | |
| 25 | 25 | } |
| 26 | + // Jdk 代理 | |
| 26 | 27 | if (AopUtils.isJdkDynamicProxy(proxy)) { |
| 27 | 28 | return getJdkDynamicProxyTargetObject(proxy); |
| 28 | - } else { //cglib | |
| 29 | - return getCglibProxyTargetObject(proxy); | |
| 30 | 29 | } |
| 30 | + // Cglib 代理 | |
| 31 | + return getCglibProxyTargetObject(proxy); | |
| 31 | 32 | } |
| 32 | 33 | |
| 33 | 34 | private static Object getCglibProxyTargetObject(Object proxy) throws Exception { |
| 34 | 35 | Object dynamicAdvisedInterceptor = BeanUtil.getFieldValue(proxy, "CGLIB$CALLBACK_0"); |
| 35 | 36 | AdvisedSupport advisedSupport = (AdvisedSupport) BeanUtil.getFieldValue(dynamicAdvisedInterceptor, "advised"); |
| 36 | - Object target = advisedSupport.getTargetSource().getTarget(); | |
| 37 | - return target; | |
| 37 | + return advisedSupport.getTargetSource().getTarget(); | |
| 38 | 38 | } |
| 39 | 39 | |
| 40 | 40 | private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception { |
| 41 | 41 | AopProxy aopProxy = (AopProxy) BeanUtil.getFieldValue(proxy, "h"); |
| 42 | 42 | AdvisedSupport advisedSupport = (AdvisedSupport) BeanUtil.getFieldValue(aopProxy, "advised"); |
| 43 | - Object target = advisedSupport.getTargetSource().getTarget(); | |
| 44 | - return target; | |
| 43 | + return advisedSupport.getTargetSource().getTarget(); | |
| 45 | 44 | } |
| 46 | 45 | |
| 47 | -} | |
| 48 | 46 | \ No newline at end of file |
| 47 | +} | ... | ... |
src/test/java/cn/iocoder/dashboard/util/AssertUtils.java
| ... | ... | @@ -10,6 +10,7 @@ import org.junit.jupiter.api.function.Executable; |
| 10 | 10 | |
| 11 | 11 | import java.lang.reflect.Field; |
| 12 | 12 | import java.util.Arrays; |
| 13 | +import java.util.Objects; | |
| 13 | 14 | |
| 14 | 15 | import static org.junit.jupiter.api.Assertions.assertThrows; |
| 15 | 16 | |
| ... | ... | @@ -51,6 +52,33 @@ public class AssertUtils { |
| 51 | 52 | } |
| 52 | 53 | |
| 53 | 54 | /** |
| 55 | + * 比对两个对象的属性是否一致 | |
| 56 | + * | |
| 57 | + * 注意,如果 expected 存在的属性,actual 不存在的时候,会进行忽略 | |
| 58 | + * | |
| 59 | + * @param expected 期望对象 | |
| 60 | + * @param actual 实际对象 | |
| 61 | + * @param ignoreFields 忽略的属性数组 | |
| 62 | + * @return 是否一致 | |
| 63 | + */ | |
| 64 | + public static boolean isPojoEquals(Object expected, Object actual, String... ignoreFields) { | |
| 65 | + Field[] expectedFields = ReflectUtil.getFields(expected.getClass()); | |
| 66 | + return Arrays.stream(expectedFields).allMatch(expectedField -> { | |
| 67 | + // 如果是忽略的属性,则不进行比对 | |
| 68 | + if (ArrayUtil.contains(ignoreFields, expectedField.getName())) { | |
| 69 | + return true; | |
| 70 | + } | |
| 71 | + // 忽略不存在的属性 | |
| 72 | + Field actualField = ReflectUtil.getField(actual.getClass(), expectedField.getName()); | |
| 73 | + if (actualField == null) { | |
| 74 | + return true; | |
| 75 | + } | |
| 76 | + return Objects.equals(ReflectUtil.getFieldValue(expected, expectedField), | |
| 77 | + ReflectUtil.getFieldValue(actual, actualField)); | |
| 78 | + }); | |
| 79 | + } | |
| 80 | + | |
| 81 | + /** | |
| 54 | 82 | * 执行方法,校验抛出的 Service 是否符合条件 |
| 55 | 83 | * |
| 56 | 84 | * @param executable 业务异常 |
| ... | ... | @@ -62,7 +90,7 @@ public class AssertUtils { |
| 62 | 90 | ServiceException serviceException = assertThrows(ServiceException.class, executable); |
| 63 | 91 | // 校验错误码 |
| 64 | 92 | Assertions.assertEquals(errorCode.getCode(), serviceException.getCode(), "错误码不匹配"); |
| 65 | - String message = ServiceExceptionUtil.doFormat(errorCode.getCode(), errorCode.getMessage(), messageParams); | |
| 93 | + String message = ServiceExceptionUtil.doFormat(errorCode.getCode(), errorCode.getMsg(), messageParams); | |
| 66 | 94 | Assertions.assertEquals(message, serviceException.getMessage(), "错误提示不匹配"); |
| 67 | 95 | } |
| 68 | 96 | ... | ... |
src/test/java/cn/iocoder/dashboard/util/RandomUtils.java
| ... | ... | @@ -7,9 +7,8 @@ import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO; |
| 7 | 7 | import uk.co.jemos.podam.api.PodamFactory; |
| 8 | 8 | import uk.co.jemos.podam.api.PodamFactoryImpl; |
| 9 | 9 | |
| 10 | -import java.util.Arrays; | |
| 11 | -import java.util.Date; | |
| 12 | -import java.util.Set; | |
| 10 | +import java.lang.reflect.Type; | |
| 11 | +import java.util.*; | |
| 13 | 12 | import java.util.function.Consumer; |
| 14 | 13 | import java.util.stream.Collectors; |
| 15 | 14 | import java.util.stream.Stream; |
| ... | ... | @@ -87,4 +86,21 @@ public class RandomUtils { |
| 87 | 86 | return pojo; |
| 88 | 87 | } |
| 89 | 88 | |
| 89 | + @SafeVarargs | |
| 90 | + public static <T> T randomPojo(Class<T> clazz, Type type, Consumer<T>... consumers) { | |
| 91 | + T pojo = PODAM_FACTORY.manufacturePojo(clazz, type); | |
| 92 | + // 非空时,回调逻辑。通过它,可以实现 Pojo 的进一步处理 | |
| 93 | + if (ArrayUtil.isNotEmpty(consumers)) { | |
| 94 | + Arrays.stream(consumers).forEach(consumer -> consumer.accept(pojo)); | |
| 95 | + } | |
| 96 | + return pojo; | |
| 97 | + } | |
| 98 | + | |
| 99 | + @SafeVarargs | |
| 100 | + public static <T> List<T> randomPojoList(Class<T> clazz, Consumer<T>... consumers) { | |
| 101 | + int size = RandomUtil.randomInt(0, RANDOM_COLLECTION_LENGTH); | |
| 102 | + return Stream.iterate(0, i -> i).limit(size).map(o -> randomPojo(clazz, consumers)) | |
| 103 | + .collect(Collectors.toList()); | |
| 104 | + } | |
| 105 | + | |
| 90 | 106 | } | ... | ... |
src/test/resources/sql/clean.sql
src/test/resources/sql/create_tables.sql
| ... | ... | @@ -47,8 +47,7 @@ CREATE TABLE IF NOT EXISTS "inf_job" ( |
| 47 | 47 | PRIMARY KEY ("id") |
| 48 | 48 | ) COMMENT='定时任务表'; |
| 49 | 49 | |
| 50 | -DROP TABLE IF EXISTS "inf_job_log"; | |
| 51 | -CREATE TABLE "inf_job_log" ( | |
| 50 | +CREATE TABLE IF NOT EXISTS "inf_job_log" ( | |
| 52 | 51 | "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '日志编号', |
| 53 | 52 | "job_id" bigint(20) NOT NULL COMMENT '任务编号', |
| 54 | 53 | "handler_name" varchar(64) NOT NULL COMMENT '处理器的名字', |
| ... | ... | @@ -192,8 +191,7 @@ CREATE TABLE IF NOT EXISTS `sys_user_session` ( |
| 192 | 191 | PRIMARY KEY (`id`) |
| 193 | 192 | ) COMMENT '用户在线 Session'; |
| 194 | 193 | |
| 195 | -CREATE TABLE IF NOT EXISTS "sys_post" | |
| 196 | -( | |
| 194 | +CREATE TABLE IF NOT EXISTS "sys_post" ( | |
| 197 | 195 | "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, |
| 198 | 196 | "code" varchar(64) NOT NULL, |
| 199 | 197 | "name" varchar(50) NOT NULL, |
| ... | ... | @@ -208,7 +206,6 @@ CREATE TABLE IF NOT EXISTS "sys_post" |
| 208 | 206 | PRIMARY KEY ("id") |
| 209 | 207 | ) COMMENT '岗位信息表'; |
| 210 | 208 | |
| 211 | - | |
| 212 | 209 | CREATE TABLE IF NOT EXISTS "sys_notice" ( |
| 213 | 210 | "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, |
| 214 | 211 | "title" varchar(50) NOT NULL COMMENT '公告标题', |
| ... | ... | @@ -223,7 +220,6 @@ CREATE TABLE IF NOT EXISTS "sys_notice" ( |
| 223 | 220 | PRIMARY KEY("id") |
| 224 | 221 | ) COMMENT '通知公告表'; |
| 225 | 222 | |
| 226 | - | |
| 227 | 223 | CREATE TABLE IF NOT EXISTS `sys_login_log` ( |
| 228 | 224 | `id` bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, |
| 229 | 225 | `log_type` bigint(4) NOT NULL, |
| ... | ... | @@ -240,7 +236,6 @@ CREATE TABLE IF NOT EXISTS `sys_login_log` ( |
| 240 | 236 | PRIMARY KEY (`id`) |
| 241 | 237 | ) COMMENT ='系统访问记录'; |
| 242 | 238 | |
| 243 | - | |
| 244 | 239 | CREATE TABLE IF NOT EXISTS `sys_operate_log` ( |
| 245 | 240 | `id` bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, |
| 246 | 241 | `trace_id` varchar(64) NOT NULL DEFAULT '', |
| ... | ... | @@ -346,3 +341,73 @@ CREATE TABLE IF NOT EXISTS "inf_api_error_log" ( |
| 346 | 341 | "deleted" bit not null default false, |
| 347 | 342 | primary key ("id") |
| 348 | 343 | ) COMMENT '系统异常日志'; |
| 344 | + | |
| 345 | +CREATE TABLE IF NOT EXISTS "sys_sms_channel" ( | |
| 346 | + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | |
| 347 | + "signature" varchar(10) NOT NULL, | |
| 348 | + "code" varchar(63) NOT NULL, | |
| 349 | + "status" tinyint NOT NULL, | |
| 350 | + "remark" varchar(255) DEFAULT NULL, | |
| 351 | + "api_key" varchar(63) NOT NULL, | |
| 352 | + "api_secret" varchar(63) DEFAULT NULL, | |
| 353 | + "callback_url" varchar(255) DEFAULT NULL, | |
| 354 | + "creator" varchar(64) DEFAULT '', | |
| 355 | + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| 356 | + "updater" varchar(64) DEFAULT '', | |
| 357 | + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| 358 | + "deleted" bit NOT NULL DEFAULT FALSE, | |
| 359 | + PRIMARY KEY ("id") | |
| 360 | +) COMMENT '短信渠道'; | |
| 361 | + | |
| 362 | +CREATE TABLE "sys_sms_template" ( | |
| 363 | + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | |
| 364 | + "type" tinyint NOT NULL, | |
| 365 | + "status" tinyint NOT NULL, | |
| 366 | + "code" varchar(63) NOT NULL, | |
| 367 | + "name" varchar(63) NOT NULL, | |
| 368 | + "content" varchar(255) NOT NULL, | |
| 369 | + "params" varchar(255) NOT NULL, | |
| 370 | + "remark" varchar(255) DEFAULT NULL, | |
| 371 | + "api_template_id" varchar(63) NOT NULL, | |
| 372 | + "channel_id" bigint NOT NULL, | |
| 373 | + "channel_code" varchar(63) NOT NULL, | |
| 374 | + "creator" varchar(64) DEFAULT '', | |
| 375 | + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| 376 | + "updater" varchar(64) DEFAULT '', | |
| 377 | + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| 378 | + "deleted" bit NOT NULL DEFAULT FALSE, | |
| 379 | + PRIMARY KEY ("id") | |
| 380 | +) COMMENT '短信模板'; | |
| 381 | + | |
| 382 | +CREATE TABLE "sys_sms_log" ( | |
| 383 | + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | |
| 384 | + "channel_id" bigint NOT NULL, | |
| 385 | + "channel_code" varchar(63) NOT NULL, | |
| 386 | + "template_id" bigint NOT NULL, | |
| 387 | + "template_code" varchar(63) NOT NULL, | |
| 388 | + "template_type" tinyint NOT NULL, | |
| 389 | + "template_content" varchar(255) NOT NULL, | |
| 390 | + "template_params" varchar(255) NOT NULL, | |
| 391 | + "api_template_id" varchar(63) NOT NULL, | |
| 392 | + "mobile" varchar(11) NOT NULL, | |
| 393 | + "user_id" bigint DEFAULT '0', | |
| 394 | + "user_type" tinyint DEFAULT '0', | |
| 395 | + "send_status" tinyint NOT NULL DEFAULT '0', | |
| 396 | + "send_time" timestamp DEFAULT NULL, | |
| 397 | + "send_code" int DEFAULT NULL, | |
| 398 | + "send_msg" varchar(255) DEFAULT NULL, | |
| 399 | + "api_send_code" varchar(63) DEFAULT NULL, | |
| 400 | + "api_send_msg" varchar(255) DEFAULT NULL, | |
| 401 | + "api_request_id" varchar(255) DEFAULT NULL, | |
| 402 | + "api_serial_no" varchar(255) DEFAULT NULL, | |
| 403 | + "receive_status" tinyint NOT NULL DEFAULT '0', | |
| 404 | + "receive_time" timestamp DEFAULT NULL, | |
| 405 | + "api_receive_code" varchar(63) DEFAULT NULL, | |
| 406 | + "api_receive_msg" varchar(255) DEFAULT NULL, | |
| 407 | + "creator" varchar(64) DEFAULT '', | |
| 408 | + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| 409 | + "updater" varchar(64) DEFAULT '', | |
| 410 | + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| 411 | + "deleted" bit NOT NULL DEFAULT FALSE, | |
| 412 | + PRIMARY KEY ("id") | |
| 413 | +) COMMENT '短信日志'; | ... | ... |