Commit e62495f65076dde537b559b6b39a1fc9eaa0eedd
Merge branch 'master' of https://gitee.com/zhijiantianya/ruoyi-vue-pro
Showing
141 changed files
with
8690 additions
and
185 deletions
pom.xml
| @@ -46,6 +46,7 @@ | @@ -46,6 +46,7 @@ | ||
| 46 | <easyexcel.verion>2.2.7</easyexcel.verion> | 46 | <easyexcel.verion>2.2.7</easyexcel.verion> |
| 47 | <velocity.version>2.2</velocity.version> | 47 | <velocity.version>2.2</velocity.version> |
| 48 | <screw.version>1.0.5</screw.version> | 48 | <screw.version>1.0.5</screw.version> |
| 49 | + <!-- 三方云服务相关 --> | ||
| 49 | </properties> | 50 | </properties> |
| 50 | 51 | ||
| 51 | <!-- 依赖声明 --> | 52 | <!-- 依赖声明 --> |
| @@ -271,6 +272,27 @@ | @@ -271,6 +272,27 @@ | ||
| 271 | <version>${screw.version}</version> | 272 | <version>${screw.version}</version> |
| 272 | </dependency> | 273 | </dependency> |
| 273 | 274 | ||
| 275 | + <!-- 三方云服务相关 --> | ||
| 276 | + | ||
| 277 | + <!-- SMS SDK begin --> | ||
| 278 | + <dependency> | ||
| 279 | + <groupId>com.yunpian.sdk</groupId> | ||
| 280 | + <artifactId>yunpian-java-sdk</artifactId> | ||
| 281 | + <version>1.2.7</version> | ||
| 282 | + </dependency> | ||
| 283 | + | ||
| 284 | + <dependency> | ||
| 285 | + <groupId>com.aliyun</groupId> | ||
| 286 | + <artifactId>aliyun-java-sdk-core</artifactId> | ||
| 287 | + <version>4.5.18</version> | ||
| 288 | + </dependency> | ||
| 289 | + <dependency> | ||
| 290 | + <groupId>com.aliyun</groupId> | ||
| 291 | + <artifactId>aliyun-java-sdk-dysmsapi</artifactId> | ||
| 292 | + <version>2.1.0</version> | ||
| 293 | + </dependency> | ||
| 294 | + <!-- SMS SDK end --> | ||
| 295 | + | ||
| 274 | </dependencies> | 296 | </dependencies> |
| 275 | 297 | ||
| 276 | 298 |
ruoyi-ui/.env.demo1024
0 → 100644
ruoyi-ui/package.json
| @@ -8,6 +8,7 @@ | @@ -8,6 +8,7 @@ | ||
| 8 | "dev": "vue-cli-service serve", | 8 | "dev": "vue-cli-service serve", |
| 9 | "build:prod": "vue-cli-service build", | 9 | "build:prod": "vue-cli-service build", |
| 10 | "build:stage": "vue-cli-service build --mode staging", | 10 | "build:stage": "vue-cli-service build --mode staging", |
| 11 | + "build:demo1024": "vue-cli-service build --mode demo1024", | ||
| 11 | "preview": "node build/index.js --preview", | 12 | "preview": "node build/index.js --preview", |
| 12 | "lint": "eslint --ext .js,.vue src" | 13 | "lint": "eslint --ext .js,.vue src" |
| 13 | }, | 14 | }, |
ruoyi-ui/src/api/system/sms/smsChannel.js
0 → 100644
| 1 | +import request from '@/utils/request' | ||
| 2 | + | ||
| 3 | +// 创建短信渠道 | ||
| 4 | +export function createSmsChannel(data) { | ||
| 5 | + return request({ | ||
| 6 | + url: '/system/sms-channel/create', | ||
| 7 | + method: 'post', | ||
| 8 | + data: data | ||
| 9 | + }) | ||
| 10 | +} | ||
| 11 | + | ||
| 12 | +// 更新短信渠道 | ||
| 13 | +export function updateSmsChannel(data) { | ||
| 14 | + return request({ | ||
| 15 | + url: '/system/sms-channel/update', | ||
| 16 | + method: 'put', | ||
| 17 | + data: data | ||
| 18 | + }) | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | +// 删除短信渠道 | ||
| 22 | +export function deleteSmsChannel(id) { | ||
| 23 | + return request({ | ||
| 24 | + url: '/system/sms-channel/delete?id=' + id, | ||
| 25 | + method: 'delete' | ||
| 26 | + }) | ||
| 27 | +} | ||
| 28 | + | ||
| 29 | +// 获得短信渠道 | ||
| 30 | +export function getSmsChannel(id) { | ||
| 31 | + return request({ | ||
| 32 | + url: '/system/sms-channel/get?id=' + id, | ||
| 33 | + method: 'get' | ||
| 34 | + }) | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +// 获得短信渠道分页 | ||
| 38 | +export function getSmsChannelPage(query) { | ||
| 39 | + return request({ | ||
| 40 | + url: '/system/sms-channel/page', | ||
| 41 | + method: 'get', | ||
| 42 | + params: query | ||
| 43 | + }) | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +// 获得短信渠道精简列表 | ||
| 47 | +export function getSimpleSmsChannels() { | ||
| 48 | + return request({ | ||
| 49 | + url: '/system/sms-channel/list-all-simple', | ||
| 50 | + method: 'get', | ||
| 51 | + }) | ||
| 52 | +} |
ruoyi-ui/src/api/system/sms/smsLog.js
0 → 100644
| 1 | +import request from '@/utils/request' | ||
| 2 | + | ||
| 3 | +// 获得短信日志分页 | ||
| 4 | +export function getSmsLogPage(query) { | ||
| 5 | + return request({ | ||
| 6 | + url: '/system/sms-log/page', | ||
| 7 | + method: 'get', | ||
| 8 | + params: query | ||
| 9 | + }) | ||
| 10 | +} | ||
| 11 | + | ||
| 12 | +// 导出短信日志 Excel | ||
| 13 | +export function exportSmsLogExcel(query) { | ||
| 14 | + return request({ | ||
| 15 | + url: '/system/sms-log/export-excel', | ||
| 16 | + method: 'get', | ||
| 17 | + params: query, | ||
| 18 | + responseType: 'blob' | ||
| 19 | + }) | ||
| 20 | +} |
ruoyi-ui/src/api/system/sms/smsTemplate.js
0 → 100644
| 1 | +import request from '@/utils/request' | ||
| 2 | + | ||
| 3 | +// 创建短信模板 | ||
| 4 | +export function createSmsTemplate(data) { | ||
| 5 | + return request({ | ||
| 6 | + url: '/system/sms-template/create', | ||
| 7 | + method: 'post', | ||
| 8 | + data: data | ||
| 9 | + }) | ||
| 10 | +} | ||
| 11 | + | ||
| 12 | +// 更新短信模板 | ||
| 13 | +export function updateSmsTemplate(data) { | ||
| 14 | + return request({ | ||
| 15 | + url: '/system/sms-template/update', | ||
| 16 | + method: 'put', | ||
| 17 | + data: data | ||
| 18 | + }) | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | +// 删除短信模板 | ||
| 22 | +export function deleteSmsTemplate(id) { | ||
| 23 | + return request({ | ||
| 24 | + url: '/system/sms-template/delete?id=' + id, | ||
| 25 | + method: 'delete' | ||
| 26 | + }) | ||
| 27 | +} | ||
| 28 | + | ||
| 29 | +// 获得短信模板 | ||
| 30 | +export function getSmsTemplate(id) { | ||
| 31 | + return request({ | ||
| 32 | + url: '/system/sms-template/get?id=' + id, | ||
| 33 | + method: 'get' | ||
| 34 | + }) | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +// 获得短信模板分页 | ||
| 38 | +export function getSmsTemplatePage(query) { | ||
| 39 | + return request({ | ||
| 40 | + url: '/system/sms-template/page', | ||
| 41 | + method: 'get', | ||
| 42 | + params: query | ||
| 43 | + }) | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +// 创建短信模板 | ||
| 47 | +export function sendSms(data) { | ||
| 48 | + return request({ | ||
| 49 | + url: '/system/sms-template/send-sms', | ||
| 50 | + method: 'post', | ||
| 51 | + data: data | ||
| 52 | + }) | ||
| 53 | +} | ||
| 54 | + | ||
| 55 | +// 导出短信模板 Excel | ||
| 56 | +export function exportSmsTemplateExcel(query) { | ||
| 57 | + return request({ | ||
| 58 | + url: '/system/sms-template/export-excel', | ||
| 59 | + method: 'get', | ||
| 60 | + params: query, | ||
| 61 | + responseType: 'blob' | ||
| 62 | + }) | ||
| 63 | +} | ||
| 64 | + |
ruoyi-ui/src/utils/dict.js
| @@ -17,6 +17,10 @@ export const DICT_TYPE = { | @@ -17,6 +17,10 @@ export const DICT_TYPE = { | ||
| 17 | SYS_OPERATE_TYPE: 'sys_operate_type', | 17 | SYS_OPERATE_TYPE: 'sys_operate_type', |
| 18 | SYS_LOGIN_RESULT: 'sys_login_result', | 18 | SYS_LOGIN_RESULT: 'sys_login_result', |
| 19 | SYS_CONFIG_TYPE: 'sys_config_type', | 19 | SYS_CONFIG_TYPE: 'sys_config_type', |
| 20 | + SYS_SMS_CHANNEL_CODE: 'sys_sms_channel_code', | ||
| 21 | + SYS_SMS_TEMPLATE_TYPE: 'sys_sms_template_type', | ||
| 22 | + SYS_SMS_SEND_STATUS: 'sys_sms_send_status', | ||
| 23 | + SYS_SMS_RECEIVE_STATUS: 'sys_sms_receive_status', | ||
| 20 | 24 | ||
| 21 | INF_REDIS_TIMEOUT_TYPE: 'inf_redis_timeout_type', | 25 | INF_REDIS_TIMEOUT_TYPE: 'inf_redis_timeout_type', |
| 22 | INF_JOB_STATUS: 'inf_job_status', | 26 | INF_JOB_STATUS: 'inf_job_status', |
ruoyi-ui/src/views/system/operatelog/index.vue
ruoyi-ui/src/views/system/sms/smsChannel.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="app-container"> | ||
| 3 | + | ||
| 4 | + <!-- 搜索工作栏 --> | ||
| 5 | + <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px"> | ||
| 6 | + <el-form-item label="短信签名" prop="signature"> | ||
| 7 | + <el-input v-model="queryParams.signature" placeholder="请输入短信签名" clearable size="small" @keyup.enter.native="handleQuery"/> | ||
| 8 | + </el-form-item> | ||
| 9 | + <el-form-item label="启用状态" prop="status"> | ||
| 10 | + <el-select v-model="queryParams.status" placeholder="请选择启用状态" clearable size="small"> | ||
| 11 | + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_COMMON_STATUS)" | ||
| 12 | + :key="dict.value" :label="dict.label" :value="dict.value"/> | ||
| 13 | + </el-select> | ||
| 14 | + </el-form-item> | ||
| 15 | + <el-form-item label="创建时间"> | ||
| 16 | + <el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd" | ||
| 17 | + type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" /> | ||
| 18 | + </el-form-item> | ||
| 19 | + <el-form-item> | ||
| 20 | + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> | ||
| 21 | + <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> | ||
| 22 | + </el-form-item> | ||
| 23 | + </el-form> | ||
| 24 | + | ||
| 25 | + <!-- 操作工具栏 --> | ||
| 26 | + <el-row :gutter="10" class="mb8"> | ||
| 27 | + <el-col :span="1.5"> | ||
| 28 | + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" | ||
| 29 | + v-hasPermi="['system:sms-channel:create']">新增</el-button> | ||
| 30 | + </el-col> | ||
| 31 | + <el-col :span="1.5"> | ||
| 32 | + <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" | ||
| 33 | + v-hasPermi="['system:sms-channel:export']">导出</el-button> | ||
| 34 | + </el-col> | ||
| 35 | + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> | ||
| 36 | + </el-row> | ||
| 37 | + | ||
| 38 | + <!-- 列表 --> | ||
| 39 | + <el-table v-loading="loading" :data="list"> | ||
| 40 | + <el-table-column label="编号" align="center" prop="id" /> | ||
| 41 | + <el-table-column label="短信签名" align="center" prop="signature" /> | ||
| 42 | + <el-table-column label="渠道编码" align="center" prop="code"> | ||
| 43 | + <template slot-scope="scope"> | ||
| 44 | + <span>{{ getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, scope.row.code) }}</span> | ||
| 45 | + </template> | ||
| 46 | + </el-table-column>> | ||
| 47 | + <el-table-column label="启用状态" align="center" prop="status"> | ||
| 48 | + <template slot-scope="scope"> | ||
| 49 | + <span>{{ getDictDataLabel(DICT_TYPE.SYS_COMMON_STATUS, scope.row.status) }}</span> | ||
| 50 | + </template> | ||
| 51 | + </el-table-column>> | ||
| 52 | + <el-table-column label="备注" align="center" prop="remark" /> | ||
| 53 | + <el-table-column label="短信 API 的账号" align="center" prop="apiKey" /> | ||
| 54 | + <el-table-column label="短信 API 的秘钥" align="center" prop="apiSecret" /> | ||
| 55 | + <el-table-column label="短信发送回调 URL" align="center" prop="callbackUrl" /> | ||
| 56 | + <el-table-column label="创建时间" align="center" prop="createTime" width="180"> | ||
| 57 | + <template slot-scope="scope"> | ||
| 58 | + <span>{{ parseTime(scope.row.createTime) }}</span> | ||
| 59 | + </template> | ||
| 60 | + </el-table-column> | ||
| 61 | + <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> | ||
| 62 | + <template slot-scope="scope"> | ||
| 63 | + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" | ||
| 64 | + v-hasPermi="['system:sms-channel:update']">修改</el-button> | ||
| 65 | + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" | ||
| 66 | + v-hasPermi="['system:sms-channel:delete']">删除</el-button> | ||
| 67 | + </template> | ||
| 68 | + </el-table-column> | ||
| 69 | + </el-table> | ||
| 70 | + <!-- 分页组件 --> | ||
| 71 | + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" | ||
| 72 | + @pagination="getList"/> | ||
| 73 | + | ||
| 74 | + <!-- 对话框(添加 / 修改) --> | ||
| 75 | + <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> | ||
| 76 | + <el-form ref="form" :model="form" :rules="rules" label-width="80px"> | ||
| 77 | + <el-form-item label="短信签名" prop="signature"> | ||
| 78 | + <el-input v-model="form.signature" placeholder="请输入短信签名" /> | ||
| 79 | + </el-form-item> | ||
| 80 | + <el-form-item label="渠道编码" prop="code"> | ||
| 81 | + <el-input v-model="form.code" placeholder="请输入渠道编码" /> | ||
| 82 | + </el-form-item> | ||
| 83 | + <el-form-item label="启用状态"> | ||
| 84 | + <el-radio-group v-model="form.status"> | ||
| 85 | + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.SYS_COMMON_STATUS)" | ||
| 86 | + :key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio> | ||
| 87 | + </el-radio-group> | ||
| 88 | + </el-form-item> | ||
| 89 | + <el-form-item label="备注" prop="remark"> | ||
| 90 | + <el-input v-model="form.remark" placeholder="请输入备注" /> | ||
| 91 | + </el-form-item> | ||
| 92 | + <el-form-item label="短信 API 的账号" prop="apiKey"> | ||
| 93 | + <el-input v-model="form.apiKey" placeholder="请输入短信 API 的账号" /> | ||
| 94 | + </el-form-item> | ||
| 95 | + <el-form-item label="短信 API 的秘钥" prop="apiSecret"> | ||
| 96 | + <el-input v-model="form.apiSecret" placeholder="请输入短信 API 的秘钥" /> | ||
| 97 | + </el-form-item> | ||
| 98 | + <el-form-item label="短信发送回调 URL" prop="callbackUrl"> | ||
| 99 | + <el-input v-model="form.callbackUrl" placeholder="请输入短信发送回调 URL" /> | ||
| 100 | + </el-form-item> | ||
| 101 | + </el-form> | ||
| 102 | + <div slot="footer" class="dialog-footer"> | ||
| 103 | + <el-button type="primary" @click="submitForm">确 定</el-button> | ||
| 104 | + <el-button @click="cancel">取 消</el-button> | ||
| 105 | + </div> | ||
| 106 | + </el-dialog> | ||
| 107 | + </div> | ||
| 108 | +</template> | ||
| 109 | + | ||
| 110 | +<script> | ||
| 111 | +import { createSmsChannel, updateSmsChannel, deleteSmsChannel, getSmsChannel, getSmsChannelPage, | ||
| 112 | + getSimpleSmsChannels } from "@/api/system/sms/smsChannel"; | ||
| 113 | + | ||
| 114 | +export default { | ||
| 115 | + name: "SmsChannel", | ||
| 116 | + components: { | ||
| 117 | + }, | ||
| 118 | + data() { | ||
| 119 | + return { | ||
| 120 | + // 遮罩层 | ||
| 121 | + loading: true, | ||
| 122 | + // 显示搜索条件 | ||
| 123 | + showSearch: true, | ||
| 124 | + // 总条数 | ||
| 125 | + total: 0, | ||
| 126 | + // 短信渠道列表 | ||
| 127 | + list: [], | ||
| 128 | + // 弹出层标题 | ||
| 129 | + title: "", | ||
| 130 | + // 是否显示弹出层 | ||
| 131 | + open: false, | ||
| 132 | + dateRangeCreateTime: [], | ||
| 133 | + // 查询参数 | ||
| 134 | + queryParams: { | ||
| 135 | + pageNo: 1, | ||
| 136 | + pageSize: 10, | ||
| 137 | + signature: null, | ||
| 138 | + status: null, | ||
| 139 | + }, | ||
| 140 | + // 表单参数 | ||
| 141 | + form: {}, | ||
| 142 | + // 表单校验 | ||
| 143 | + rules: { | ||
| 144 | + signature: [{ required: true, message: "短信签名不能为空", trigger: "blur" }], | ||
| 145 | + code: [{ required: true, message: "渠道编码不能为空", trigger: "blur" }], | ||
| 146 | + status: [{ required: true, message: "启用状态不能为空", trigger: "blur" }], | ||
| 147 | + apiKey: [{ required: true, message: "短信 API 的账号不能为空", trigger: "blur" }], | ||
| 148 | + }, | ||
| 149 | + }; | ||
| 150 | + }, | ||
| 151 | + created() { | ||
| 152 | + this.getList(); | ||
| 153 | + }, | ||
| 154 | + methods: { | ||
| 155 | + /** 查询列表 */ | ||
| 156 | + getList() { | ||
| 157 | + this.loading = true; | ||
| 158 | + // 处理查询参数 | ||
| 159 | + let params = {...this.queryParams}; | ||
| 160 | + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime'); | ||
| 161 | + // 执行查询 | ||
| 162 | + getSmsChannelPage(params).then(response => { | ||
| 163 | + this.list = response.data.list; | ||
| 164 | + this.total = response.data.total; | ||
| 165 | + this.loading = false; | ||
| 166 | + }); | ||
| 167 | + }, | ||
| 168 | + /** 取消按钮 */ | ||
| 169 | + cancel() { | ||
| 170 | + this.open = false; | ||
| 171 | + this.reset(); | ||
| 172 | + }, | ||
| 173 | + /** 表单重置 */ | ||
| 174 | + reset() { | ||
| 175 | + this.form = { | ||
| 176 | + id: undefined, | ||
| 177 | + signature: undefined, | ||
| 178 | + code: undefined, | ||
| 179 | + status: undefined, | ||
| 180 | + remark: undefined, | ||
| 181 | + apiKey: undefined, | ||
| 182 | + apiSecret: undefined, | ||
| 183 | + callbackUrl: undefined, | ||
| 184 | + }; | ||
| 185 | + this.resetForm("form"); | ||
| 186 | + }, | ||
| 187 | + /** 搜索按钮操作 */ | ||
| 188 | + handleQuery() { | ||
| 189 | + this.queryParams.pageNo = 1; | ||
| 190 | + this.getList(); | ||
| 191 | + }, | ||
| 192 | + /** 重置按钮操作 */ | ||
| 193 | + resetQuery() { | ||
| 194 | + this.dateRangeCreateTime = []; | ||
| 195 | + this.resetForm("queryForm"); | ||
| 196 | + this.handleQuery(); | ||
| 197 | + }, | ||
| 198 | + /** 新增按钮操作 */ | ||
| 199 | + handleAdd() { | ||
| 200 | + this.reset(); | ||
| 201 | + this.open = true; | ||
| 202 | + this.title = "添加短信渠道"; | ||
| 203 | + }, | ||
| 204 | + /** 修改按钮操作 */ | ||
| 205 | + handleUpdate(row) { | ||
| 206 | + this.reset(); | ||
| 207 | + const id = row.id; | ||
| 208 | + getSmsChannel(id).then(response => { | ||
| 209 | + this.form = response.data; | ||
| 210 | + this.open = true; | ||
| 211 | + this.title = "修改短信渠道"; | ||
| 212 | + }); | ||
| 213 | + }, | ||
| 214 | + /** 提交按钮 */ | ||
| 215 | + submitForm() { | ||
| 216 | + this.$refs["form"].validate(valid => { | ||
| 217 | + if (!valid) { | ||
| 218 | + return; | ||
| 219 | + } | ||
| 220 | + // 修改的提交 | ||
| 221 | + if (this.form.id != null) { | ||
| 222 | + updateSmsChannel(this.form).then(response => { | ||
| 223 | + this.msgSuccess("修改成功"); | ||
| 224 | + this.open = false; | ||
| 225 | + this.getList(); | ||
| 226 | + }); | ||
| 227 | + return; | ||
| 228 | + } | ||
| 229 | + // 添加的提交 | ||
| 230 | + createSmsChannel(this.form).then(response => { | ||
| 231 | + this.msgSuccess("新增成功"); | ||
| 232 | + this.open = false; | ||
| 233 | + this.getList(); | ||
| 234 | + }); | ||
| 235 | + }); | ||
| 236 | + }, | ||
| 237 | + /** 删除按钮操作 */ | ||
| 238 | + handleDelete(row) { | ||
| 239 | + const id = row.id; | ||
| 240 | + this.$confirm('是否确认删除短信渠道编号为"' + id + '"的数据项?', "警告", { | ||
| 241 | + confirmButtonText: "确定", | ||
| 242 | + cancelButtonText: "取消", | ||
| 243 | + type: "warning" | ||
| 244 | + }).then(function() { | ||
| 245 | + return deleteSmsChannel(id); | ||
| 246 | + }).then(() => { | ||
| 247 | + this.getList(); | ||
| 248 | + this.msgSuccess("删除成功"); | ||
| 249 | + }) | ||
| 250 | + }, | ||
| 251 | + /** 导出按钮操作 */ | ||
| 252 | + handleExport() { | ||
| 253 | + // 处理查询参数 | ||
| 254 | + let params = {...this.queryParams}; | ||
| 255 | + params.pageNo = undefined; | ||
| 256 | + params.pageSize = undefined; | ||
| 257 | + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime'); | ||
| 258 | + // 执行导出 | ||
| 259 | + this.$confirm('是否确认导出所有短信渠道数据项?', "警告", { | ||
| 260 | + confirmButtonText: "确定", | ||
| 261 | + cancelButtonText: "取消", | ||
| 262 | + type: "warning" | ||
| 263 | + }).then(function() { | ||
| 264 | + return exportSmsChannelExcel(params); | ||
| 265 | + }).then(response => { | ||
| 266 | + this.downloadExcel(response, '短信渠道.xls'); | ||
| 267 | + }) | ||
| 268 | + } | ||
| 269 | + } | ||
| 270 | +}; | ||
| 271 | +</script><template> | ||
| 272 | + <div class="app-container"> | ||
| 273 | + | ||
| 274 | + <!-- 搜索工作栏 --> | ||
| 275 | + <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px"> | ||
| 276 | + <el-form-item label="短信签名" prop="signature"> | ||
| 277 | + <el-input v-model="queryParams.signature" placeholder="请输入短信签名" clearable size="small" @keyup.enter.native="handleQuery"/> | ||
| 278 | + </el-form-item> | ||
| 279 | + <el-form-item label="启用状态" prop="status"> | ||
| 280 | + <el-select v-model="queryParams.status" placeholder="请选择启用状态" clearable size="small"> | ||
| 281 | + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_COMMON_STATUS)" | ||
| 282 | + :key="dict.value" :label="dict.label" :value="dict.value"/> | ||
| 283 | + </el-select> | ||
| 284 | + </el-form-item> | ||
| 285 | + <el-form-item label="创建时间"> | ||
| 286 | + <el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd" | ||
| 287 | + type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" /> | ||
| 288 | + </el-form-item> | ||
| 289 | + <el-form-item> | ||
| 290 | + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> | ||
| 291 | + <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> | ||
| 292 | + </el-form-item> | ||
| 293 | + </el-form> | ||
| 294 | + | ||
| 295 | + <!-- 操作工具栏 --> | ||
| 296 | + <el-row :gutter="10" class="mb8"> | ||
| 297 | + <el-col :span="1.5"> | ||
| 298 | + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" | ||
| 299 | + v-hasPermi="['system:sms-channel:create']">新增</el-button> | ||
| 300 | + </el-col> | ||
| 301 | + <el-col :span="1.5"> | ||
| 302 | + <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" | ||
| 303 | + v-hasPermi="['system:sms-channel:export']">导出</el-button> | ||
| 304 | + </el-col> | ||
| 305 | + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> | ||
| 306 | + </el-row> | ||
| 307 | + | ||
| 308 | + <!-- 列表 --> | ||
| 309 | + <el-table v-loading="loading" :data="list"> | ||
| 310 | + <el-table-column label="编号" align="center" prop="id" /> | ||
| 311 | + <el-table-column label="短信签名" align="center" prop="signature" /> | ||
| 312 | + <el-table-column label="渠道编码" align="center" prop="code"> | ||
| 313 | + <template slot-scope="scope"> | ||
| 314 | + <span>{{ getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, scope.row.code) }}</span> | ||
| 315 | + </template> | ||
| 316 | + </el-table-column>> | ||
| 317 | + <el-table-column label="启用状态" align="center" prop="status"> | ||
| 318 | + <template slot-scope="scope"> | ||
| 319 | + <span>{{ getDictDataLabel(DICT_TYPE.SYS_COMMON_STATUS, scope.row.status) }}</span> | ||
| 320 | + </template> | ||
| 321 | + </el-table-column>> | ||
| 322 | + <el-table-column label="备注" align="center" prop="remark" /> | ||
| 323 | + <el-table-column label="创建时间" align="center" prop="createTime" width="180"> | ||
| 324 | + <template slot-scope="scope"> | ||
| 325 | + <span>{{ parseTime(scope.row.createTime) }}</span> | ||
| 326 | + </template> | ||
| 327 | + </el-table-column> | ||
| 328 | + <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> | ||
| 329 | + <template slot-scope="scope"> | ||
| 330 | + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" | ||
| 331 | + v-hasPermi="['system:sms-channel:update']">修改</el-button> | ||
| 332 | + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" | ||
| 333 | + v-hasPermi="['system:sms-channel:delete']">删除</el-button> | ||
| 334 | + </template> | ||
| 335 | + </el-table-column> | ||
| 336 | + </el-table> | ||
| 337 | + <!-- 分页组件 --> | ||
| 338 | + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" | ||
| 339 | + @pagination="getList"/> | ||
| 340 | + | ||
| 341 | + <!-- 对话框(添加 / 修改) --> | ||
| 342 | + <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> | ||
| 343 | + <el-form ref="form" :model="form" :rules="rules" label-width="130px"> | ||
| 344 | + <el-form-item label="短信签名" prop="signature"> | ||
| 345 | + <el-input v-model="form.signature" placeholder="请输入短信签名" /> | ||
| 346 | + </el-form-item> | ||
| 347 | + <el-form-item label="渠道编码" prop="code"> | ||
| 348 | + <el-select v-model="form.code" placeholder="请选择渠道编码" :disabled="form.id > 0"> | ||
| 349 | + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_SMS_CHANNEL_CODE)" | ||
| 350 | + :key="dict.value" :label="dict.label" :value="dict.value" /> | ||
| 351 | + </el-select> | ||
| 352 | + </el-form-item> | ||
| 353 | + <el-form-item label="启用状态"> | ||
| 354 | + <el-radio-group v-model="form.status"> | ||
| 355 | + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.SYS_COMMON_STATUS)" | ||
| 356 | + :key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio> | ||
| 357 | + </el-radio-group> | ||
| 358 | + </el-form-item> | ||
| 359 | + <el-form-item label="备注" prop="remark"> | ||
| 360 | + <el-input v-model="form.remark" placeholder="请输入备注" /> | ||
| 361 | + </el-form-item> | ||
| 362 | + <el-form-item label="短信 API 的账号" prop="apiKey"> | ||
| 363 | + <el-input v-model="form.apiKey" placeholder="请输入短信 API 的账号" /> | ||
| 364 | + </el-form-item> | ||
| 365 | + <el-form-item v-if="form.code !== 'YUN_PIAN'" label="短信 API 的秘钥" prop="apiSecret"> | ||
| 366 | + <el-input v-model="form.apiSecret" placeholder="请输入短信 API 的秘钥" /> | ||
| 367 | + </el-form-item> | ||
| 368 | + <el-form-item label="短信发送回调 URL" prop="callbackUrl"> | ||
| 369 | + <el-input v-model="form.callbackUrl" placeholder="请输入短信发送回调 URL" /> | ||
| 370 | + </el-form-item> | ||
| 371 | + </el-form> | ||
| 372 | + <div slot="footer" class="dialog-footer"> | ||
| 373 | + <el-button type="primary" @click="submitForm">确 定</el-button> | ||
| 374 | + <el-button @click="cancel">取 消</el-button> | ||
| 375 | + </div> | ||
| 376 | + </el-dialog> | ||
| 377 | + </div> | ||
| 378 | +</template> | ||
| 379 | + | ||
| 380 | +<script> | ||
| 381 | +import { createSmsChannel, updateSmsChannel, deleteSmsChannel, getSmsChannel, getSmsChannelPage } from "@/api/system/sms/smsChannel"; | ||
| 382 | + | ||
| 383 | +export default { | ||
| 384 | + name: "SmsChannel", | ||
| 385 | + components: { | ||
| 386 | + }, | ||
| 387 | + data() { | ||
| 388 | + return { | ||
| 389 | + // 遮罩层 | ||
| 390 | + loading: true, | ||
| 391 | + // 显示搜索条件 | ||
| 392 | + showSearch: true, | ||
| 393 | + // 总条数 | ||
| 394 | + total: 0, | ||
| 395 | + // 短信渠道列表 | ||
| 396 | + list: [], | ||
| 397 | + // 弹出层标题 | ||
| 398 | + title: "", | ||
| 399 | + // 是否显示弹出层 | ||
| 400 | + open: false, | ||
| 401 | + dateRangeCreateTime: [], | ||
| 402 | + // 查询参数 | ||
| 403 | + queryParams: { | ||
| 404 | + pageNo: 1, | ||
| 405 | + pageSize: 10, | ||
| 406 | + signature: null, | ||
| 407 | + status: null, | ||
| 408 | + }, | ||
| 409 | + // 表单参数 | ||
| 410 | + form: {}, | ||
| 411 | + // 表单校验 | ||
| 412 | + apiKeyEnableChannelCodes: ['YUN_PIAN'], | ||
| 413 | + rules: { | ||
| 414 | + signature: [{ required: true, message: "短信签名不能为空", trigger: "blur" }], | ||
| 415 | + code: [{ required: true, message: "渠道编码不能为空", trigger: "blur" }], | ||
| 416 | + status: [{ required: true, message: "启用状态不能为空", trigger: "blur" }], | ||
| 417 | + apiKey: [{ required: true, message: "短信 API 的账号不能为空", trigger: "blur" }], | ||
| 418 | + apiSecret: [{ required: true, message: "短信 API 的秘钥不能为空", trigger: "blur" }], | ||
| 419 | + } | ||
| 420 | + }; | ||
| 421 | + }, | ||
| 422 | + created() { | ||
| 423 | + this.getList(); | ||
| 424 | + }, | ||
| 425 | + methods: { | ||
| 426 | + /** 查询列表 */ | ||
| 427 | + getList() { | ||
| 428 | + this.loading = true; | ||
| 429 | + // 处理查询参数 | ||
| 430 | + let params = {...this.queryParams}; | ||
| 431 | + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime'); | ||
| 432 | + // 执行查询 | ||
| 433 | + getSmsChannelPage(params).then(response => { | ||
| 434 | + this.list = response.data.list; | ||
| 435 | + this.total = response.data.total; | ||
| 436 | + this.loading = false; | ||
| 437 | + }); | ||
| 438 | + }, | ||
| 439 | + /** 取消按钮 */ | ||
| 440 | + cancel() { | ||
| 441 | + this.open = false; | ||
| 442 | + this.reset(); | ||
| 443 | + }, | ||
| 444 | + /** 表单重置 */ | ||
| 445 | + reset() { | ||
| 446 | + this.form = { | ||
| 447 | + id: undefined, | ||
| 448 | + signature: undefined, | ||
| 449 | + code: undefined, | ||
| 450 | + status: undefined, | ||
| 451 | + remark: undefined, | ||
| 452 | + apiKey: undefined, | ||
| 453 | + apiSecret: undefined, | ||
| 454 | + callbackUrl: undefined, | ||
| 455 | + }; | ||
| 456 | + this.resetForm("form"); | ||
| 457 | + }, | ||
| 458 | + /** 搜索按钮操作 */ | ||
| 459 | + handleQuery() { | ||
| 460 | + this.queryParams.pageNo = 1; | ||
| 461 | + this.getList(); | ||
| 462 | + }, | ||
| 463 | + /** 重置按钮操作 */ | ||
| 464 | + resetQuery() { | ||
| 465 | + this.dateRangeCreateTime = []; | ||
| 466 | + this.resetForm("queryForm"); | ||
| 467 | + this.handleQuery(); | ||
| 468 | + }, | ||
| 469 | + /** 新增按钮操作 */ | ||
| 470 | + handleAdd() { | ||
| 471 | + this.reset(); | ||
| 472 | + this.open = true; | ||
| 473 | + this.title = "添加短信渠道"; | ||
| 474 | + }, | ||
| 475 | + /** 修改按钮操作 */ | ||
| 476 | + handleUpdate(row) { | ||
| 477 | + this.reset(); | ||
| 478 | + const id = row.id; | ||
| 479 | + getSmsChannel(id).then(response => { | ||
| 480 | + this.form = response.data; | ||
| 481 | + this.open = true; | ||
| 482 | + this.title = "修改短信渠道"; | ||
| 483 | + }); | ||
| 484 | + }, | ||
| 485 | + /** 提交按钮 */ | ||
| 486 | + submitForm() { | ||
| 487 | + this.$refs["form"].validate(valid => { | ||
| 488 | + if (!valid) { | ||
| 489 | + return; | ||
| 490 | + } | ||
| 491 | + // 修改的提交 | ||
| 492 | + if (this.form.id != null) { | ||
| 493 | + updateSmsChannel(this.form).then(response => { | ||
| 494 | + this.msgSuccess("修改成功"); | ||
| 495 | + this.open = false; | ||
| 496 | + this.getList(); | ||
| 497 | + }); | ||
| 498 | + return; | ||
| 499 | + } | ||
| 500 | + // 添加的提交 | ||
| 501 | + createSmsChannel(this.form).then(response => { | ||
| 502 | + this.msgSuccess("新增成功"); | ||
| 503 | + this.open = false; | ||
| 504 | + this.getList(); | ||
| 505 | + }); | ||
| 506 | + }); | ||
| 507 | + }, | ||
| 508 | + /** 删除按钮操作 */ | ||
| 509 | + handleDelete(row) { | ||
| 510 | + const id = row.id; | ||
| 511 | + this.$confirm('是否确认删除短信渠道编号为"' + id + '"的数据项?', "警告", { | ||
| 512 | + confirmButtonText: "确定", | ||
| 513 | + cancelButtonText: "取消", | ||
| 514 | + type: "warning" | ||
| 515 | + }).then(function() { | ||
| 516 | + return deleteSmsChannel(id); | ||
| 517 | + }).then(() => { | ||
| 518 | + this.getList(); | ||
| 519 | + this.msgSuccess("删除成功"); | ||
| 520 | + }) | ||
| 521 | + }, | ||
| 522 | + /** 导出按钮操作 */ | ||
| 523 | + handleExport() { | ||
| 524 | + // 处理查询参数 | ||
| 525 | + let params = {...this.queryParams}; | ||
| 526 | + params.pageNo = undefined; | ||
| 527 | + params.pageSize = undefined; | ||
| 528 | + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime'); | ||
| 529 | + // 执行导出 | ||
| 530 | + this.$confirm('是否确认导出所有短信渠道数据项?', "警告", { | ||
| 531 | + confirmButtonText: "确定", | ||
| 532 | + cancelButtonText: "取消", | ||
| 533 | + type: "warning" | ||
| 534 | + }).then(function() { | ||
| 535 | + return exportSmsChannelExcel(params); | ||
| 536 | + }).then(response => { | ||
| 537 | + this.downloadExcel(response, '短信渠道.xls'); | ||
| 538 | + }) | ||
| 539 | + } | ||
| 540 | + } | ||
| 541 | +}; | ||
| 542 | +</script> |
ruoyi-ui/src/views/system/sms/smsLog.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="app-container"> | ||
| 3 | + | ||
| 4 | + <!-- 搜索工作栏 --> | ||
| 5 | + <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px"> | ||
| 6 | + <el-form-item label="手机号" prop="mobile"> | ||
| 7 | + <el-input v-model="queryParams.mobile" placeholder="请输入手机号" clearable size="small" @keyup.enter.native="handleQuery"/> | ||
| 8 | + </el-form-item> | ||
| 9 | + <el-form-item label="短信渠道" prop="channelId"> | ||
| 10 | + <el-select v-model="queryParams.channelId" placeholder="请选择短信渠道" clearable size="small"> | ||
| 11 | + <el-option v-for="channel in channelOptions" | ||
| 12 | + :key="channel.id" :value="channel.id" | ||
| 13 | + :label="channel.signature + '【' + getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, channel.code) + '】'" /> | ||
| 14 | + </el-select> | ||
| 15 | + </el-form-item> | ||
| 16 | + <el-form-item label="模板编号" prop="templateId"> | ||
| 17 | + <el-input v-model="queryParams.templateId" placeholder="请输入模板编号" clearable size="small" @keyup.enter.native="handleQuery"/> | ||
| 18 | + </el-form-item> | ||
| 19 | + <el-form-item label="发送状态" prop="sendStatus"> | ||
| 20 | + <el-select v-model="queryParams.sendStatus" placeholder="请选择发送状态" clearable size="small"> | ||
| 21 | + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_SMS_SEND_STATUS)" | ||
| 22 | + :key="dict.value" :label="dict.label" :value="dict.value"/> | ||
| 23 | + </el-select> | ||
| 24 | + </el-form-item> | ||
| 25 | + <el-form-item label="发送时间"> | ||
| 26 | + <el-date-picker v-model="dateRangeSendTime" size="small" style="width: 240px" value-format="yyyy-MM-dd" | ||
| 27 | + type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" /> | ||
| 28 | + </el-form-item> | ||
| 29 | + <el-form-item label="接收状态" prop="receiveStatus"> | ||
| 30 | + <el-select v-model="queryParams.receiveStatus" placeholder="请选择接收状态" clearable size="small"> | ||
| 31 | + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_SMS_RECEIVE_STATUS)" | ||
| 32 | + :key="dict.value" :label="dict.label" :value="dict.value"/> | ||
| 33 | + </el-select> | ||
| 34 | + </el-form-item> | ||
| 35 | + <el-form-item label="接收时间"> | ||
| 36 | + <el-date-picker v-model="dateRangeReceiveTime" size="small" style="width: 240px" value-format="yyyy-MM-dd" | ||
| 37 | + type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" /> | ||
| 38 | + </el-form-item> | ||
| 39 | + <el-form-item> | ||
| 40 | + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> | ||
| 41 | + <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> | ||
| 42 | + </el-form-item> | ||
| 43 | + </el-form> | ||
| 44 | + | ||
| 45 | + <!-- 操作工具栏 --> | ||
| 46 | + <el-row :gutter="10" class="mb8"> | ||
| 47 | + <el-col :span="1.5"> | ||
| 48 | + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" | ||
| 49 | + v-hasPermi="['system:sms-log:create']">新增</el-button> | ||
| 50 | + </el-col> | ||
| 51 | + <el-col :span="1.5"> | ||
| 52 | + <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" | ||
| 53 | + v-hasPermi="['system:sms-log:export']">导出</el-button> | ||
| 54 | + </el-col> | ||
| 55 | + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> | ||
| 56 | + </el-row> | ||
| 57 | + | ||
| 58 | + <!-- 列表 --> | ||
| 59 | + <el-table v-loading="loading" :data="list"> | ||
| 60 | + <el-table-column label="编号" align="center" prop="id" /> | ||
| 61 | + <el-table-column label="创建时间" align="center" prop="createTime" width="180"> | ||
| 62 | + <template slot-scope="scope"> | ||
| 63 | + <span>{{ parseTime(scope.row.createTime) }}</span> | ||
| 64 | + </template> | ||
| 65 | + </el-table-column> | ||
| 66 | + <el-table-column label="手机号" align="center" prop="mobile" width="120"> | ||
| 67 | + <template slot-scope="scope"> | ||
| 68 | + <div>{{ scope.row.mobile }}</div> | ||
| 69 | + <div v-if="scope.row.userType && scope.row.userId"> | ||
| 70 | + {{ getDictDataLabel(DICT_TYPE.USER_TYPE, scope.row.userType) + '(' + scope.row.userId + ')' }} | ||
| 71 | + </div> | ||
| 72 | + </template> | ||
| 73 | + </el-table-column> | ||
| 74 | + <el-table-column label="短信内容" align="center" prop="templateContent" width="300" /> | ||
| 75 | + <el-table-column label="发送状态" align="center" width="180"> | ||
| 76 | + <template slot-scope="scope"> | ||
| 77 | + <div>{{ getDictDataLabel(DICT_TYPE.SYS_SMS_SEND_STATUS, scope.row.sendStatus) }}</div> | ||
| 78 | + <div>{{ parseTime(scope.row.sendTime) }}</div> | ||
| 79 | + </template> | ||
| 80 | + </el-table-column> | ||
| 81 | + <el-table-column label="接收状态" align="center" width="180"> | ||
| 82 | + <template slot-scope="scope"> | ||
| 83 | + <div>{{ getDictDataLabel(DICT_TYPE.SYS_SMS_RECEIVE_STATUS, scope.row.receiveStatus) }}</div> | ||
| 84 | + <div>{{ parseTime(scope.row.receiveTime) }}</div> | ||
| 85 | + </template> | ||
| 86 | + </el-table-column> | ||
| 87 | + <el-table-column label="短信渠道" align="center" width="120"> | ||
| 88 | + <template slot-scope="scope"> | ||
| 89 | + <div>{{ formatChannelSignature(scope.row.channelId) }}</div> | ||
| 90 | + <div>【{{ getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, scope.row.channelCode) }}】</div> | ||
| 91 | + </template> | ||
| 92 | + </el-table-column> | ||
| 93 | + <el-table-column label="模板编号" align="center" prop="templateId" /> | ||
| 94 | + <el-table-column label="短信类型" align="center" prop="templateType"> | ||
| 95 | + <template slot-scope="scope"> | ||
| 96 | + <span>{{ getDictDataLabel(DICT_TYPE.SYS_SMS_TEMPLATE_TYPE, scope.row.templateType) }}</span> | ||
| 97 | + </template> | ||
| 98 | + </el-table-column> | ||
| 99 | + <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> | ||
| 100 | + <template slot-scope="scope"> | ||
| 101 | + <el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row,scope.index)" | ||
| 102 | + v-hasPermi="['system:sms-log:query']">详细</el-button> | ||
| 103 | + </template> | ||
| 104 | + </el-table-column> | ||
| 105 | + </el-table> | ||
| 106 | + <!-- 分页组件 --> | ||
| 107 | + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" | ||
| 108 | + @pagination="getList"/> | ||
| 109 | + | ||
| 110 | + <!-- 短信日志详细 --> | ||
| 111 | + <el-dialog title="短信日志详细" :visible.sync="open" width="700px" append-to-body> | ||
| 112 | + <el-form ref="form" :model="form" label-width="140px" size="mini"> | ||
| 113 | + <el-row> | ||
| 114 | + <el-col :span="24"> | ||
| 115 | + <el-form-item label="日志主键:">{{ form.id }}</el-form-item> | ||
| 116 | + </el-col> | ||
| 117 | + <el-col :span="24"> | ||
| 118 | + <el-form-item label="短信渠道:"> | ||
| 119 | + {{ formatChannelSignature(form.channelId) }}【{{ getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, form.channelCode) }}】 | ||
| 120 | + </el-form-item> | ||
| 121 | + </el-col> | ||
| 122 | + <el-col :span="24"> | ||
| 123 | + <el-form-item label="短信模板:"> | ||
| 124 | + {{ form.templateId }} | {{ form.templateCode}} | {{ getDictDataLabel(DICT_TYPE.SYS_SMS_TEMPLATE_TYPE, form.templateType) }} | ||
| 125 | + </el-form-item> | ||
| 126 | + </el-col> | ||
| 127 | + <el-col :span="24"> | ||
| 128 | + <el-form-item label="API 的模板编号:">{{ form.apiTemplateId }}</el-form-item> | ||
| 129 | + </el-col> | ||
| 130 | + <el-col :span="24"> | ||
| 131 | + <el-form-item label="用户信息:">{{ form.mobile }} | ||
| 132 | + <span v-if="form.userType && form.userId"> | {{ getDictDataLabel(DICT_TYPE.USER_TYPE, form.userType) }} | {{ form.userId }}</span> | ||
| 133 | + </el-form-item> | ||
| 134 | + </el-col> | ||
| 135 | + <el-col :span="24"> | ||
| 136 | + <el-form-item label="短信内容:">{{ form.templateContent }}</el-form-item> | ||
| 137 | + </el-col> | ||
| 138 | + <el-col :span="24"> | ||
| 139 | + <el-form-item label="短信参数:">{{ form.templateParams }}</el-form-item> | ||
| 140 | + </el-col> | ||
| 141 | + <el-col :span="24"> | ||
| 142 | + <el-form-item label="创建时间:">{{ parseTime(form.createTime) }}</el-form-item> | ||
| 143 | + </el-col> | ||
| 144 | + <el-col :span="24"> | ||
| 145 | + <el-form-item label="发送状态:">{{ getDictDataLabel(DICT_TYPE.SYS_SMS_SEND_STATUS, form.sendStatus) }}</el-form-item> | ||
| 146 | + </el-col> | ||
| 147 | + <el-col :span="24"> | ||
| 148 | + <el-form-item label="发送时间:">{{ parseTime(form.sendTime) }}</el-form-item> | ||
| 149 | + </el-col> | ||
| 150 | + <el-col :span="24"> | ||
| 151 | + <el-form-item label="发送结果:">{{ form.sendCode }} | {{ form.sendMsg }} | ||
| 152 | + </el-form-item> | ||
| 153 | + </el-col> | ||
| 154 | + <el-col :span="24"> | ||
| 155 | + <el-form-item label="API 发送结果:">{{ form.apiSendCode }} | {{ form.apiSendMsg }}</el-form-item> | ||
| 156 | + </el-col> | ||
| 157 | + <el-col :span="24"> | ||
| 158 | + <el-form-item label="API 短信编号:">{{ form.apiSerialNo }}</el-form-item> | ||
| 159 | + </el-col> | ||
| 160 | + <el-col :span="24"> | ||
| 161 | + <el-form-item label="API 请求编号:">{{ form.apiRequestId }}</el-form-item> | ||
| 162 | + </el-col> | ||
| 163 | + <el-col :span="24"> | ||
| 164 | + <el-form-item label="接收状态:">{{ getDictDataLabel(DICT_TYPE.SYS_SMS_RECEIVE_STATUS, form.receiveStatus) }}</el-form-item> | ||
| 165 | + </el-col> | ||
| 166 | + <el-col :span="24"> | ||
| 167 | + <el-form-item label="接收时间:">{{ parseTime(form.receiveTime) }}</el-form-item> | ||
| 168 | + </el-col> | ||
| 169 | + <el-col :span="24"> | ||
| 170 | + <el-form-item label="API 接收结果:">{{ form.apiReceiveCode }} | {{ form.apiReceiveMsg }} | ||
| 171 | + </el-form-item> | ||
| 172 | + </el-col> | ||
| 173 | + </el-row> | ||
| 174 | + </el-form> | ||
| 175 | + <div slot="footer" class="dialog-footer"> | ||
| 176 | + <el-button @click="open = false">关 闭</el-button> | ||
| 177 | + </div> | ||
| 178 | + </el-dialog> | ||
| 179 | + | ||
| 180 | + </div> | ||
| 181 | +</template> | ||
| 182 | + | ||
| 183 | +<script> | ||
| 184 | +import { getSmsLogPage, exportSmsLogExcel } from "@/api/system/sms/smsLog"; | ||
| 185 | +import { getSimpleSmsChannels } from "@/api/system/sms/smsChannel"; | ||
| 186 | + | ||
| 187 | +export default { | ||
| 188 | + name: "SmsLog", | ||
| 189 | + components: { | ||
| 190 | + }, | ||
| 191 | + data() { | ||
| 192 | + return { | ||
| 193 | + // 遮罩层 | ||
| 194 | + loading: true, | ||
| 195 | + // 显示搜索条件 | ||
| 196 | + showSearch: true, | ||
| 197 | + // 总条数 | ||
| 198 | + total: 0, | ||
| 199 | + // 短信日志列表 | ||
| 200 | + list: [], | ||
| 201 | + // 弹出层标题 | ||
| 202 | + title: "", | ||
| 203 | + // 是否显示弹出层 | ||
| 204 | + open: false, | ||
| 205 | + dateRangeSendTime: [], | ||
| 206 | + dateRangeReceiveTime: [], | ||
| 207 | + // 表单参数 | ||
| 208 | + form: {}, | ||
| 209 | + // 查询参数 | ||
| 210 | + queryParams: { | ||
| 211 | + pageNo: 1, | ||
| 212 | + pageSize: 10, | ||
| 213 | + channelId: null, | ||
| 214 | + templateId: null, | ||
| 215 | + mobile: null, | ||
| 216 | + sendStatus: null, | ||
| 217 | + receiveStatus: null, | ||
| 218 | + }, | ||
| 219 | + // 短信渠道 | ||
| 220 | + channelOptions: [], | ||
| 221 | + }; | ||
| 222 | + }, | ||
| 223 | + created() { | ||
| 224 | + this.getList(); | ||
| 225 | + // 获得短信渠道 | ||
| 226 | + getSimpleSmsChannels().then(response => { | ||
| 227 | + this.channelOptions = response.data; | ||
| 228 | + }) | ||
| 229 | + }, | ||
| 230 | + methods: { | ||
| 231 | + /** 查询列表 */ | ||
| 232 | + getList() { | ||
| 233 | + this.loading = true; | ||
| 234 | + // 处理查询参数 | ||
| 235 | + let params = {...this.queryParams}; | ||
| 236 | + this.addBeginAndEndTime(params, this.dateRangeSendTime, 'sendTime'); | ||
| 237 | + this.addBeginAndEndTime(params, this.dateRangeReceiveTime, 'receiveTime'); | ||
| 238 | + // 执行查询 | ||
| 239 | + getSmsLogPage(params).then(response => { | ||
| 240 | + this.list = response.data.list; | ||
| 241 | + this.total = response.data.total; | ||
| 242 | + this.loading = false; | ||
| 243 | + }); | ||
| 244 | + }, | ||
| 245 | + /** 取消按钮 */ | ||
| 246 | + cancel() { | ||
| 247 | + this.open = false; | ||
| 248 | + this.reset(); | ||
| 249 | + }, | ||
| 250 | + /** 搜索按钮操作 */ | ||
| 251 | + handleQuery() { | ||
| 252 | + this.queryParams.pageNo = 1; | ||
| 253 | + this.getList(); | ||
| 254 | + }, | ||
| 255 | + /** 重置按钮操作 */ | ||
| 256 | + resetQuery() { | ||
| 257 | + this.dateRangeSendTime = []; | ||
| 258 | + this.dateRangeReceiveTime = []; | ||
| 259 | + this.resetForm("queryForm"); | ||
| 260 | + this.handleQuery(); | ||
| 261 | + }, | ||
| 262 | + /** 导出按钮操作 */ | ||
| 263 | + handleExport() { | ||
| 264 | + // 处理查询参数 | ||
| 265 | + let params = {...this.queryParams}; | ||
| 266 | + params.pageNo = undefined; | ||
| 267 | + params.pageSize = undefined; | ||
| 268 | + this.addBeginAndEndTime(params, this.dateRangeSendTime, 'sendTime'); | ||
| 269 | + this.addBeginAndEndTime(params, this.dateRangeReceiveTime, 'receiveTime'); | ||
| 270 | + // 执行导出 | ||
| 271 | + this.$confirm('是否确认导出所有短信日志数据项?', "警告", { | ||
| 272 | + confirmButtonText: "确定", | ||
| 273 | + cancelButtonText: "取消", | ||
| 274 | + type: "warning" | ||
| 275 | + }).then(function() { | ||
| 276 | + return exportSmsLogExcel(params); | ||
| 277 | + }).then(response => { | ||
| 278 | + this.downloadExcel(response, '短信日志.xls'); | ||
| 279 | + }) | ||
| 280 | + }, | ||
| 281 | + /** 详细按钮操作 */ | ||
| 282 | + handleView(row) { | ||
| 283 | + this.open = true; | ||
| 284 | + this.form = row; | ||
| 285 | + }, | ||
| 286 | + /** 格式化短信渠道 */ | ||
| 287 | + formatChannelSignature(channelId) { | ||
| 288 | + for (const channel of this.channelOptions) { | ||
| 289 | + if (channel.id === channelId) { | ||
| 290 | + return channel.signature; | ||
| 291 | + } | ||
| 292 | + } | ||
| 293 | + return '找不到签名:' + channelId; | ||
| 294 | + } | ||
| 295 | + } | ||
| 296 | +}; | ||
| 297 | +</script> |
ruoyi-ui/src/views/system/sms/smsTemplate.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="app-container"> | ||
| 3 | + | ||
| 4 | + <!-- 搜索工作栏 --> | ||
| 5 | + <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="150px"> | ||
| 6 | + <el-form-item label="短信类型" prop="type"> | ||
| 7 | + <el-select v-model="queryParams.type" placeholder="请选择短信类型" clearable size="small"> | ||
| 8 | + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_SMS_TEMPLATE_TYPE)" | ||
| 9 | + :key="dict.value" :label="dict.label" :value="dict.value"/> | ||
| 10 | + </el-select> | ||
| 11 | + </el-form-item> | ||
| 12 | + <el-form-item label="开启状态" prop="status"> | ||
| 13 | + <el-select v-model="queryParams.status" placeholder="请选择开启状态" clearable size="small"> | ||
| 14 | + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_COMMON_STATUS)" | ||
| 15 | + :key="dict.value" :label="dict.label" :value="dict.value"/> | ||
| 16 | + </el-select> | ||
| 17 | + </el-form-item> | ||
| 18 | + <el-form-item label="模板编码" prop="code"> | ||
| 19 | + <el-input v-model="queryParams.code" placeholder="请输入模板编码" clearable size="small" @keyup.enter.native="handleQuery"/> | ||
| 20 | + </el-form-item> | ||
| 21 | + <el-form-item label="短信 API 的模板编号" prop="apiTemplateId"> | ||
| 22 | + <el-input v-model="queryParams.apiTemplateId" placeholder="请输入短信 API 的模板编号" clearable size="small" @keyup.enter.native="handleQuery"/> | ||
| 23 | + </el-form-item> | ||
| 24 | + <el-form-item label="短信渠道" prop="channelId"> | ||
| 25 | + <el-select v-model="queryParams.channelId" placeholder="请选择短信渠道" clearable size="small"> | ||
| 26 | + <el-option v-for="channel in channelOptions" | ||
| 27 | + :key="channel.id" :value="channel.id" | ||
| 28 | + :label="channel.signature + '【' + getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, channel.code) + '】'" /> | ||
| 29 | + </el-select> | ||
| 30 | + </el-form-item> | ||
| 31 | + <el-form-item label="创建时间"> | ||
| 32 | + <el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd" | ||
| 33 | + type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" /> | ||
| 34 | + </el-form-item> | ||
| 35 | + <el-form-item> | ||
| 36 | + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> | ||
| 37 | + <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> | ||
| 38 | + </el-form-item> | ||
| 39 | + </el-form> | ||
| 40 | + | ||
| 41 | + <!-- 操作工具栏 --> | ||
| 42 | + <el-row :gutter="10" class="mb8"> | ||
| 43 | + <el-col :span="1.5"> | ||
| 44 | + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" | ||
| 45 | + v-hasPermi="['system:sms-template:create']">新增</el-button> | ||
| 46 | + </el-col> | ||
| 47 | + <el-col :span="1.5"> | ||
| 48 | + <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" | ||
| 49 | + v-hasPermi="['system:sms-template:export']">导出</el-button> | ||
| 50 | + </el-col> | ||
| 51 | + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> | ||
| 52 | + </el-row> | ||
| 53 | + | ||
| 54 | + <!-- 列表 --> | ||
| 55 | + <el-table v-loading="loading" :data="list"> | ||
| 56 | + <el-table-column label="模板编码" align="center" prop="code" /> | ||
| 57 | + <el-table-column label="模板名称" align="center" prop="name" /> | ||
| 58 | + <el-table-column label="模板内容" align="center" prop="content" width="300" /> | ||
| 59 | + <el-table-column label="短信类型" align="center" prop="type"> | ||
| 60 | + <template slot-scope="scope"> | ||
| 61 | + <span>{{ getDictDataLabel(DICT_TYPE.SYS_SMS_TEMPLATE_TYPE, scope.row.type) }}</span> | ||
| 62 | + </template> | ||
| 63 | + </el-table-column> | ||
| 64 | + <el-table-column label="开启状态" align="center" prop="status"> | ||
| 65 | + <template slot-scope="scope"> | ||
| 66 | + <span>{{ getDictDataLabel(DICT_TYPE.SYS_COMMON_STATUS, scope.row.status) }}</span> | ||
| 67 | + </template> | ||
| 68 | + </el-table-column> | ||
| 69 | + <el-table-column label="备注" align="center" prop="remark" /> | ||
| 70 | + <el-table-column label="短信 API 的模板编号" align="center" prop="apiTemplateId" width="180" /> | ||
| 71 | + <el-table-column label="短信渠道" align="center" width="120"> | ||
| 72 | + <template slot-scope="scope"> | ||
| 73 | + <div>{{ formatChannelSignature(scope.row.channelId) }}</div> | ||
| 74 | + <div>【{{ getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, scope.row.channelCode) }}】</div> | ||
| 75 | + </template> | ||
| 76 | + </el-table-column> | ||
| 77 | + <el-table-column label="创建时间" align="center" prop="createTime" width="180"> | ||
| 78 | + <template slot-scope="scope"> | ||
| 79 | + <span>{{ parseTime(scope.row.createTime) }}</span> | ||
| 80 | + </template> | ||
| 81 | + </el-table-column> | ||
| 82 | + <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="150"> | ||
| 83 | + <template slot-scope="scope"> | ||
| 84 | + <el-button size="mini" type="text" icon="el-icon-share" @click="handleSendSms(scope.row)" | ||
| 85 | + v-hasPermi="['system:sms-template:send-sms']">测试</el-button> | ||
| 86 | + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" | ||
| 87 | + v-hasPermi="['system:sms-template:update']">修改</el-button> | ||
| 88 | + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" | ||
| 89 | + v-hasPermi="['system:sms-template:delete']">删除</el-button> | ||
| 90 | + </template> | ||
| 91 | + </el-table-column> | ||
| 92 | + </el-table> | ||
| 93 | + <!-- 分页组件 --> | ||
| 94 | + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" | ||
| 95 | + @pagination="getList"/> | ||
| 96 | + | ||
| 97 | + <!-- 对话框(添加 / 修改) --> | ||
| 98 | + <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> | ||
| 99 | + <el-form ref="form" :model="form" :rules="rules" label-width="140px"> | ||
| 100 | + <el-form-item label="短信渠道编号" prop="channelId"> | ||
| 101 | + <el-select v-model="form.channelId" placeholder="请选择短信渠道编号"> | ||
| 102 | + <el-option v-for="channel in channelOptions" | ||
| 103 | + :key="channel.id" :value="channel.id" | ||
| 104 | + :label="channel.signature + '【' + getDictDataLabel(DICT_TYPE.SYS_SMS_CHANNEL_CODE, channel.code) + '】'" /> | ||
| 105 | + </el-select> | ||
| 106 | + </el-form-item> | ||
| 107 | + <el-form-item label="短信类型" prop="type"> | ||
| 108 | + <el-select v-model="form.type" placeholder="请选择短信类型"> | ||
| 109 | + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_SMS_TEMPLATE_TYPE)" | ||
| 110 | + :key="dict.value" :label="dict.label" :value="parseInt(dict.value)" /> | ||
| 111 | + </el-select> | ||
| 112 | + </el-form-item> | ||
| 113 | + <el-form-item label="模板编号" prop="code"> | ||
| 114 | + <el-input v-model="form.code" placeholder="请输入模板编号" /> | ||
| 115 | + </el-form-item> | ||
| 116 | + <el-form-item label="模板名称" prop="name"> | ||
| 117 | + <el-input v-model="form.name" placeholder="请输入模板名称" /> | ||
| 118 | + </el-form-item> | ||
| 119 | + <el-form-item label="模板内容" prop="content"> | ||
| 120 | + <el-input type="textarea" v-model="form.content" placeholder="请输入模板内容" /> | ||
| 121 | + </el-form-item> | ||
| 122 | + <el-form-item label="开启状态" prop="status"> | ||
| 123 | + <el-radio-group v-model="form.status"> | ||
| 124 | + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.SYS_COMMON_STATUS)" | ||
| 125 | + :key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio> | ||
| 126 | + </el-radio-group> | ||
| 127 | + </el-form-item> | ||
| 128 | + <el-form-item label="短信 API 模板编号" prop="apiTemplateId"> | ||
| 129 | + <el-input v-model="form.apiTemplateId" placeholder="请输入短信 API 的模板编号" /> | ||
| 130 | + </el-form-item> | ||
| 131 | + <el-form-item label="备注" prop="remark"> | ||
| 132 | + <el-input v-model="form.remark" placeholder="请输入备注" /> | ||
| 133 | + </el-form-item> | ||
| 134 | + </el-form> | ||
| 135 | + <div slot="footer" class="dialog-footer"> | ||
| 136 | + <el-button type="primary" @click="submitForm">确 定</el-button> | ||
| 137 | + <el-button @click="cancel">取 消</el-button> | ||
| 138 | + </div> | ||
| 139 | + </el-dialog> | ||
| 140 | + | ||
| 141 | + <!-- 对话框(发送短信) --> | ||
| 142 | + <el-dialog title="测试发送短信" :visible.sync="sendSmsOpen" width="500px" append-to-body> | ||
| 143 | + <el-form ref="sendSmsForm" :model="sendSmsForm" :rules="sendSmsRules" label-width="140px"> | ||
| 144 | + <el-form-item label="模板内容" prop="content"> | ||
| 145 | + <el-input v-model="sendSmsForm.content" type="textarea" placeholder="请输入模板内容" readonly /> | ||
| 146 | + </el-form-item> | ||
| 147 | + <el-form-item label="手机号" prop="mobile"> | ||
| 148 | + <el-input v-model="sendSmsForm.mobile" placeholder="请输入手机号" /> | ||
| 149 | + </el-form-item> | ||
| 150 | + <el-form-item v-for="param in sendSmsForm.params" :label="'参数 {' + param + '}'" :prop="'templateParams.' + param"> | ||
| 151 | + <el-input v-model="sendSmsForm.templateParams[param]" :placeholder="'请输入 ' + param + ' 参数'" /> | ||
| 152 | + </el-form-item> | ||
| 153 | + </el-form> | ||
| 154 | + <div slot="footer" class="dialog-footer"> | ||
| 155 | + <el-button type="primary" @click="submitSendSmsForm">确 定</el-button> | ||
| 156 | + <el-button @click="cancelSendSms">取 消</el-button> | ||
| 157 | + </div> | ||
| 158 | + </el-dialog> | ||
| 159 | + | ||
| 160 | + </div> | ||
| 161 | +</template> | ||
| 162 | + | ||
| 163 | +<script> | ||
| 164 | +import { createSmsTemplate, updateSmsTemplate, deleteSmsTemplate, getSmsTemplate, getSmsTemplatePage, | ||
| 165 | + exportSmsTemplateExcel, sendSms } from "@/api/system/sms/smsTemplate"; | ||
| 166 | +import { getSimpleSmsChannels } from "@/api/system/sms/smsChannel"; | ||
| 167 | + | ||
| 168 | +export default { | ||
| 169 | + name: "SmsTemplate", | ||
| 170 | + data() { | ||
| 171 | + return { | ||
| 172 | + // 遮罩层 | ||
| 173 | + loading: true, | ||
| 174 | + // 显示搜索条件 | ||
| 175 | + showSearch: true, | ||
| 176 | + // 总条数 | ||
| 177 | + total: 0, | ||
| 178 | + // 短信模板列表 | ||
| 179 | + list: [], | ||
| 180 | + // 弹出层标题 | ||
| 181 | + title: "", | ||
| 182 | + // 是否显示弹出层 | ||
| 183 | + open: false, | ||
| 184 | + dateRangeCreateTime: [], | ||
| 185 | + // 查询参数 | ||
| 186 | + queryParams: { | ||
| 187 | + pageNo: 1, | ||
| 188 | + pageSize: 10, | ||
| 189 | + type: null, | ||
| 190 | + status: null, | ||
| 191 | + code: null, | ||
| 192 | + content: null, | ||
| 193 | + apiTemplateId: null, | ||
| 194 | + channelId: null, | ||
| 195 | + }, | ||
| 196 | + // 表单参数 | ||
| 197 | + form: {}, | ||
| 198 | + // 表单校验 | ||
| 199 | + rules: { | ||
| 200 | + type: [{ required: true, message: "短信类型不能为空", trigger: "change" }], | ||
| 201 | + status: [{ required: true, message: "开启状态不能为空", trigger: "blur" }], | ||
| 202 | + code: [{ required: true, message: "模板编码不能为空", trigger: "blur" }], | ||
| 203 | + name: [{ required: true, message: "模板名称不能为空", trigger: "blur" }], | ||
| 204 | + content: [{ required: true, message: "模板内容不能为空", trigger: "blur" }], | ||
| 205 | + apiTemplateId: [{ required: true, message: "短信 API 的模板编号不能为空", trigger: "blur" }], | ||
| 206 | + channelId: [{ required: true, message: "短信渠道编号不能为空", trigger: "change" }], | ||
| 207 | + }, | ||
| 208 | + // 短信渠道 | ||
| 209 | + channelOptions: [], | ||
| 210 | + // 发送短信 | ||
| 211 | + sendSmsOpen: false, | ||
| 212 | + sendSmsForm: { | ||
| 213 | + params: [], // 模板的参数列表 | ||
| 214 | + }, | ||
| 215 | + sendSmsRules: { | ||
| 216 | + mobile: [{ required: true, message: "手机不能为空", trigger: "blur" }], | ||
| 217 | + templateCode: [{ required: true, message: "手机不能为空", trigger: "blur" }], | ||
| 218 | + templateParams: { } | ||
| 219 | + } | ||
| 220 | + }; | ||
| 221 | + }, | ||
| 222 | + created() { | ||
| 223 | + this.getList(); | ||
| 224 | + // 获得短信渠道 | ||
| 225 | + getSimpleSmsChannels().then(response => { | ||
| 226 | + this.channelOptions = response.data; | ||
| 227 | + }) | ||
| 228 | + }, | ||
| 229 | + methods: { | ||
| 230 | + /** 查询列表 */ | ||
| 231 | + getList() { | ||
| 232 | + this.loading = true; | ||
| 233 | + // 处理查询参数 | ||
| 234 | + let params = {...this.queryParams}; | ||
| 235 | + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime'); | ||
| 236 | + // 执行查询 | ||
| 237 | + getSmsTemplatePage(params).then(response => { | ||
| 238 | + this.list = response.data.list; | ||
| 239 | + this.total = response.data.total; | ||
| 240 | + this.loading = false; | ||
| 241 | + }); | ||
| 242 | + }, | ||
| 243 | + /** 取消按钮 */ | ||
| 244 | + cancel() { | ||
| 245 | + this.open = false; | ||
| 246 | + this.reset(); | ||
| 247 | + }, | ||
| 248 | + /** 表单重置 */ | ||
| 249 | + reset() { | ||
| 250 | + this.form = { | ||
| 251 | + id: undefined, | ||
| 252 | + type: undefined, | ||
| 253 | + status: undefined, | ||
| 254 | + code: undefined, | ||
| 255 | + name: undefined, | ||
| 256 | + content: undefined, | ||
| 257 | + remark: undefined, | ||
| 258 | + apiTemplateId: undefined, | ||
| 259 | + channelId: undefined, | ||
| 260 | + }; | ||
| 261 | + this.resetForm("form"); | ||
| 262 | + }, | ||
| 263 | + /** 搜索按钮操作 */ | ||
| 264 | + handleQuery() { | ||
| 265 | + this.queryParams.pageNo = 1; | ||
| 266 | + this.getList(); | ||
| 267 | + }, | ||
| 268 | + /** 重置按钮操作 */ | ||
| 269 | + resetQuery() { | ||
| 270 | + this.dateRangeCreateTime = []; | ||
| 271 | + this.resetForm("queryForm"); | ||
| 272 | + this.handleQuery(); | ||
| 273 | + }, | ||
| 274 | + /** 新增按钮操作 */ | ||
| 275 | + handleAdd() { | ||
| 276 | + this.reset(); | ||
| 277 | + this.open = true; | ||
| 278 | + this.title = "添加短信模板"; | ||
| 279 | + }, | ||
| 280 | + /** 修改按钮操作 */ | ||
| 281 | + handleUpdate(row) { | ||
| 282 | + this.reset(); | ||
| 283 | + const id = row.id; | ||
| 284 | + getSmsTemplate(id).then(response => { | ||
| 285 | + this.form = response.data; | ||
| 286 | + this.open = true; | ||
| 287 | + this.title = "修改短信模板"; | ||
| 288 | + }); | ||
| 289 | + }, | ||
| 290 | + /** 提交按钮 */ | ||
| 291 | + submitForm() { | ||
| 292 | + this.$refs["form"].validate(valid => { | ||
| 293 | + if (!valid) { | ||
| 294 | + return; | ||
| 295 | + } | ||
| 296 | + // 修改的提交 | ||
| 297 | + if (this.form.id != null) { | ||
| 298 | + updateSmsTemplate(this.form).then(response => { | ||
| 299 | + this.msgSuccess("修改成功"); | ||
| 300 | + this.open = false; | ||
| 301 | + this.getList(); | ||
| 302 | + }); | ||
| 303 | + return; | ||
| 304 | + } | ||
| 305 | + // 添加的提交 | ||
| 306 | + createSmsTemplate(this.form).then(response => { | ||
| 307 | + this.msgSuccess("新增成功"); | ||
| 308 | + this.open = false; | ||
| 309 | + this.getList(); | ||
| 310 | + }); | ||
| 311 | + }); | ||
| 312 | + }, | ||
| 313 | + /** 删除按钮操作 */ | ||
| 314 | + handleDelete(row) { | ||
| 315 | + const id = row.id; | ||
| 316 | + this.$confirm('是否确认删除短信模板编号为"' + id + '"的数据项?', "警告", { | ||
| 317 | + confirmButtonText: "确定", | ||
| 318 | + cancelButtonText: "取消", | ||
| 319 | + type: "warning" | ||
| 320 | + }).then(function() { | ||
| 321 | + return deleteSmsTemplate(id); | ||
| 322 | + }).then(() => { | ||
| 323 | + this.getList(); | ||
| 324 | + this.msgSuccess("删除成功"); | ||
| 325 | + }) | ||
| 326 | + }, | ||
| 327 | + /** 导出按钮操作 */ | ||
| 328 | + handleExport() { | ||
| 329 | + // 处理查询参数 | ||
| 330 | + let params = {...this.queryParams}; | ||
| 331 | + params.pageNo = undefined; | ||
| 332 | + params.pageSize = undefined; | ||
| 333 | + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime'); | ||
| 334 | + // 执行导出 | ||
| 335 | + this.$confirm('是否确认导出所有短信模板数据项?', "警告", { | ||
| 336 | + confirmButtonText: "确定", | ||
| 337 | + cancelButtonText: "取消", | ||
| 338 | + type: "warning" | ||
| 339 | + }).then(function() { | ||
| 340 | + return exportSmsTemplateExcel(params); | ||
| 341 | + }).then(response => { | ||
| 342 | + this.downloadExcel(response, '短信模板.xls'); | ||
| 343 | + }) | ||
| 344 | + }, | ||
| 345 | + /** 发送短息按钮 */ | ||
| 346 | + handleSendSms(row) { | ||
| 347 | + this.resetSendSms(row); | ||
| 348 | + // 设置参数 | ||
| 349 | + this.sendSmsForm.content = row.content; | ||
| 350 | + this.sendSmsForm.params = row.params; | ||
| 351 | + this.sendSmsForm.templateCode = row.code; | ||
| 352 | + this.sendSmsForm.templateParams = row.params.reduce(function(obj, item) { | ||
| 353 | + obj[item] = undefined; | ||
| 354 | + return obj; | ||
| 355 | + }, {}); | ||
| 356 | + // 根据 row 重置 rules | ||
| 357 | + this.sendSmsRules.templateParams = row.params.reduce(function(obj, item) { | ||
| 358 | + obj[item] = { required: true, message: '参数 ' + item + " 不能为空", trigger: "change" }; | ||
| 359 | + return obj; | ||
| 360 | + }, {}); | ||
| 361 | + // 设置打开 | ||
| 362 | + this.sendSmsOpen = true; | ||
| 363 | + }, | ||
| 364 | + /** 重置发送短信的表单 */ | ||
| 365 | + resetSendSms() { | ||
| 366 | + // 根据 row 重置表单 | ||
| 367 | + this.sendSmsForm = { | ||
| 368 | + content: undefined, | ||
| 369 | + params: undefined, | ||
| 370 | + mobile: undefined, | ||
| 371 | + templateCode: undefined, | ||
| 372 | + templateParams: {} | ||
| 373 | + }; | ||
| 374 | + this.resetForm("sendSmsForm"); | ||
| 375 | + }, | ||
| 376 | + /** 取消发送短信 */ | ||
| 377 | + cancelSendSms() { | ||
| 378 | + this.sendSmsOpen = false; | ||
| 379 | + this.resetSendSms(); | ||
| 380 | + }, | ||
| 381 | + /** 提交按钮 */ | ||
| 382 | + submitSendSmsForm() { | ||
| 383 | + this.$refs["sendSmsForm"].validate(valid => { | ||
| 384 | + if (!valid) { | ||
| 385 | + return; | ||
| 386 | + } | ||
| 387 | + // 添加的提交 | ||
| 388 | + sendSms(this.sendSmsForm).then(response => { | ||
| 389 | + this.msgSuccess("提交发送成功!发送结果,见发送日志编号:" + response.data); | ||
| 390 | + this.sendSmsOpen = false; | ||
| 391 | + }); | ||
| 392 | + }); | ||
| 393 | + }, | ||
| 394 | + /** 格式化短信渠道 */ | ||
| 395 | + formatChannelSignature(channelId) { | ||
| 396 | + for (const channel of this.channelOptions) { | ||
| 397 | + if (channel.id === channelId) { | ||
| 398 | + return channel.signature; | ||
| 399 | + } | ||
| 400 | + } | ||
| 401 | + return '找不到签名:' + channelId; | ||
| 402 | + } | ||
| 403 | + } | ||
| 404 | +}; | ||
| 405 | +</script> |
sql/ruoyi-vue-pro.sql
| 1 | /* | 1 | /* |
| 2 | Navicat Premium Data Transfer | 2 | Navicat Premium Data Transfer |
| 3 | 3 | ||
| 4 | - Source Server : 127.0.0.1 | 4 | + Source Server : local-mysql001 |
| 5 | Source Server Type : MySQL | 5 | Source Server Type : MySQL |
| 6 | Source Server Version : 50718 | 6 | Source Server Version : 50718 |
| 7 | Source Host : localhost:3306 | 7 | Source Host : localhost:3306 |
| @@ -11,7 +11,7 @@ | @@ -11,7 +11,7 @@ | ||
| 11 | Target Server Version : 50718 | 11 | Target Server Version : 50718 |
| 12 | File Encoding : 65001 | 12 | File Encoding : 65001 |
| 13 | 13 | ||
| 14 | - Date: 21/03/2021 18:53:24 | 14 | + Date: 05/04/2021 23:51:38 |
| 15 | */ | 15 | */ |
| 16 | 16 | ||
| 17 | SET NAMES utf8mb4; | 17 | SET NAMES utf8mb4; |
| @@ -43,7 +43,7 @@ CREATE TABLE `inf_api_access_log` ( | @@ -43,7 +43,7 @@ CREATE TABLE `inf_api_access_log` ( | ||
| 43 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', | 43 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 44 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', | 44 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 45 | PRIMARY KEY (`id`) USING BTREE | 45 | PRIMARY KEY (`id`) USING BTREE |
| 46 | -) ENGINE=InnoDB AUTO_INCREMENT=1318 DEFAULT CHARSET=utf8mb4 COMMENT='API 访问日志表'; | 46 | +) ENGINE=InnoDB AUTO_INCREMENT=1909 DEFAULT CHARSET=utf8mb4 COMMENT='API 访问日志表'; |
| 47 | 47 | ||
| 48 | -- ---------------------------- | 48 | -- ---------------------------- |
| 49 | -- Records of inf_api_access_log | 49 | -- Records of inf_api_access_log |
| @@ -84,7 +84,7 @@ CREATE TABLE `inf_api_error_log` ( | @@ -84,7 +84,7 @@ CREATE TABLE `inf_api_error_log` ( | ||
| 84 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', | 84 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 85 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', | 85 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 86 | PRIMARY KEY (`id`) USING BTREE | 86 | PRIMARY KEY (`id`) USING BTREE |
| 87 | -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COMMENT='系统异常日志'; | 87 | +) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COMMENT='系统异常日志'; |
| 88 | 88 | ||
| 89 | -- ---------------------------- | 89 | -- ---------------------------- |
| 90 | -- Records of inf_api_error_log | 90 | -- Records of inf_api_error_log |
| @@ -201,7 +201,7 @@ CREATE TABLE `inf_job_log` ( | @@ -201,7 +201,7 @@ CREATE TABLE `inf_job_log` ( | ||
| 201 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', | 201 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 202 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', | 202 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 203 | PRIMARY KEY (`id`) USING BTREE | 203 | PRIMARY KEY (`id`) USING BTREE |
| 204 | -) ENGINE=InnoDB AUTO_INCREMENT=627 DEFAULT CHARSET=utf8mb4 COMMENT='定时任务日志表'; | 204 | +) ENGINE=InnoDB AUTO_INCREMENT=1035 DEFAULT CHARSET=utf8mb4 COMMENT='定时任务日志表'; |
| 205 | 205 | ||
| 206 | -- ---------------------------- | 206 | -- ---------------------------- |
| 207 | -- Records of inf_job_log | 207 | -- Records of inf_job_log |
| @@ -264,7 +264,7 @@ CREATE TABLE `sys_dict_data` ( | @@ -264,7 +264,7 @@ CREATE TABLE `sys_dict_data` ( | ||
| 264 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', | 264 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 265 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', | 265 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 266 | PRIMARY KEY (`id`) USING BTREE | 266 | PRIMARY KEY (`id`) USING BTREE |
| 267 | -) ENGINE=InnoDB AUTO_INCREMENT=65 DEFAULT CHARSET=utf8mb4 COMMENT='å—典数æ®è¡¨'; | 267 | +) ENGINE=InnoDB AUTO_INCREMENT=70 DEFAULT CHARSET=utf8mb4 COMMENT='å—典数æ®è¡¨'; |
| 268 | 268 | ||
| 269 | -- ---------------------------- | 269 | -- ---------------------------- |
| 270 | -- Records of sys_dict_data | 270 | -- Records of sys_dict_data |
| @@ -324,6 +324,11 @@ INSERT INTO `sys_dict_data` VALUES (61, 2, '管ç†å‘˜', '2', 'user_type', 0, NUL | @@ -324,6 +324,11 @@ INSERT INTO `sys_dict_data` VALUES (61, 2, '管ç†å‘˜', '2', 'user_type', 0, NUL | ||
| 324 | INSERT INTO `sys_dict_data` VALUES (62, 0, '未处ç†', '0', 'inf_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:19', '', '2021-02-26 08:11:23', b'0'); | 324 | INSERT INTO `sys_dict_data` VALUES (62, 0, '未处ç†', '0', 'inf_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:19', '', '2021-02-26 08:11:23', b'0'); |
| 325 | INSERT INTO `sys_dict_data` VALUES (63, 1, '已处ç†', '1', 'inf_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:26', '', '2021-02-26 08:11:29', b'0'); | 325 | INSERT INTO `sys_dict_data` VALUES (63, 1, '已处ç†', '1', 'inf_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:26', '', '2021-02-26 08:11:29', b'0'); |
| 326 | INSERT INTO `sys_dict_data` VALUES (64, 2, '已忽略', '2', 'inf_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:34', '', '2021-02-26 08:11:34', b'0'); | 326 | INSERT INTO `sys_dict_data` VALUES (64, 2, '已忽略', '2', 'inf_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:34', '', '2021-02-26 08:11:34', b'0'); |
| 327 | +INSERT INTO `sys_dict_data` VALUES (65, 0, '云片', 'YUN_PIAN', 'sys_sms_channel_code', 0, NULL, '1', '2021-04-05 01:05:14', '1', '2021-04-05 01:05:14', b'0'); | ||
| 328 | +INSERT INTO `sys_dict_data` VALUES (66, 0, '阿里云', 'ALIYUN', 'sys_sms_channel_code', 0, NULL, '1', '2021-04-05 01:05:26', '1', '2021-04-05 01:05:26', b'0'); | ||
| 329 | +INSERT INTO `sys_dict_data` VALUES (67, 1, '验è¯ç ', '1', 'sys_sms_template_type', 0, NULL, '1', '2021-04-05 21:50:57', '1', '2021-04-05 21:50:57', b'0'); | ||
| 330 | +INSERT INTO `sys_dict_data` VALUES (68, 2, '通知', '2', 'sys_sms_template_type', 0, NULL, '1', '2021-04-05 21:51:08', '1', '2021-04-05 21:51:08', b'0'); | ||
| 331 | +INSERT INTO `sys_dict_data` VALUES (69, 0, 'è¥é”€', '3', 'sys_sms_template_type', 0, NULL, '1', '2021-04-05 21:51:15', '1', '2021-04-05 21:51:15', b'0'); | ||
| 327 | COMMIT; | 332 | COMMIT; |
| 328 | 333 | ||
| 329 | -- ---------------------------- | 334 | -- ---------------------------- |
| @@ -343,7 +348,7 @@ CREATE TABLE `sys_dict_type` ( | @@ -343,7 +348,7 @@ CREATE TABLE `sys_dict_type` ( | ||
| 343 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', | 348 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 344 | PRIMARY KEY (`id`) USING BTREE, | 349 | PRIMARY KEY (`id`) USING BTREE, |
| 345 | UNIQUE KEY `dict_type` (`type`) | 350 | UNIQUE KEY `dict_type` (`type`) |
| 346 | -) ENGINE=InnoDB AUTO_INCREMENT=111 DEFAULT CHARSET=utf8mb4 COMMENT='å—典类型表'; | 351 | +) ENGINE=InnoDB AUTO_INCREMENT=113 DEFAULT CHARSET=utf8mb4 COMMENT='å—典类型表'; |
| 347 | 352 | ||
| 348 | -- ---------------------------- | 353 | -- ---------------------------- |
| 349 | -- Records of sys_dict_type | 354 | -- Records of sys_dict_type |
| @@ -366,6 +371,8 @@ INSERT INTO `sys_dict_type` VALUES (107, '定时任务状æ€', 'inf_job_status', | @@ -366,6 +371,8 @@ INSERT INTO `sys_dict_type` VALUES (107, '定时任务状æ€', 'inf_job_status', | ||
| 366 | INSERT INTO `sys_dict_type` VALUES (108, '定时任务日志状æ€', 'inf_job_log_status', 0, NULL, '', '2021-02-08 10:03:51', '', '2021-02-08 10:03:51', b'0'); | 371 | INSERT INTO `sys_dict_type` VALUES (108, '定时任务日志状æ€', 'inf_job_log_status', 0, NULL, '', '2021-02-08 10:03:51', '', '2021-02-08 10:03:51', b'0'); |
| 367 | INSERT INTO `sys_dict_type` VALUES (109, '用户类型', 'user_type', 0, NULL, '', '2021-02-26 00:15:51', '', '2021-02-26 00:15:51', b'0'); | 372 | INSERT INTO `sys_dict_type` VALUES (109, '用户类型', 'user_type', 0, NULL, '', '2021-02-26 00:15:51', '', '2021-02-26 00:15:51', b'0'); |
| 368 | INSERT INTO `sys_dict_type` VALUES (110, 'API 异常数æ®çš„处ç†çжæ€', 'inf_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:01', '', '2021-02-26 07:07:01', b'0'); | 373 | INSERT INTO `sys_dict_type` VALUES (110, 'API 异常数æ®çš„处ç†çжæ€', 'inf_api_error_log_process_status', 0, NULL, '', '2021-02-26 07:07:01', '', '2021-02-26 07:07:01', b'0'); |
| 374 | +INSERT INTO `sys_dict_type` VALUES (111, 'çŸä¿¡æ¸ é“ç¼–ç ', 'sys_sms_channel_code', 0, NULL, '1', '2021-04-05 01:04:50', '1', '2021-04-05 01:04:50', b'0'); | ||
| 375 | +INSERT INTO `sys_dict_type` VALUES (112, 'çŸä¿¡æ¨¡æ¿çš„类型', 'sys_sms_template_type', 0, NULL, '1', '2021-04-05 21:50:43', '1', '2021-04-05 21:50:43', b'0'); | ||
| 369 | COMMIT; | 376 | COMMIT; |
| 370 | 377 | ||
| 371 | -- ---------------------------- | 378 | -- ---------------------------- |
| @@ -386,7 +393,7 @@ CREATE TABLE `sys_login_log` ( | @@ -386,7 +393,7 @@ CREATE TABLE `sys_login_log` ( | ||
| 386 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', | 393 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 387 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', | 394 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 388 | PRIMARY KEY (`id`) USING BTREE | 395 | PRIMARY KEY (`id`) USING BTREE |
| 389 | -) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8mb4 COMMENT='系统访问记录'; | 396 | +) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8mb4 COMMENT='系统访问记录'; |
| 390 | 397 | ||
| 391 | -- ---------------------------- | 398 | -- ---------------------------- |
| 392 | -- Records of sys_login_log | 399 | -- Records of sys_login_log |
| @@ -415,7 +422,7 @@ CREATE TABLE `sys_menu` ( | @@ -415,7 +422,7 @@ CREATE TABLE `sys_menu` ( | ||
| 415 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', | 422 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 416 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', | 423 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 417 | PRIMARY KEY (`id`) USING BTREE | 424 | PRIMARY KEY (`id`) USING BTREE |
| 418 | -) ENGINE=InnoDB AUTO_INCREMENT=1093 DEFAULT CHARSET=utf8mb4 COMMENT='èœå•æƒé™è¡¨'; | 425 | +) ENGINE=InnoDB AUTO_INCREMENT=1100 DEFAULT CHARSET=utf8mb4 COMMENT='èœå•æƒé™è¡¨'; |
| 419 | 426 | ||
| 420 | -- ---------------------------- | 427 | -- ---------------------------- |
| 421 | -- Records of sys_menu | 428 | -- Records of sys_menu |
| @@ -530,6 +537,12 @@ INSERT INTO `sys_menu` VALUES (1089, '日志查询', 'infra:api-error-log:query' | @@ -530,6 +537,12 @@ INSERT INTO `sys_menu` VALUES (1089, '日志查询', 'infra:api-error-log:query' | ||
| 530 | INSERT INTO `sys_menu` VALUES (1090, '文件管ç†', '', 2, 0, 2, 'file', 'upload', 'infra/file/index', 0, '', '2021-03-12 20:16:20', '1', '2021-03-13 11:07:05', b'0'); | 537 | INSERT INTO `sys_menu` VALUES (1090, '文件管ç†', '', 2, 0, 2, 'file', 'upload', 'infra/file/index', 0, '', '2021-03-12 20:16:20', '1', '2021-03-13 11:07:05', b'0'); |
| 531 | INSERT INTO `sys_menu` VALUES (1091, '文件查询', 'infra:file:query', 3, 1, 1090, '', '', '', 0, '', '2021-03-12 20:16:20', '', '2021-03-12 20:16:20', b'0'); | 538 | INSERT INTO `sys_menu` VALUES (1091, '文件查询', 'infra:file:query', 3, 1, 1090, '', '', '', 0, '', '2021-03-12 20:16:20', '', '2021-03-12 20:16:20', b'0'); |
| 532 | INSERT INTO `sys_menu` VALUES (1092, 'æ–‡ä»¶åˆ é™¤', 'infra:file:delete', 3, 4, 1090, '', '', '', 0, '', '2021-03-12 20:16:20', '', '2021-03-12 20:16:20', b'0'); | 539 | INSERT INTO `sys_menu` VALUES (1092, 'æ–‡ä»¶åˆ é™¤', 'infra:file:delete', 3, 4, 1090, '', '', '', 0, '', '2021-03-12 20:16:20', '', '2021-03-12 20:16:20', b'0'); |
| 540 | +INSERT INTO `sys_menu` VALUES (1093, 'çŸä¿¡ç®¡ç†', '', 1, 11, 1, 'sms', 'validCode', NULL, 0, '1', '2021-04-05 01:10:16', '1', '2021-04-05 01:11:38', b'0'); | ||
| 541 | +INSERT INTO `sys_menu` VALUES (1094, 'çŸä¿¡æ¸ é“', '', 2, 0, 1093, 'sms-channel', '', 'system/sms/smsChannel', 0, '', '2021-04-01 11:07:15', '1', '2021-04-05 20:32:53', b'0'); | ||
| 542 | +INSERT INTO `sys_menu` VALUES (1095, 'çŸä¿¡æ¸ 铿Ÿ¥è¯¢', 'system:sms-channel:query', 3, 1, 1094, '', '', '', 0, '', '2021-04-01 11:07:15', '', '2021-04-01 11:07:15', b'0'); | ||
| 543 | +INSERT INTO `sys_menu` VALUES (1096, 'çŸä¿¡æ¸ é“创建', 'system:sms-channel:create', 3, 2, 1094, '', '', '', 0, '', '2021-04-01 11:07:15', '', '2021-04-01 11:07:15', b'0'); | ||
| 544 | +INSERT INTO `sys_menu` VALUES (1097, 'çŸä¿¡æ¸ 铿›´æ–°', 'system:sms-channel:update', 3, 3, 1094, '', '', '', 0, '', '2021-04-01 11:07:15', '', '2021-04-01 11:07:15', b'0'); | ||
| 545 | +INSERT INTO `sys_menu` VALUES (1098, 'çŸä¿¡æ¸ é“åˆ é™¤', 'system:sms-channel:delete', 3, 4, 1094, '', '', '', 0, '', '2021-04-01 11:07:15', '', '2021-04-01 11:07:15', b'0'); | ||
| 533 | COMMIT; | 546 | COMMIT; |
| 534 | 547 | ||
| 535 | -- ---------------------------- | 548 | -- ---------------------------- |
| @@ -589,7 +602,7 @@ CREATE TABLE `sys_operate_log` ( | @@ -589,7 +602,7 @@ CREATE TABLE `sys_operate_log` ( | ||
| 589 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', | 602 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 590 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', | 603 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 591 | PRIMARY KEY (`id`) USING BTREE | 604 | PRIMARY KEY (`id`) USING BTREE |
| 592 | -) ENGINE=InnoDB AUTO_INCREMENT=65 DEFAULT CHARSET=utf8mb4 COMMENT='æ“作日志记录'; | 605 | +) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8mb4 COMMENT='æ“作日志记录'; |
| 593 | 606 | ||
| 594 | -- ---------------------------- | 607 | -- ---------------------------- |
| 595 | -- Records of sys_operate_log | 608 | -- Records of sys_operate_log |
| @@ -835,6 +848,118 @@ INSERT INTO `sys_role_menu` VALUES (238, 101, 1065, '', '2021-01-21 03:23:27', ' | @@ -835,6 +848,118 @@ INSERT INTO `sys_role_menu` VALUES (238, 101, 1065, '', '2021-01-21 03:23:27', ' | ||
| 835 | COMMIT; | 848 | COMMIT; |
| 836 | 849 | ||
| 837 | -- ---------------------------- | 850 | -- ---------------------------- |
| 851 | +-- Table structure for sys_sms_channel | ||
| 852 | +-- ---------------------------- | ||
| 853 | +DROP TABLE IF EXISTS `sys_sms_channel`; | ||
| 854 | +CREATE TABLE `sys_sms_channel` ( | ||
| 855 | + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ç¼–å·', | ||
| 856 | + `signature` varchar(10) NOT NULL COMMENT 'çŸä¿¡ç¾å', | ||
| 857 | + `code` varchar(63) NOT NULL COMMENT 'æ¸ é“ç¼–ç ', | ||
| 858 | + `status` tinyint(4) NOT NULL COMMENT 'å¼€å¯çжæ€', | ||
| 859 | + `remark` varchar(255) DEFAULT NULL COMMENT '备注', | ||
| 860 | + `api_key` varchar(63) NOT NULL COMMENT 'çŸä¿¡ API 的账å·', | ||
| 861 | + `api_secret` varchar(63) DEFAULT NULL COMMENT 'çŸä¿¡ API 的秘钥', | ||
| 862 | + `callback_url` varchar(255) DEFAULT NULL COMMENT 'çŸä¿¡å‘é€å›žè°ƒ URL', | ||
| 863 | + `creator` varchar(64) DEFAULT '' COMMENT '创建者', | ||
| 864 | + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | ||
| 865 | + `updater` varchar(64) DEFAULT '' COMMENT '更新者', | ||
| 866 | + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', | ||
| 867 | + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', | ||
| 868 | + PRIMARY KEY (`id`) USING BTREE | ||
| 869 | +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COMMENT='çŸä¿¡æ¸ é“'; | ||
| 870 | + | ||
| 871 | +-- ---------------------------- | ||
| 872 | +-- Records of sys_sms_channel | ||
| 873 | +-- ---------------------------- | ||
| 874 | +BEGIN; | ||
| 875 | +INSERT INTO `sys_sms_channel` VALUES (1, '芋é“', 'YUN_PIAN', 0, '呵呵呵哒', '1555a14277cb8a608cf45a9e6a80d510', NULL, 'http://java.nat300.top/api/system/sms/callback/sms/yunpian', '', '2021-03-31 06:12:20', '1', '2021-04-05 21:02:38', b'0'); | ||
| 876 | +INSERT INTO `sys_sms_channel` VALUES (2, 'Ballcat', 'ALIYUN', 0, '啦啦啦', 'LTAI5tCnKso2uG3kJ5gRav88', 'fGJ5SNXL7P1NHNRmJ7DJaMJGPyE55C', NULL, '', '2021-03-31 11:53:10', '1', '2021-04-05 21:02:44', b'0'); | ||
| 877 | +INSERT INTO `sys_sms_channel` VALUES (3, '测试', 'YUN_PIAN', 0, '哈哈哈', '23132', NULL, 'http://www.baidu.com', '1', '2021-04-05 21:10:34', '1', '2021-04-05 21:10:34', b'0'); | ||
| 878 | +COMMIT; | ||
| 879 | + | ||
| 880 | +-- ---------------------------- | ||
| 881 | +-- Table structure for sys_sms_log | ||
| 882 | +-- ---------------------------- | ||
| 883 | +DROP TABLE IF EXISTS `sys_sms_log`; | ||
| 884 | +CREATE TABLE `sys_sms_log` ( | ||
| 885 | + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ç¼–å·', | ||
| 886 | + `channel_id` bigint(20) NOT NULL COMMENT 'çŸä¿¡æ¸ é“ç¼–å·', | ||
| 887 | + `channel_code` varchar(63) NOT NULL COMMENT 'çŸä¿¡æ¸ é“ç¼–ç ', | ||
| 888 | + `template_id` bigint(20) NOT NULL COMMENT '模æ¿ç¼–å·', | ||
| 889 | + `template_code` varchar(63) NOT NULL COMMENT '模æ¿ç¼–ç ', | ||
| 890 | + `template_type` tinyint(4) NOT NULL COMMENT 'çŸä¿¡ç±»åž‹', | ||
| 891 | + `template_content` varchar(255) NOT NULL COMMENT 'çŸä¿¡å†…容', | ||
| 892 | + `template_params` varchar(255) NOT NULL COMMENT 'çŸä¿¡å‚æ•°', | ||
| 893 | + `api_template_id` varchar(63) NOT NULL COMMENT 'çŸä¿¡ API 的模æ¿ç¼–å·', | ||
| 894 | + `mobile` varchar(11) NOT NULL COMMENT '手机å·', | ||
| 895 | + `user_id` bigint(20) DEFAULT '0' COMMENT '用户编å·', | ||
| 896 | + `user_type` tinyint(4) DEFAULT '0' COMMENT '用户类型', | ||
| 897 | + `send_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT 'å‘é€çжæ€', | ||
| 898 | + `send_time` datetime DEFAULT NULL COMMENT 'å‘逿—¶é—´', | ||
| 899 | + `send_code` int(11) DEFAULT NULL COMMENT 'å‘é€ç»“果的编ç ', | ||
| 900 | + `send_msg` varchar(255) DEFAULT NULL COMMENT 'å‘é€ç»“果的æç¤º', | ||
| 901 | + `api_send_code` varchar(63) DEFAULT NULL COMMENT 'çŸä¿¡ API å‘é€ç»“果的编ç ', | ||
| 902 | + `api_send_msg` varchar(255) DEFAULT NULL COMMENT 'çŸä¿¡ API å‘é€å¤±è´¥çš„æç¤º', | ||
| 903 | + `api_request_id` varchar(255) DEFAULT NULL COMMENT 'çŸä¿¡ API å‘é€è¿”回的唯一请求 ID', | ||
| 904 | + `api_serial_no` varchar(255) DEFAULT NULL COMMENT 'çŸä¿¡ API å‘é€è¿”回的åºå·', | ||
| 905 | + `receive_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '接收状æ€', | ||
| 906 | + `receive_time` datetime DEFAULT NULL COMMENT '接收时间', | ||
| 907 | + `api_receive_code` varchar(63) DEFAULT NULL COMMENT 'API 接收结果的编ç ', | ||
| 908 | + `api_receive_msg` varchar(255) DEFAULT NULL COMMENT 'API 接收结果的说明', | ||
| 909 | + `creator` varchar(64) DEFAULT '' COMMENT '创建者', | ||
| 910 | + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | ||
| 911 | + `updater` varchar(64) DEFAULT '' COMMENT '更新者', | ||
| 912 | + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', | ||
| 913 | + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', | ||
| 914 | + PRIMARY KEY (`id`) USING BTREE | ||
| 915 | +) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8mb4 COMMENT='çŸä¿¡moan'; | ||
| 916 | + | ||
| 917 | +-- ---------------------------- | ||
| 918 | +-- Records of sys_sms_log | ||
| 919 | +-- ---------------------------- | ||
| 920 | +BEGIN; | ||
| 921 | +INSERT INTO `sys_sms_log` VALUES (15, 1, 'YUN_PIAN', 2, 'test_01', 1, 'æ£åœ¨è¿›è¡Œç™»å½•æ“作登陆,您的验è¯ç 是1234', '{\"code\":\"1234\",\"operation\":\"登陆\"}', '4383920', '15601691399', 1, 2, 10, '2021-04-04 23:24:13', 0, 'æˆåŠŸ', '0', 'å‘逿ˆåŠŸ', NULL, '62922707786', 10, '2021-04-04 23:24:26', 'DELIVRD', 'DELIVRD', NULL, '2021-04-04 23:23:29', NULL, '2021-04-04 23:25:17', b'0'); | ||
| 922 | +INSERT INTO `sys_sms_log` VALUES (16, 2, 'ALIYUN', 3, 'test_02', 1, '您的验è¯ç 1234,该验è¯ç 5分钟内有效,请勿泄æ¼äºŽä»–人ï¼', '{\"code\":\"1234\"}', 'SMS_207945135', '15601691399', 1, 2, 20, '2021-04-05 00:08:39', 999, '未知错误,需è¦è§£æž', 'SDK.InvalidAccessKeySecret', 'SDK.InvalidAccessKeySecret : Specified Access Key Secret is not valid.\r\nRequestId : 2EA9C6A5-579F-4D21-B7D3-0AD3BA4F7741', '2EA9C6A5-579F-4D21-B7D3-0AD3BA4F7741', NULL, 0, NULL, NULL, NULL, NULL, '2021-04-05 00:08:39', NULL, '2021-04-05 00:08:39', b'0'); | ||
| 923 | +INSERT INTO `sys_sms_log` VALUES (17, 2, 'ALIYUN', 3, 'test_02', 1, '您的验è¯ç 1234,该验è¯ç 5分钟内有效,请勿泄æ¼äºŽä»–人ï¼', '{\"code\":\"1234\"}', 'SMS_207945135', '15601691399', 1, 2, 20, '2021-04-05 00:09:43', 999, '未知错误,需è¦è§£æž', 'SDK.InvalidAccessKeySecret', 'SDK.InvalidAccessKeySecret : Specified Access Key Secret is not valid.\r\nRequestId : BF766164-9C03-44FD-B6D3-ADA74118E432', 'BF766164-9C03-44FD-B6D3-ADA74118E432', NULL, 0, NULL, NULL, NULL, NULL, '2021-04-05 00:09:43', NULL, '2021-04-05 00:09:43', b'0'); | ||
| 924 | +INSERT INTO `sys_sms_log` VALUES (18, 2, 'ALIYUN', 3, 'test_02', 1, '您的验è¯ç 1234,该验è¯ç 5分钟内有效,请勿泄æ¼äºŽä»–人ï¼', '{\"code\":\"1234\"}', 'SMS_207945135', '15601691399', 1, 2, 20, '2021-04-05 00:11:13', 999, '未知错误,需è¦è§£æž', 'SDK.InvalidAccessKeySecret', 'SDK.InvalidAccessKeySecret : Specified Access Key Secret is not valid.\r\nRequestId : 2D7C0ABC-7538-45B4-BFEF-B610D591CE3D', '2D7C0ABC-7538-45B4-BFEF-B610D591CE3D', NULL, 0, NULL, NULL, NULL, NULL, '2021-04-05 00:11:12', NULL, '2021-04-05 00:11:13', b'0'); | ||
| 925 | +INSERT INTO `sys_sms_log` VALUES (19, 2, 'ALIYUN', 3, 'test_02', 1, '您的验è¯ç 1234,该验è¯ç 5分钟内有效,请勿泄æ¼äºŽä»–人ï¼', '{\"code\":\"1234\"}', 'SMS_207945135', '15601691399', 1, 2, 20, '2021-04-05 00:12:21', 999, '未知错误,需è¦è§£æž', 'SDK.InvalidAccessKeySecret', 'SDK.InvalidAccessKeySecret : Specified Access Key Secret is not valid.\r\nRequestId : 0A86DC5C-2985-474F-B076-748C9F2C5D3F', '0A86DC5C-2985-474F-B076-748C9F2C5D3F', NULL, 0, NULL, NULL, NULL, NULL, '2021-04-05 00:12:01', NULL, '2021-04-05 00:12:21', b'0'); | ||
| 926 | +INSERT INTO `sys_sms_log` VALUES (20, 1, 'YUN_PIAN', 2, 'test_01', 1, 'æ£åœ¨è¿›è¡Œç™»å½•æ“作登陆,您的验è¯ç 是1234', '{\"code\":\"1234\",\"operation\":\"登陆\"}', '4383920', '15601691399', 1, 2, 10, '2021-04-05 00:14:36', 0, 'æˆåŠŸ', '0', 'å‘逿ˆåŠŸ', NULL, '62923244790', 0, NULL, NULL, NULL, NULL, '2021-04-05 00:13:42', NULL, '2021-04-05 00:14:36', b'0'); | ||
| 927 | +INSERT INTO `sys_sms_log` VALUES (21, 2, 'ALIYUN', 3, 'test_02', 1, '您的验è¯ç 1234,该验è¯ç 5分钟内有效,请勿泄æ¼äºŽä»–人ï¼', '{\"code\":\"1234\"}', 'SMS_207945135', '15601691399', 1, 2, 20, '2021-04-05 00:19:43', 999, '未知错误,需è¦è§£æž', 'SDK.InvalidAccessKeySecret', 'SDK.InvalidAccessKeySecret : Specified Access Key Secret is not valid.\r\nRequestId : 3837C6D3-B96F-428C-BBB2-86135D4B5B99', '3837C6D3-B96F-428C-BBB2-86135D4B5B99', NULL, 0, NULL, NULL, NULL, NULL, '2021-04-05 00:15:06', NULL, '2021-04-05 00:19:44', b'0'); | ||
| 928 | +COMMIT; | ||
| 929 | + | ||
| 930 | +-- ---------------------------- | ||
| 931 | +-- Table structure for sys_sms_template | ||
| 932 | +-- ---------------------------- | ||
| 933 | +DROP TABLE IF EXISTS `sys_sms_template`; | ||
| 934 | +CREATE TABLE `sys_sms_template` ( | ||
| 935 | + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ç¼–å·', | ||
| 936 | + `type` tinyint(4) NOT NULL COMMENT 'çŸä¿¡ç¾å', | ||
| 937 | + `status` tinyint(4) NOT NULL COMMENT 'å¼€å¯çжæ€', | ||
| 938 | + `code` varchar(63) NOT NULL COMMENT '模æ¿ç¼–ç ', | ||
| 939 | + `name` varchar(63) NOT NULL COMMENT '模æ¿åç§°', | ||
| 940 | + `content` varchar(255) NOT NULL COMMENT '模æ¿å†…容', | ||
| 941 | + `params` varchar(255) NOT NULL COMMENT '傿•°æ•°ç»„', | ||
| 942 | + `remark` varchar(255) DEFAULT NULL COMMENT '备注', | ||
| 943 | + `api_template_id` varchar(63) NOT NULL COMMENT 'çŸä¿¡ API 的模æ¿ç¼–å·', | ||
| 944 | + `channel_id` bigint(20) NOT NULL COMMENT 'çŸä¿¡æ¸ é“ç¼–å·', | ||
| 945 | + `channel_code` varchar(63) NOT NULL COMMENT 'çŸä¿¡æ¸ é“ç¼–ç ', | ||
| 946 | + `creator` varchar(64) DEFAULT '' COMMENT '创建者', | ||
| 947 | + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | ||
| 948 | + `updater` varchar(64) DEFAULT '' COMMENT '更新者', | ||
| 949 | + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', | ||
| 950 | + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', | ||
| 951 | + PRIMARY KEY (`id`) USING BTREE | ||
| 952 | +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COMMENT='çŸä¿¡æ¨¡æ¿'; | ||
| 953 | + | ||
| 954 | +-- ---------------------------- | ||
| 955 | +-- Records of sys_sms_template | ||
| 956 | +-- ---------------------------- | ||
| 957 | +BEGIN; | ||
| 958 | +INSERT INTO `sys_sms_template` VALUES (2, 1, 0, 'test_01', '测试验è¯ç çŸä¿¡', 'æ£åœ¨è¿›è¡Œç™»å½•æ“作{operation},您的验è¯ç 是{code}', '[\"operation\",\"code\"]', NULL, '4383920', 1, 'YUN_PIAN', '', '2021-03-31 10:49:38', '', '2021-03-31 12:01:38', b'0'); | ||
| 959 | +INSERT INTO `sys_sms_template` VALUES (3, 1, 0, 'test_02', '公告通知', '您的验è¯ç {code},该验è¯ç 5分钟内有效,请勿泄æ¼äºŽä»–人ï¼', '[\"code\"]', NULL, 'SMS_207945135', 2, 'ALIYUN', '', '2021-03-31 11:56:30', '', '2021-03-31 11:56:30', b'0'); | ||
| 960 | +COMMIT; | ||
| 961 | + | ||
| 962 | +-- ---------------------------- | ||
| 838 | -- Table structure for sys_user | 963 | -- Table structure for sys_user |
| 839 | -- ---------------------------- | 964 | -- ---------------------------- |
| 840 | DROP TABLE IF EXISTS `sys_user`; | 965 | DROP TABLE IF EXISTS `sys_user`; |
| @@ -865,8 +990,8 @@ CREATE TABLE `sys_user` ( | @@ -865,8 +990,8 @@ CREATE TABLE `sys_user` ( | ||
| 865 | -- Records of sys_user | 990 | -- Records of sys_user |
| 866 | -- ---------------------------- | 991 | -- ---------------------------- |
| 867 | BEGIN; | 992 | BEGIN; |
| 868 | -INSERT INTO `sys_user` VALUES (1, 'admin', '$2a$10$0acJOIk2D25/oC87nyclE..0lzeu9DtQ/n3geP4fkun/zIVRhHJIO', '芋é“', '管ç†å‘˜', 103, '[1]', 'aoteman@126.com', '15612345678', 1, 'http://127.0.0.1:8080/api/system/file/get/add5ec1891a7d97d2cc1d60847e16294.jpg', 0, '127.0.0.1', '2021-01-05 17:03:47', 'admin', '2021-01-05 17:03:47', '1', '2021-03-21 18:16:16', b'0'); | ||
| 869 | -INSERT INTO `sys_user` VALUES (2, 'ry', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', 'è‹¥ä¾', '测试员', 105, '[2]', 'ry@qq.com', '15666666666', 1, '', 0, '127.0.0.1', '2021-01-05 17:03:47', 'admin', '2021-01-05 17:03:47', '', '2021-01-05 17:03:47', b'0'); | 993 | +INSERT INTO `sys_user` VALUES (1, 'admin', '$2a$10$0acJOIk2D25/oC87nyclE..0lzeu9DtQ/n3geP4fkun/zIVRhHJIO', 'èŠ‹é“æºç ', '管ç†å‘˜', 103, '[1]', 'aoteman@126.com', '15612345678', 1, 'http://api-dashboard.yudao.iocoder.cn/api/infra/file/get/5e8609290e915c4fa8b08e67.jpg', 0, '127.0.0.1', '2021-01-05 17:03:47', 'admin', '2021-01-05 17:03:47', '1', '2021-04-05 02:16:10', b'0'); |
| 994 | +INSERT INTO `sys_user` VALUES (2, 'ry', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', 'è‹¥ä¾', '测试员', 105, '[2]', 'ry@qq.com', '15666666666', 1, '', 0, '127.0.0.1', '2021-01-05 17:03:47', 'admin', '2021-01-05 17:03:47', '', '2021-04-01 04:50:36', b'1'); | ||
| 870 | INSERT INTO `sys_user` VALUES (100, 'yudao', '$2a$10$11U48RhyJ5pSBYWSn12AD./ld671.ycSzJHbyrtpeoMeYiw31eo8a', '芋é“', 'ä¸è¦å“我', 100, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 1, '', NULL, '', '2021-01-07 09:07:17', '1', '2021-03-14 22:35:17', b'0'); | 995 | INSERT INTO `sys_user` VALUES (100, 'yudao', '$2a$10$11U48RhyJ5pSBYWSn12AD./ld671.ycSzJHbyrtpeoMeYiw31eo8a', '芋é“', 'ä¸è¦å“我', 100, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 1, '', NULL, '', '2021-01-07 09:07:17', '1', '2021-03-14 22:35:17', b'0'); |
| 871 | INSERT INTO `sys_user` VALUES (103, 'yuanma', '', 'æºç ', NULL, 100, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '', NULL, '', '2021-01-13 23:50:35', '', '2021-01-13 23:50:35', b'0'); | 996 | INSERT INTO `sys_user` VALUES (103, 'yuanma', '', 'æºç ', NULL, 100, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '', NULL, '', '2021-01-13 23:50:35', '', '2021-01-13 23:50:35', b'0'); |
| 872 | INSERT INTO `sys_user` VALUES (104, 'test', '$2a$10$.TOFpaIiI3PzEwkGrNq0Eu6Cc3rOqJMxTb1DqeSEM8StxaGPBRKoi', '测试å·', NULL, 100, '[]', '', '15601691200', 1, '', 0, '', NULL, '', '2021-01-21 02:13:53', '1', '2021-03-14 22:36:38', b'0'); | 997 | INSERT INTO `sys_user` VALUES (104, 'test', '$2a$10$.TOFpaIiI3PzEwkGrNq0Eu6Cc3rOqJMxTb1DqeSEM8StxaGPBRKoi', '测试å·', NULL, 100, '[]', '', '15601691200', 1, '', 0, '', NULL, '', '2021-01-21 02:13:53', '1', '2021-03-14 22:36:38', b'0'); |
| @@ -924,11 +1049,17 @@ CREATE TABLE `sys_user_session` ( | @@ -924,11 +1049,17 @@ CREATE TABLE `sys_user_session` ( | ||
| 924 | -- Records of sys_user_session | 1049 | -- Records of sys_user_session |
| 925 | -- ---------------------------- | 1050 | -- ---------------------------- |
| 926 | BEGIN; | 1051 | BEGIN; |
| 1052 | +INSERT INTO `sys_user_session` VALUES ('04c6624c7bf14b1ba1a01cb976a9d876', 1, '2021-04-05 21:40:12', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-05 20:21:09', NULL, '2021-04-01 12:25:35', b'1'); | ||
| 1053 | +INSERT INTO `sys_user_session` VALUES ('0e235ce5ae7342a09b372a00bd7d1b41', 1, '2021-04-05 01:43:22', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-05 00:51:03', NULL, '2021-04-01 04:18:06', b'1'); | ||
| 1054 | +INSERT INTO `sys_user_session` VALUES ('0e6943f8ca9b4215a014843eb489ccc7', 1, '2021-04-05 22:53:22', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-05 21:43:59', NULL, '2021-04-05 22:23:22', b'0'); | ||
| 1055 | +INSERT INTO `sys_user_session` VALUES ('40d532d8900c43b791266429a7911751', 1, '2021-04-05 22:11:34', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-05 21:41:34', NULL, '2021-04-01 12:28:20', b'1'); | ||
| 927 | INSERT INTO `sys_user_session` VALUES ('505b4e7d8b0d4b40aa23bf540da81234', 1, '2021-03-14 01:25:13', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-14 00:31:43', NULL, '2021-03-13 07:35:26', b'1'); | 1056 | INSERT INTO `sys_user_session` VALUES ('505b4e7d8b0d4b40aa23bf540da81234', 1, '2021-03-14 01:25:13', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-14 00:31:43', NULL, '2021-03-13 07:35:26', b'1'); |
| 928 | INSERT INTO `sys_user_session` VALUES ('5a7248bf87d14e7e9f0578b05969986c', 1, '2021-03-13 10:42:50', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-13 09:37:36', NULL, '2021-03-12 19:53:07', b'1'); | 1057 | INSERT INTO `sys_user_session` VALUES ('5a7248bf87d14e7e9f0578b05969986c', 1, '2021-03-13 10:42:50', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-13 09:37:36', NULL, '2021-03-12 19:53:07', b'1'); |
| 1058 | +INSERT INTO `sys_user_session` VALUES ('8b3eac5e4a104a4191c8070e03d553ea', 1, '2021-04-05 02:45:12', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-05 02:15:12', NULL, '2021-04-01 11:05:25', b'1'); | ||
| 929 | INSERT INTO `sys_user_session` VALUES ('9ae27346d8b7491aad1385f51e8aa196', 1, '2021-03-13 14:02:12', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-13 10:43:06', NULL, '2021-03-13 06:40:35', b'1'); | 1059 | INSERT INTO `sys_user_session` VALUES ('9ae27346d8b7491aad1385f51e8aa196', 1, '2021-03-13 14:02:12', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-13 10:43:06', NULL, '2021-03-13 06:40:35', b'1'); |
| 930 | INSERT INTO `sys_user_session` VALUES ('ae9ee7452ee54e4b983d658188c15c4d', 1, '2021-03-14 21:32:57', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-14 20:25:00', NULL, '2021-03-13 15:19:10', b'1'); | 1060 | INSERT INTO `sys_user_session` VALUES ('ae9ee7452ee54e4b983d658188c15c4d', 1, '2021-03-14 21:32:57', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-14 20:25:00', NULL, '2021-03-13 15:19:10', b'1'); |
| 931 | -INSERT INTO `sys_user_session` VALUES ('d0adf48f82914212b947e5ab04d9fb65', 1, '2021-03-21 19:16:28', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-03-21 18:13:37', NULL, '2021-03-21 18:46:28', b'0'); | 1061 | +INSERT INTO `sys_user_session` VALUES ('d0adf48f82914212b947e5ab04d9fb65', 1, '2021-03-21 19:16:28', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-03-21 18:13:37', NULL, '2021-03-15 05:53:20', b'1'); |
| 1062 | +INSERT INTO `sys_user_session` VALUES ('e80c2400724042a2ab73732166cde8fc', 1, '2021-03-21 21:17:12', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-03-21 20:47:12', NULL, '2021-03-15 08:18:56', b'1'); | ||
| 932 | INSERT INTO `sys_user_session` VALUES ('e8872f5192584440a548641b83c877ef', 1, '2021-03-21 18:36:01', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-03-21 17:51:48', NULL, '2021-03-15 03:54:20', b'1'); | 1063 | INSERT INTO `sys_user_session` VALUES ('e8872f5192584440a548641b83c877ef', 1, '2021-03-21 18:36:01', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-03-21 17:51:48', NULL, '2021-03-15 03:54:20', b'1'); |
| 933 | INSERT INTO `sys_user_session` VALUES ('f1ab99b09b5a475795579ff99d60ac78', 1, '2021-03-14 23:04:31', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-14 21:12:44', NULL, '2021-03-15 03:32:38', b'1'); | 1064 | INSERT INTO `sys_user_session` VALUES ('f1ab99b09b5a475795579ff99d60ac78', 1, '2021-03-14 23:04:31', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-14 21:12:44', NULL, '2021-03-15 03:32:38', b'1'); |
| 934 | INSERT INTO `sys_user_session` VALUES ('f853b50d064340a581e9a49bba9411fc', 1, '2021-03-10 01:55:41', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-10 01:11:53', NULL, '2021-03-12 18:37:05', b'1'); | 1065 | INSERT INTO `sys_user_session` VALUES ('f853b50d064340a581e9a49bba9411fc', 1, '2021-03-10 01:55:41', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-10 01:11:53', NULL, '2021-03-12 18:37:05', b'1'); |
| @@ -964,7 +1095,7 @@ CREATE TABLE `tool_codegen_column` ( | @@ -964,7 +1095,7 @@ CREATE TABLE `tool_codegen_column` ( | ||
| 964 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', | 1095 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 965 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', | 1096 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 966 | PRIMARY KEY (`id`) USING BTREE | 1097 | PRIMARY KEY (`id`) USING BTREE |
| 967 | -) ENGINE=InnoDB AUTO_INCREMENT=389 DEFAULT CHARSET=utf8mb4 COMMENT='代ç 生æˆè¡¨å—段定义'; | 1098 | +) ENGINE=InnoDB AUTO_INCREMENT=418 DEFAULT CHARSET=utf8mb4 COMMENT='代ç 生æˆè¡¨å—段定义'; |
| 968 | 1099 | ||
| 969 | -- ---------------------------- | 1100 | -- ---------------------------- |
| 970 | -- Records of tool_codegen_column | 1101 | -- Records of tool_codegen_column |
| @@ -1126,6 +1257,35 @@ INSERT INTO `tool_codegen_column` VALUES (385, 33, 'create_time', 'datetime', 'å | @@ -1126,6 +1257,35 @@ INSERT INTO `tool_codegen_column` VALUES (385, 33, 'create_time', 'datetime', 'å | ||
| 1126 | INSERT INTO `tool_codegen_column` VALUES (386, 33, 'updater', 'varchar(64)', '更新者', b'1', b'0', '0', 6, 'String', 'updater', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0'); | 1257 | INSERT INTO `tool_codegen_column` VALUES (386, 33, 'updater', 'varchar(64)', '更新者', b'1', b'0', '0', 6, 'String', 'updater', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0'); |
| 1127 | INSERT INTO `tool_codegen_column` VALUES (387, 33, 'update_time', 'datetime', 'æ›´æ–°æ—¶é—´', b'0', b'0', '0', 7, 'Date', 'updateTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'0', 'datetime', '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0'); | 1258 | INSERT INTO `tool_codegen_column` VALUES (387, 33, 'update_time', 'datetime', 'æ›´æ–°æ—¶é—´', b'0', b'0', '0', 7, 'Date', 'updateTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'0', 'datetime', '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0'); |
| 1128 | INSERT INTO `tool_codegen_column` VALUES (388, 33, 'deleted', 'bit(1)', '是å¦åˆ 除', b'0', b'0', '0', 8, 'Boolean', 'deleted', '', NULL, b'0', b'0', b'0', '=', b'0', 'radio', '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0'); | 1259 | INSERT INTO `tool_codegen_column` VALUES (388, 33, 'deleted', 'bit(1)', '是å¦åˆ 除', b'0', b'0', '0', 8, 'Boolean', 'deleted', '', NULL, b'0', b'0', b'0', '=', b'0', 'radio', '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0'); |
| 1260 | +INSERT INTO `tool_codegen_column` VALUES (389, 34, 'id', 'bigint(20)', 'ç¼–å·', b'0', b'1', '1', 1, 'Long', 'id', '', '1024', b'0', b'1', b'0', '=', b'1', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | ||
| 1261 | +INSERT INTO `tool_codegen_column` VALUES (390, 34, 'signature', 'varchar(8)', 'çŸä¿¡ç¾å', b'0', b'0', '0', 2, 'String', 'signature', '', 'èŠ‹é“æºç ', b'1', b'1', b'1', '=', b'1', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | ||
| 1262 | +INSERT INTO `tool_codegen_column` VALUES (391, 34, 'code', 'varchar(63)', 'æ¸ é“ç¼–ç ', b'0', b'0', '0', 3, 'String', 'code', 'sys_sms_channel_code', 'YUN_PIAN', b'1', b'0', b'0', '=', b'1', 'select', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | ||
| 1263 | +INSERT INTO `tool_codegen_column` VALUES (392, 34, 'status', 'tinyint(4)', 'å¯ç”¨çжæ€', b'0', b'0', '0', 4, 'Integer', 'status', 'sys_common_status', '1', b'1', b'1', b'1', '=', b'1', 'radio', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | ||
| 1264 | +INSERT INTO `tool_codegen_column` VALUES (393, 34, 'remark', 'varchar(255)', '备注', b'1', b'0', '0', 5, 'String', 'remark', '', '好åƒï¼', b'1', b'1', b'0', '=', b'1', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | ||
| 1265 | +INSERT INTO `tool_codegen_column` VALUES (394, 34, 'api_key', 'varchar(63)', 'çŸä¿¡ API 的账å·', b'0', b'0', '0', 6, 'String', 'apiKey', '', 'yudao', b'1', b'1', b'0', '=', b'1', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | ||
| 1266 | +INSERT INTO `tool_codegen_column` VALUES (395, 34, 'api_secret', 'varchar(63)', 'çŸä¿¡ API 的秘钥', b'1', b'0', '0', 7, 'String', 'apiSecret', '', 'yuanma', b'1', b'1', b'0', '=', b'1', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | ||
| 1267 | +INSERT INTO `tool_codegen_column` VALUES (396, 34, 'callback_url', 'varchar(255)', 'çŸä¿¡å‘é€å›žè°ƒ URL', b'1', b'0', '0', 8, 'String', 'callbackUrl', '', 'http://www.iocoder.cn', b'1', b'1', b'0', '=', b'1', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | ||
| 1268 | +INSERT INTO `tool_codegen_column` VALUES (397, 34, 'creator', 'varchar(64)', '创建者', b'1', b'0', '0', 9, 'String', 'creator', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | ||
| 1269 | +INSERT INTO `tool_codegen_column` VALUES (398, 34, 'create_time', 'datetime', '创建时间', b'0', b'0', '0', 10, 'Date', 'createTime', '', NULL, b'0', b'0', b'1', 'BETWEEN', b'1', 'datetime', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | ||
| 1270 | +INSERT INTO `tool_codegen_column` VALUES (399, 34, 'updater', 'varchar(64)', '更新者', b'1', b'0', '0', 11, 'String', 'updater', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | ||
| 1271 | +INSERT INTO `tool_codegen_column` VALUES (400, 34, 'update_time', 'datetime', 'æ›´æ–°æ—¶é—´', b'0', b'0', '0', 12, 'Date', 'updateTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'0', 'datetime', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | ||
| 1272 | +INSERT INTO `tool_codegen_column` VALUES (401, 34, 'deleted', 'bit(1)', '是å¦åˆ 除', b'0', b'0', '0', 13, 'Boolean', 'deleted', '', NULL, b'0', b'0', b'0', '=', b'0', 'radio', '1', '2021-04-05 00:51:34', '1', '2021-04-05 20:52:09', b'0'); | ||
| 1273 | +INSERT INTO `tool_codegen_column` VALUES (402, 35, 'id', 'bigint(20)', 'ç¼–å·', b'0', b'1', '1', 1, 'Long', 'id', '', '1024', b'0', b'1', b'0', '=', b'1', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | ||
| 1274 | +INSERT INTO `tool_codegen_column` VALUES (403, 35, 'type', 'tinyint(4)', 'çŸä¿¡ç¾å', b'0', b'0', '0', 2, 'Integer', 'type', 'sys_sms_template_type', '1', b'1', b'1', b'1', '=', b'1', 'select', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | ||
| 1275 | +INSERT INTO `tool_codegen_column` VALUES (404, 35, 'status', 'tinyint(4)', 'å¼€å¯çжæ€', b'0', b'0', '0', 3, 'Integer', 'status', 'sys_common_status', '1', b'1', b'1', b'1', '=', b'1', 'radio', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | ||
| 1276 | +INSERT INTO `tool_codegen_column` VALUES (405, 35, 'code', 'varchar(63)', '模æ¿ç¼–ç ', b'0', b'0', '0', 4, 'String', 'code', '', 'test_01', b'1', b'1', b'1', 'LIKE', b'1', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | ||
| 1277 | +INSERT INTO `tool_codegen_column` VALUES (406, 35, 'name', 'varchar(63)', '模æ¿åç§°', b'0', b'0', '0', 5, 'String', 'name', '', 'yudao', b'1', b'1', b'0', 'LIKE', b'1', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | ||
| 1278 | +INSERT INTO `tool_codegen_column` VALUES (407, 35, 'content', 'varchar(255)', '模æ¿å†…容', b'0', b'0', '0', 6, 'String', 'content', '', 'ä½ å¥½ï¼Œ{name}ã€‚ä½ é•¿çš„å¤ª{like}啦ï¼', b'1', b'1', b'1', 'LIKE', b'1', 'editor', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | ||
| 1279 | +INSERT INTO `tool_codegen_column` VALUES (408, 35, 'params', 'varchar(255)', '傿•°æ•°ç»„', b'0', b'0', '0', 7, 'String', 'params', '', 'name,code', b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | ||
| 1280 | +INSERT INTO `tool_codegen_column` VALUES (409, 35, 'remark', 'varchar(255)', '备注', b'1', b'0', '0', 8, 'String', 'remark', '', '哈哈哈', b'1', b'1', b'0', '=', b'1', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | ||
| 1281 | +INSERT INTO `tool_codegen_column` VALUES (410, 35, 'api_template_id', 'varchar(63)', 'çŸä¿¡ API 的模æ¿ç¼–å·', b'0', b'0', '0', 9, 'String', 'apiTemplateId', '', '4383920', b'1', b'1', b'1', 'LIKE', b'1', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | ||
| 1282 | +INSERT INTO `tool_codegen_column` VALUES (411, 35, 'channel_id', 'bigint(20)', 'çŸä¿¡æ¸ é“ç¼–å·', b'0', b'0', '0', 10, 'Long', 'channelId', '', '10', b'1', b'1', b'1', '=', b'1', 'select', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | ||
| 1283 | +INSERT INTO `tool_codegen_column` VALUES (412, 35, 'channel_code', 'varchar(63)', 'çŸä¿¡æ¸ é“ç¼–ç ', b'0', b'0', '0', 11, 'String', 'channelCode', 'sys_sms_channel_code', 'ALIYUN', b'0', b'0', b'0', '=', b'1', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | ||
| 1284 | +INSERT INTO `tool_codegen_column` VALUES (413, 35, 'creator', 'varchar(64)', '创建者', b'1', b'0', '0', 12, 'String', 'creator', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | ||
| 1285 | +INSERT INTO `tool_codegen_column` VALUES (414, 35, 'create_time', 'datetime', '创建时间', b'0', b'0', '0', 13, 'Date', 'createTime', '', NULL, b'0', b'0', b'1', 'BETWEEN', b'1', 'datetime', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | ||
| 1286 | +INSERT INTO `tool_codegen_column` VALUES (415, 35, 'updater', 'varchar(64)', '更新者', b'1', b'0', '0', 14, 'String', 'updater', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | ||
| 1287 | +INSERT INTO `tool_codegen_column` VALUES (416, 35, 'update_time', 'datetime', 'æ›´æ–°æ—¶é—´', b'0', b'0', '0', 15, 'Date', 'updateTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'0', 'datetime', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | ||
| 1288 | +INSERT INTO `tool_codegen_column` VALUES (417, 35, 'deleted', 'bit(1)', '是å¦åˆ 除', b'0', b'0', '0', 16, 'Boolean', 'deleted', '', NULL, b'0', b'0', b'0', '=', b'0', 'radio', '1', '2021-04-05 21:42:22', '1', '2021-04-05 22:23:38', b'0'); | ||
| 1129 | COMMIT; | 1289 | COMMIT; |
| 1130 | 1290 | ||
| 1131 | -- ---------------------------- | 1291 | -- ---------------------------- |
| @@ -1151,7 +1311,7 @@ CREATE TABLE `tool_codegen_table` ( | @@ -1151,7 +1311,7 @@ CREATE TABLE `tool_codegen_table` ( | ||
| 1151 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', | 1311 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', |
| 1152 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', | 1312 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ 除', |
| 1153 | PRIMARY KEY (`id`) USING BTREE | 1313 | PRIMARY KEY (`id`) USING BTREE |
| 1154 | -) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8mb4 COMMENT='代ç 生æˆè¡¨å®šä¹‰'; | 1314 | +) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8mb4 COMMENT='代ç 生æˆè¡¨å®šä¹‰'; |
| 1155 | 1315 | ||
| 1156 | -- ---------------------------- | 1316 | -- ---------------------------- |
| 1157 | -- Records of tool_codegen_table | 1317 | -- Records of tool_codegen_table |
| @@ -1169,6 +1329,8 @@ INSERT INTO `tool_codegen_table` VALUES (28, 1, 'sys_dict_type', 'å—å…¸ç±»åž‹è¡ | @@ -1169,6 +1329,8 @@ INSERT INTO `tool_codegen_table` VALUES (28, 1, 'sys_dict_type', 'å—å…¸ç±»åž‹è¡ | ||
| 1169 | INSERT INTO `tool_codegen_table` VALUES (29, 1, 'sys_dict_type', 'å—典类型表', NULL, 'system', 'dict', 'SysDictType', 'å—典类型', '芋艿', 1, NULL, '', '2021-03-06 03:52:57', '', '2021-03-06 04:03:52', b'0'); | 1329 | INSERT INTO `tool_codegen_table` VALUES (29, 1, 'sys_dict_type', 'å—典类型表', NULL, 'system', 'dict', 'SysDictType', 'å—典类型', '芋艿', 1, NULL, '', '2021-03-06 03:52:57', '', '2021-03-06 04:03:52', b'0'); |
| 1170 | INSERT INTO `tool_codegen_table` VALUES (30, 1, 'sys_dict_data', 'å—典数æ®è¡¨', NULL, 'system', 'type', 'SysDictData', 'å—典数æ®', 'èŠ‹é“æºç ', 1, NULL, '', '2021-03-06 06:48:28', '', '2021-03-06 06:50:47', b'0'); | 1330 | INSERT INTO `tool_codegen_table` VALUES (30, 1, 'sys_dict_data', 'å—典数æ®è¡¨', NULL, 'system', 'type', 'SysDictData', 'å—典数æ®', 'èŠ‹é“æºç ', 1, NULL, '', '2021-03-06 06:48:28', '', '2021-03-06 06:50:47', b'0'); |
| 1171 | INSERT INTO `tool_codegen_table` VALUES (33, 1, 'inf_file', '文件表', NULL, 'infra', 'file', 'InfFile', '文件', '芋艿', 1, 2, '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0'); | 1331 | INSERT INTO `tool_codegen_table` VALUES (33, 1, 'inf_file', '文件表', NULL, 'infra', 'file', 'InfFile', '文件', '芋艿', 1, 2, '1', '2021-03-13 09:43:20', '1', '2021-03-13 11:27:12', b'0'); |
| 1332 | +INSERT INTO `tool_codegen_table` VALUES (34, 1, 'sys_sms_channel', 'çŸä¿¡æ¸ é“', NULL, 'system', 'sms', 'SysSmsChannel', 'çŸä¿¡æ¸ é“', 'èŠ‹é“æºç ', 1, 1093, '1', '2021-04-03 13:39:06', '1', '2021-04-05 20:52:09', b'0'); | ||
| 1333 | +INSERT INTO `tool_codegen_table` VALUES (35, 1, 'sys_sms_template', 'çŸä¿¡æ¨¡æ¿', NULL, 'system', 'sms', 'SysSmsTemplate', 'çŸä¿¡æ¨¡æ¿', 'èŠ‹é“æºç ', 1, 1093, '1', '2021-04-03 13:58:55', '1', '2021-04-05 22:23:38', b'0'); | ||
| 1172 | COMMIT; | 1334 | COMMIT; |
| 1173 | 1335 | ||
| 1174 | -- ---------------------------- | 1336 | -- ---------------------------- |
src/main/java/cn/iocoder/dashboard/common/core/KeyValue.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.common.core; | ||
| 2 | + | ||
| 3 | +import lombok.AllArgsConstructor; | ||
| 4 | +import lombok.Data; | ||
| 5 | +import lombok.NoArgsConstructor; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * Key Value 的键值对 | ||
| 9 | + * | ||
| 10 | + * @author 芋道源码 | ||
| 11 | + */ | ||
| 12 | +@Data | ||
| 13 | +@NoArgsConstructor | ||
| 14 | +@AllArgsConstructor | ||
| 15 | +public class KeyValue<K, V> { | ||
| 16 | + | ||
| 17 | + private K key; | ||
| 18 | + private V value; | ||
| 19 | + | ||
| 20 | +} |
src/main/java/cn/iocoder/dashboard/common/enums/DefaultBitFieldEnum.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.common.enums; | ||
| 2 | + | ||
| 3 | +import lombok.AllArgsConstructor; | ||
| 4 | +import lombok.Getter; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 通用状态枚举 | ||
| 8 | + * | ||
| 9 | + * @author 芋道源码 | ||
| 10 | + */ | ||
| 11 | +@Getter | ||
| 12 | +@AllArgsConstructor | ||
| 13 | +public enum DefaultBitFieldEnum { | ||
| 14 | + | ||
| 15 | + NO(0, "否"), | ||
| 16 | + YES(1, "是"); | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * 状态值 | ||
| 20 | + */ | ||
| 21 | + private final Integer val; | ||
| 22 | + /** | ||
| 23 | + * 状态名 | ||
| 24 | + */ | ||
| 25 | + private final String name; | ||
| 26 | + | ||
| 27 | +} |
src/main/java/cn/iocoder/dashboard/common/exception/ErrorCode.java
| 1 | package cn.iocoder.dashboard.common.exception; | 1 | package cn.iocoder.dashboard.common.exception; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; | ||
| 3 | import cn.iocoder.dashboard.common.exception.enums.ServiceErrorCodeRange; | 4 | import cn.iocoder.dashboard.common.exception.enums.ServiceErrorCodeRange; |
| 4 | import lombok.Data; | 5 | import lombok.Data; |
| 5 | 6 | ||
| 6 | /** | 7 | /** |
| 7 | * 错误码对象 | 8 | * 错误码对象 |
| 8 | * | 9 | * |
| 9 | - * 全局错误码,占用 [0, 999],参见 {@link GlobalException} | 10 | + * 全局错误码,占用 [0, 999], 参见 {@link GlobalErrorCodeConstants} |
| 10 | * 业务异常错误码,占用 [1 000 000 000, +∞),参见 {@link ServiceErrorCodeRange} | 11 | * 业务异常错误码,占用 [1 000 000 000, +∞),参见 {@link ServiceErrorCodeRange} |
| 11 | * | 12 | * |
| 12 | * TODO 错误码设计成对象的原因,为未来的 i18 国际化做准备 | 13 | * TODO 错误码设计成对象的原因,为未来的 i18 国际化做准备 |
| @@ -21,11 +22,11 @@ public class ErrorCode { | @@ -21,11 +22,11 @@ public class ErrorCode { | ||
| 21 | /** | 22 | /** |
| 22 | * 错误提示 | 23 | * 错误提示 |
| 23 | */ | 24 | */ |
| 24 | - private final String message; | 25 | + private final String msg; |
| 25 | 26 | ||
| 26 | public ErrorCode(Integer code, String message) { | 27 | public ErrorCode(Integer code, String message) { |
| 27 | this.code = code; | 28 | this.code = code; |
| 28 | - this.message = message; | 29 | + this.msg = message; |
| 29 | } | 30 | } |
| 30 | 31 | ||
| 31 | } | 32 | } |
src/main/java/cn/iocoder/dashboard/common/exception/GlobalException.java deleted
| 1 | -package cn.iocoder.dashboard.common.exception; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; | ||
| 4 | -import lombok.Data; | ||
| 5 | -import lombok.EqualsAndHashCode; | ||
| 6 | - | ||
| 7 | -/** | ||
| 8 | - * 全局异常 Exception | ||
| 9 | - */ | ||
| 10 | -@Data | ||
| 11 | -@EqualsAndHashCode(callSuper = true) | ||
| 12 | -public class GlobalException extends RuntimeException { | ||
| 13 | - | ||
| 14 | - /** | ||
| 15 | - * 全局错误码 | ||
| 16 | - * | ||
| 17 | - * @see GlobalErrorCodeConstants | ||
| 18 | - */ | ||
| 19 | - private Integer code; | ||
| 20 | - /** | ||
| 21 | - * 错误提示 | ||
| 22 | - */ | ||
| 23 | - private String message; | ||
| 24 | - | ||
| 25 | - /** | ||
| 26 | - * 空构造方法,避免反序列化问题 | ||
| 27 | - */ | ||
| 28 | - public GlobalException() { | ||
| 29 | - } | ||
| 30 | - | ||
| 31 | - public GlobalException(ErrorCode errorCode) { | ||
| 32 | - this.code = errorCode.getCode(); | ||
| 33 | - this.message = errorCode.getMessage(); | ||
| 34 | - } | ||
| 35 | - | ||
| 36 | - public GlobalException(Integer code, String message) { | ||
| 37 | - this.code = code; | ||
| 38 | - this.message = message; | ||
| 39 | - } | ||
| 40 | - | ||
| 41 | -} |
src/main/java/cn/iocoder/dashboard/common/exception/ServiceException.java
| @@ -30,7 +30,7 @@ public final class ServiceException extends RuntimeException { | @@ -30,7 +30,7 @@ public final class ServiceException extends RuntimeException { | ||
| 30 | 30 | ||
| 31 | public ServiceException(ErrorCode errorCode) { | 31 | public ServiceException(ErrorCode errorCode) { |
| 32 | this.code = errorCode.getCode(); | 32 | this.code = errorCode.getCode(); |
| 33 | - this.message = errorCode.getMessage(); | 33 | + this.message = errorCode.getMsg(); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | public ServiceException(Integer code, String message) { | 36 | public ServiceException(Integer code, String message) { |
src/main/java/cn/iocoder/dashboard/common/exception/util/ServiceExceptionUtil.java
| @@ -47,12 +47,12 @@ public class ServiceExceptionUtil { | @@ -47,12 +47,12 @@ public class ServiceExceptionUtil { | ||
| 47 | // ========== 和 ServiceException 的集成 ========== | 47 | // ========== 和 ServiceException 的集成 ========== |
| 48 | 48 | ||
| 49 | public static ServiceException exception(ErrorCode errorCode) { | 49 | public static ServiceException exception(ErrorCode errorCode) { |
| 50 | - String messagePattern = MESSAGES.getOrDefault(errorCode.getCode(), errorCode.getMessage()); | 50 | + String messagePattern = MESSAGES.getOrDefault(errorCode.getCode(), errorCode.getMsg()); |
| 51 | return exception0(errorCode.getCode(), messagePattern); | 51 | return exception0(errorCode.getCode(), messagePattern); |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | public static ServiceException exception(ErrorCode errorCode, Object... params) { | 54 | public static ServiceException exception(ErrorCode errorCode, Object... params) { |
| 55 | - String messagePattern = MESSAGES.getOrDefault(errorCode.getCode(), errorCode.getMessage()); | 55 | + String messagePattern = MESSAGES.getOrDefault(errorCode.getCode(), errorCode.getMsg()); |
| 56 | return exception0(errorCode.getCode(), messagePattern, params); | 56 | return exception0(errorCode.getCode(), messagePattern, params); |
| 57 | } | 57 | } |
| 58 | 58 |
src/main/java/cn/iocoder/dashboard/common/pojo/CommonResult.java
| 1 | package cn.iocoder.dashboard.common.pojo; | 1 | package cn.iocoder.dashboard.common.pojo; |
| 2 | 2 | ||
| 3 | import cn.iocoder.dashboard.common.exception.ErrorCode; | 3 | import cn.iocoder.dashboard.common.exception.ErrorCode; |
| 4 | -import cn.iocoder.dashboard.common.exception.GlobalException; | ||
| 5 | import cn.iocoder.dashboard.common.exception.ServiceException; | 4 | import cn.iocoder.dashboard.common.exception.ServiceException; |
| 6 | import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; | 5 | import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; |
| 7 | import com.fasterxml.jackson.annotation.JsonIgnore; | 6 | import com.fasterxml.jackson.annotation.JsonIgnore; |
| @@ -9,6 +8,7 @@ import lombok.Data; | @@ -9,6 +8,7 @@ import lombok.Data; | ||
| 9 | import org.springframework.util.Assert; | 8 | import org.springframework.util.Assert; |
| 10 | 9 | ||
| 11 | import java.io.Serializable; | 10 | import java.io.Serializable; |
| 11 | +import java.util.Objects; | ||
| 12 | 12 | ||
| 13 | /** | 13 | /** |
| 14 | * 通用返回 | 14 | * 通用返回 |
| @@ -16,7 +16,7 @@ import java.io.Serializable; | @@ -16,7 +16,7 @@ import java.io.Serializable; | ||
| 16 | * @param <T> 数据泛型 | 16 | * @param <T> 数据泛型 |
| 17 | */ | 17 | */ |
| 18 | @Data | 18 | @Data |
| 19 | -public final class CommonResult<T> implements Serializable { | 19 | +public class CommonResult<T> implements Serializable { |
| 20 | 20 | ||
| 21 | /** | 21 | /** |
| 22 | * 错误码 | 22 | * 错误码 |
| @@ -31,7 +31,7 @@ public final class CommonResult<T> implements Serializable { | @@ -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 | private String msg; | 36 | private String msg; |
| 37 | 37 | ||
| @@ -57,7 +57,7 @@ public final class CommonResult<T> implements Serializable { | @@ -57,7 +57,7 @@ public final class CommonResult<T> implements Serializable { | ||
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | public static <T> CommonResult<T> error(ErrorCode errorCode) { | 59 | public static <T> CommonResult<T> error(ErrorCode errorCode) { |
| 60 | - return error(errorCode.getCode(), errorCode.getMessage()); | 60 | + return error(errorCode.getCode(), errorCode.getMsg()); |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | public static <T> CommonResult<T> success(T data) { | 63 | public static <T> CommonResult<T> success(T data) { |
| @@ -68,9 +68,13 @@ public final class CommonResult<T> implements Serializable { | @@ -68,9 +68,13 @@ public final class CommonResult<T> implements Serializable { | ||
| 68 | return result; | 68 | return result; |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | + public static boolean isSuccess(Integer code) { | ||
| 72 | + return Objects.equals(code, GlobalErrorCodeConstants.SUCCESS.getCode()); | ||
| 73 | + } | ||
| 74 | + | ||
| 71 | @JsonIgnore // 避免 jackson 序列化 | 75 | @JsonIgnore // 避免 jackson 序列化 |
| 72 | public boolean isSuccess() { | 76 | public boolean isSuccess() { |
| 73 | - return GlobalErrorCodeConstants.SUCCESS.getCode().equals(code); | 77 | + return isSuccess(code); |
| 74 | } | 78 | } |
| 75 | 79 | ||
| 76 | @JsonIgnore // 避免 jackson 序列化 | 80 | @JsonIgnore // 避免 jackson 序列化 |
| @@ -81,16 +85,12 @@ public final class CommonResult<T> implements Serializable { | @@ -81,16 +85,12 @@ public final class CommonResult<T> implements Serializable { | ||
| 81 | // ========= 和 Exception 异常体系集成 ========= | 85 | // ========= 和 Exception 异常体系集成 ========= |
| 82 | 86 | ||
| 83 | /** | 87 | /** |
| 84 | - * 判断是否有异常。如果有,则抛出 {@link GlobalException} 或 {@link ServiceException} 异常 | 88 | + * 判断是否有异常。如果有,则抛出 {@link ServiceException} 异常 |
| 85 | */ | 89 | */ |
| 86 | - public void checkError() throws GlobalException, ServiceException { | 90 | + public void checkError() throws ServiceException { |
| 87 | if (isSuccess()) { | 91 | if (isSuccess()) { |
| 88 | return; | 92 | return; |
| 89 | } | 93 | } |
| 90 | - // 全局异常 | ||
| 91 | - if (GlobalErrorCodeConstants.isMatch(code)) { | ||
| 92 | - throw new GlobalException(code, msg); | ||
| 93 | - } | ||
| 94 | // 业务异常 | 94 | // 业务异常 |
| 95 | throw new ServiceException(code, msg); | 95 | throw new ServiceException(code, msg); |
| 96 | } | 96 | } |
| @@ -99,8 +99,4 @@ public final class CommonResult<T> implements Serializable { | @@ -99,8 +99,4 @@ public final class CommonResult<T> implements Serializable { | ||
| 99 | return error(serviceException.getCode(), serviceException.getMessage()); | 99 | return error(serviceException.getCode(), serviceException.getMessage()); |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | - public static <T> CommonResult<T> error(GlobalException globalException) { | ||
| 103 | - return error(globalException.getCode(), globalException.getMessage()); | ||
| 104 | - } | ||
| 105 | - | ||
| 106 | } | 102 | } |
src/main/java/cn/iocoder/dashboard/framework/excel/core/convert/DictConvert.java
| @@ -13,6 +13,11 @@ import com.alibaba.excel.metadata.GlobalConfiguration; | @@ -13,6 +13,11 @@ import com.alibaba.excel.metadata.GlobalConfiguration; | ||
| 13 | import com.alibaba.excel.metadata.property.ExcelContentProperty; | 13 | import com.alibaba.excel.metadata.property.ExcelContentProperty; |
| 14 | import lombok.extern.slf4j.Slf4j; | 14 | import lombok.extern.slf4j.Slf4j; |
| 15 | 15 | ||
| 16 | +/** | ||
| 17 | + * Excel {@link SysDictDataDO} 数据字典转换器 | ||
| 18 | + * | ||
| 19 | + * @author 芋道源码 | ||
| 20 | + */ | ||
| 16 | @Slf4j | 21 | @Slf4j |
| 17 | public class DictConvert implements Converter<Object> { | 22 | public class DictConvert implements Converter<Object> { |
| 18 | 23 |
src/main/java/cn/iocoder/dashboard/framework/excel/core/convert/JsonConvert.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.excel.core.convert; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.util.json.JsonUtils; | ||
| 4 | +import com.alibaba.excel.converters.Converter; | ||
| 5 | +import com.alibaba.excel.enums.CellDataTypeEnum; | ||
| 6 | +import com.alibaba.excel.metadata.CellData; | ||
| 7 | +import com.alibaba.excel.metadata.GlobalConfiguration; | ||
| 8 | +import com.alibaba.excel.metadata.property.ExcelContentProperty; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * Excel Json 转换器 | ||
| 12 | + * | ||
| 13 | + * @author 芋道源码 | ||
| 14 | + */ | ||
| 15 | +public class JsonConvert implements Converter<Object> { | ||
| 16 | + | ||
| 17 | + @Override | ||
| 18 | + public Class<?> supportJavaTypeKey() { | ||
| 19 | + throw new UnsupportedOperationException("暂不支持,也不需要"); | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + @Override | ||
| 23 | + public CellDataTypeEnum supportExcelTypeKey() { | ||
| 24 | + throw new UnsupportedOperationException("暂不支持,也不需要"); | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + @Override | ||
| 28 | + public Object convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { | ||
| 29 | + throw new UnsupportedOperationException("暂不支持,也不需要"); | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + @Override | ||
| 33 | + public CellData<String> convertToExcelData(Object value, ExcelContentProperty contentProperty, | ||
| 34 | + GlobalConfiguration globalConfiguration) { | ||
| 35 | + // 生成 Excel 小表格 | ||
| 36 | + return new CellData<>(JsonUtils.toJsonString(value)); | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | +} |
src/main/java/cn/iocoder/dashboard/framework/redis/config/RedisConfig.java
| @@ -48,8 +48,8 @@ public class RedisConfig { | @@ -48,8 +48,8 @@ public class RedisConfig { | ||
| 48 | * 创建 Redis Pub/Sub 广播消费的容器 | 48 | * 创建 Redis Pub/Sub 广播消费的容器 |
| 49 | */ | 49 | */ |
| 50 | @Bean | 50 | @Bean |
| 51 | - public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory factory, | ||
| 52 | - List<AbstractChannelMessageListener<?>> listeners) { | 51 | + public RedisMessageListenerContainer redisMessageListenerContainer( |
| 52 | + RedisConnectionFactory factory, List<AbstractChannelMessageListener<?>> listeners) { | ||
| 53 | // 创建 RedisMessageListenerContainer 对象 | 53 | // 创建 RedisMessageListenerContainer 对象 |
| 54 | RedisMessageListenerContainer container = new RedisMessageListenerContainer(); | 54 | RedisMessageListenerContainer container = new RedisMessageListenerContainer(); |
| 55 | // 设置 RedisConnection 工厂。 | 55 | // 设置 RedisConnection 工厂。 |
| @@ -69,8 +69,8 @@ public class RedisConfig { | @@ -69,8 +69,8 @@ public class RedisConfig { | ||
| 69 | * Redis Stream 的 xreadgroup 命令:https://www.geek-book.com/src/docs/redis/redis/redis.io/commands/xreadgroup.html | 69 | * Redis Stream 的 xreadgroup 命令:https://www.geek-book.com/src/docs/redis/redis/redis.io/commands/xreadgroup.html |
| 70 | */ | 70 | */ |
| 71 | @Bean(initMethod = "start", destroyMethod = "stop") | 71 | @Bean(initMethod = "start", destroyMethod = "stop") |
| 72 | - public StreamMessageListenerContainer<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer(RedisTemplate<String, Object> redisTemplate, | ||
| 73 | - List<AbstractStreamMessageListener<?>> listeners) { | 72 | + public StreamMessageListenerContainer<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer( |
| 73 | + RedisTemplate<String, Object> redisTemplate, List<AbstractStreamMessageListener<?>> listeners) { | ||
| 74 | // 第一步,创建 StreamMessageListenerContainer 容器 | 74 | // 第一步,创建 StreamMessageListenerContainer 容器 |
| 75 | // 创建 options 配置 | 75 | // 创建 options 配置 |
| 76 | StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, String>> containerOptions = | 76 | StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, String>> containerOptions = |
src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityConfiguration.java
| @@ -128,13 +128,13 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { | @@ -128,13 +128,13 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { | ||
| 128 | // 设置每个请求的权限 | 128 | // 设置每个请求的权限 |
| 129 | .authorizeRequests() | 129 | .authorizeRequests() |
| 130 | // 登陆的接口,可匿名访问 | 130 | // 登陆的接口,可匿名访问 |
| 131 | - .antMatchers(webProperties.getApiPrefix() + "/login").anonymous() | 131 | + .antMatchers(api("/login")).anonymous() |
| 132 | // 通用的接口,可匿名访问 | 132 | // 通用的接口,可匿名访问 |
| 133 | - .antMatchers( webProperties.getApiPrefix() + "/system/captcha/**").anonymous() | 133 | + .antMatchers(api("/system/captcha/**")).anonymous() |
| 134 | // 静态资源,可匿名访问 | 134 | // 静态资源,可匿名访问 |
| 135 | .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll() | 135 | .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll() |
| 136 | // 文件的获取接口,可匿名访问 | 136 | // 文件的获取接口,可匿名访问 |
| 137 | - .antMatchers(webProperties.getApiPrefix() + "/infra/file/get/**").anonymous() | 137 | + .antMatchers(api("/infra/file/get/**")).anonymous() |
| 138 | // Swagger 接口文档 | 138 | // Swagger 接口文档 |
| 139 | .antMatchers("/swagger-ui.html").anonymous() | 139 | .antMatchers("/swagger-ui.html").anonymous() |
| 140 | .antMatchers("/swagger-resources/**").anonymous() | 140 | .antMatchers("/swagger-resources/**").anonymous() |
| @@ -148,13 +148,19 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { | @@ -148,13 +148,19 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { | ||
| 148 | .antMatchers("/actuator/**").anonymous() | 148 | .antMatchers("/actuator/**").anonymous() |
| 149 | // Druid 监控 | 149 | // Druid 监控 |
| 150 | .antMatchers("/druid/**").anonymous() | 150 | .antMatchers("/druid/**").anonymous() |
| 151 | + // 短信回调 API | ||
| 152 | + .antMatchers(api("/system/sms/callback/**")).anonymous() | ||
| 151 | // 除上面外的所有请求全部需要鉴权认证 | 153 | // 除上面外的所有请求全部需要鉴权认证 |
| 152 | .anyRequest().authenticated() | 154 | .anyRequest().authenticated() |
| 153 | .and() | 155 | .and() |
| 154 | .headers().frameOptions().disable(); | 156 | .headers().frameOptions().disable(); |
| 155 | - httpSecurity.logout().logoutUrl(webProperties.getApiPrefix() + "/logout").logoutSuccessHandler(logoutSuccessHandler); | 157 | + httpSecurity.logout().logoutUrl(api("/logout")).logoutSuccessHandler(logoutSuccessHandler); |
| 156 | // 添加 JWT Filter | 158 | // 添加 JWT Filter |
| 157 | httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); | 159 | httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); |
| 158 | } | 160 | } |
| 159 | 161 | ||
| 162 | + private String api(String url) { | ||
| 163 | + return webProperties.getApiPrefix() + url; | ||
| 164 | + } | ||
| 165 | + | ||
| 160 | } | 166 | } |
src/main/java/cn/iocoder/dashboard/framework/sms/config/SmsConfiguration.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.config; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory; | ||
| 4 | +import cn.iocoder.dashboard.framework.sms.core.client.impl.SmsClientFactoryImpl; | ||
| 5 | +import org.springframework.context.annotation.Bean; | ||
| 6 | +import org.springframework.context.annotation.Configuration; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 短信配置类 | ||
| 10 | + * | ||
| 11 | + * @author 芋道源码 | ||
| 12 | + */ | ||
| 13 | +@Configuration | ||
| 14 | +public class SmsConfiguration { | ||
| 15 | + | ||
| 16 | + @Bean | ||
| 17 | + public SmsClientFactory smsClientFactory() { | ||
| 18 | + return new SmsClientFactoryImpl(); | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | +} |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClient.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.common.core.KeyValue; | ||
| 4 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsReceiveRespDTO; | ||
| 5 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; | ||
| 6 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsTemplateRespDTO; | ||
| 7 | + | ||
| 8 | +import java.util.List; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * 短信客户端接口 | ||
| 12 | + * | ||
| 13 | + * @author zzf | ||
| 14 | + * @date 2021/1/25 14:14 | ||
| 15 | + */ | ||
| 16 | +public interface SmsClient { | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * 获得渠道编号 | ||
| 20 | + * | ||
| 21 | + * @return 渠道编号 | ||
| 22 | + */ | ||
| 23 | + Long getId(); | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 发送消息 | ||
| 27 | + * | ||
| 28 | + * @param logId 日志编号 | ||
| 29 | + * @param mobile 手机号 | ||
| 30 | + * @param apiTemplateId 短信 API 的模板编号 | ||
| 31 | + * @param templateParams 短信模板参数。通过 List 数组,保证参数的顺序 | ||
| 32 | + * @return 短信发送结果 | ||
| 33 | + */ | ||
| 34 | + SmsCommonResult<SmsSendRespDTO> sendSms(Long logId, String mobile, String apiTemplateId, | ||
| 35 | + List<KeyValue<String, Object>> templateParams); | ||
| 36 | + | ||
| 37 | + /** | ||
| 38 | + * 解析接收短信的接收结果 | ||
| 39 | + * | ||
| 40 | + * @param text 结果 | ||
| 41 | + * @return 结果内容 | ||
| 42 | + * @throws Throwable 当解析 text 发生异常时,则会抛出异常 | ||
| 43 | + */ | ||
| 44 | + List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) throws Throwable; | ||
| 45 | + | ||
| 46 | + /** | ||
| 47 | + * 查询指定的短信模板 | ||
| 48 | + * | ||
| 49 | + * @param apiTemplateId 短信 API 的模板编号 | ||
| 50 | + * @return 短信模板 | ||
| 51 | + */ | ||
| 52 | + SmsCommonResult<SmsTemplateRespDTO> getSmsTemplate(String apiTemplateId); | ||
| 53 | + | ||
| 54 | +} |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClientFactory.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * 短信客户端工厂接口 | ||
| 7 | + * | ||
| 8 | + * @author zzf | ||
| 9 | + * @date 2021/1/28 14:01 | ||
| 10 | + */ | ||
| 11 | +public interface SmsClientFactory { | ||
| 12 | + | ||
| 13 | + /** | ||
| 14 | + * 获得短信 Client | ||
| 15 | + * | ||
| 16 | + * @param channelId 渠道编号 | ||
| 17 | + * @return 短信 Client | ||
| 18 | + */ | ||
| 19 | + SmsClient getSmsClient(Long channelId); | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 获得短信 Client | ||
| 23 | + * | ||
| 24 | + * @param channelCode 渠道编码 | ||
| 25 | + * @return 短信 Client | ||
| 26 | + */ | ||
| 27 | + SmsClient getSmsClient(String channelCode); | ||
| 28 | + | ||
| 29 | + /** | ||
| 30 | + * 创建短信 Client | ||
| 31 | + * | ||
| 32 | + * @param properties 配置对象 | ||
| 33 | + */ | ||
| 34 | + void createOrUpdateSmsClient(SmsChannelProperties properties); | ||
| 35 | + | ||
| 36 | +} |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsCodeMapping.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.common.exception.ErrorCode; | ||
| 4 | +import cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants; | ||
| 5 | + | ||
| 6 | +import java.util.function.Function; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 将 API 的错误码,转换为通用的错误码 | ||
| 10 | + * | ||
| 11 | + * @see SmsCommonResult | ||
| 12 | + * @see SmsFrameworkErrorCodeConstants | ||
| 13 | + * | ||
| 14 | + * @author 芋道源码 | ||
| 15 | + */ | ||
| 16 | +public interface SmsCodeMapping extends Function<String, ErrorCode> { | ||
| 17 | +} |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsCommonResult.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.exceptions.ExceptionUtil; | ||
| 4 | +import cn.hutool.core.lang.Assert; | ||
| 5 | +import cn.iocoder.dashboard.common.exception.ErrorCode; | ||
| 6 | +import cn.iocoder.dashboard.common.pojo.CommonResult; | ||
| 7 | +import cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants; | ||
| 8 | +import lombok.Data; | ||
| 9 | +import lombok.EqualsAndHashCode; | ||
| 10 | +import lombok.ToString; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 短信的 CommonResult 拓展类 | ||
| 14 | + * | ||
| 15 | + * 考虑到不同的平台,返回的 code 和 msg 是不同的,所以统一额外返回 {@link #apiCode} 和 {@link #apiMsg} 字段 | ||
| 16 | + * | ||
| 17 | + * 另外,一些短信平台(例如说阿里云、腾讯云)会返回一个请求编号,用于排查请求失败的问题,我们设置到 {@link #apiRequestId} 字段 | ||
| 18 | + * | ||
| 19 | + * @author 芋道源码 | ||
| 20 | + */ | ||
| 21 | +@Data | ||
| 22 | +@EqualsAndHashCode(callSuper = true) | ||
| 23 | +@ToString(callSuper = true) | ||
| 24 | +public class SmsCommonResult<T> extends CommonResult<T> { | ||
| 25 | + | ||
| 26 | + /** | ||
| 27 | + * API 返回错误码 | ||
| 28 | + * | ||
| 29 | + * 由于第三方的错误码可能是字符串,所以使用 String 类型 | ||
| 30 | + */ | ||
| 31 | + private String apiCode; | ||
| 32 | + /** | ||
| 33 | + * API 返回提示 | ||
| 34 | + */ | ||
| 35 | + private String apiMsg; | ||
| 36 | + | ||
| 37 | + /** | ||
| 38 | + * API 请求编号 | ||
| 39 | + */ | ||
| 40 | + private String apiRequestId; | ||
| 41 | + | ||
| 42 | + private SmsCommonResult() { | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + public static <T> SmsCommonResult<T> build(String apiCode, String apiMsg, String apiRequestId, | ||
| 46 | + T data, SmsCodeMapping codeMapping) { | ||
| 47 | + Assert.notNull(codeMapping, "参数 codeMapping 不能为空"); | ||
| 48 | + SmsCommonResult<T> result = new SmsCommonResult<T>().setApiCode(apiCode).setApiMsg(apiMsg).setApiRequestId(apiRequestId); | ||
| 49 | + result.setData(data); | ||
| 50 | + // 翻译错误码 | ||
| 51 | + if (codeMapping != null) { | ||
| 52 | + ErrorCode errorCode = codeMapping.apply(apiCode); | ||
| 53 | + if (errorCode == null) { | ||
| 54 | + errorCode = SmsFrameworkErrorCodeConstants.SMS_UNKNOWN; | ||
| 55 | + } | ||
| 56 | + result.setCode(errorCode.getCode()).setMsg(errorCode.getMsg()); | ||
| 57 | + } | ||
| 58 | + return result; | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + public static <T> SmsCommonResult<T> error(Throwable ex) { | ||
| 62 | + SmsCommonResult<T> result = new SmsCommonResult<>(); | ||
| 63 | + result.setCode(SmsFrameworkErrorCodeConstants.EXCEPTION.getCode()); | ||
| 64 | + result.setMsg(ExceptionUtil.getRootCauseMessage(ex)); | ||
| 65 | + return result; | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | +} |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsReceiveRespDTO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client.dto; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import java.util.Date; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * 消息接收 Response DTO | ||
| 9 | + * | ||
| 10 | + * @author 芋道源码 | ||
| 11 | + */ | ||
| 12 | +@Data | ||
| 13 | +public class SmsReceiveRespDTO { | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * 是否接收成功 | ||
| 17 | + */ | ||
| 18 | + private Boolean success; | ||
| 19 | + /** | ||
| 20 | + * API 接收结果的编码 | ||
| 21 | + */ | ||
| 22 | + private String errorCode; | ||
| 23 | + /** | ||
| 24 | + * API 接收结果的说明 | ||
| 25 | + */ | ||
| 26 | + private String errorMsg; | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 手机号 | ||
| 30 | + */ | ||
| 31 | + private String mobile; | ||
| 32 | + /** | ||
| 33 | + * 用户接收时间 | ||
| 34 | + */ | ||
| 35 | + private Date receiveTime; | ||
| 36 | + | ||
| 37 | + /** | ||
| 38 | + * 短信 API 发送返回的序号 | ||
| 39 | + */ | ||
| 40 | + private String serialNo; | ||
| 41 | + /** | ||
| 42 | + * 短信日志编号 | ||
| 43 | + * | ||
| 44 | + * 对应 SysSmsLogDO 的编号 | ||
| 45 | + */ | ||
| 46 | + private Long logId; | ||
| 47 | + | ||
| 48 | +} |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsSendRespDTO.java
0 → 100644
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/core/handler/GlobalExceptionHandler.java
| @@ -3,7 +3,6 @@ package cn.iocoder.dashboard.framework.web.core.handler; | @@ -3,7 +3,6 @@ package cn.iocoder.dashboard.framework.web.core.handler; | ||
| 3 | import cn.hutool.core.exceptions.ExceptionUtil; | 3 | import cn.hutool.core.exceptions.ExceptionUtil; |
| 4 | import cn.hutool.core.map.MapUtil; | 4 | import cn.hutool.core.map.MapUtil; |
| 5 | import cn.hutool.extra.servlet.ServletUtil; | 5 | import cn.hutool.extra.servlet.ServletUtil; |
| 6 | -import cn.iocoder.dashboard.common.exception.GlobalException; | ||
| 7 | import cn.iocoder.dashboard.common.exception.ServiceException; | 6 | import cn.iocoder.dashboard.common.exception.ServiceException; |
| 8 | import cn.iocoder.dashboard.common.pojo.CommonResult; | 7 | import cn.iocoder.dashboard.common.pojo.CommonResult; |
| 9 | import cn.iocoder.dashboard.framework.logger.apilog.core.service.ApiErrorLogFrameworkService; | 8 | import cn.iocoder.dashboard.framework.logger.apilog.core.service.ApiErrorLogFrameworkService; |
| @@ -96,9 +95,6 @@ public class GlobalExceptionHandler { | @@ -96,9 +95,6 @@ public class GlobalExceptionHandler { | ||
| 96 | if (ex instanceof AccessDeniedException) { | 95 | if (ex instanceof AccessDeniedException) { |
| 97 | return accessDeniedExceptionHandler(request, (AccessDeniedException) ex); | 96 | return accessDeniedExceptionHandler(request, (AccessDeniedException) ex); |
| 98 | } | 97 | } |
| 99 | - if (ex instanceof GlobalException) { | ||
| 100 | - return globalExceptionHandler(request, (GlobalException) ex); | ||
| 101 | - } | ||
| 102 | return defaultExceptionHandler(request, ex); | 98 | return defaultExceptionHandler(request, ex); |
| 103 | } | 99 | } |
| 104 | 100 | ||
| @@ -223,25 +219,6 @@ public class GlobalExceptionHandler { | @@ -223,25 +219,6 @@ public class GlobalExceptionHandler { | ||
| 223 | } | 219 | } |
| 224 | 220 | ||
| 225 | /** | 221 | /** |
| 226 | - * 处理全局异常 ServiceException | ||
| 227 | - * | ||
| 228 | - * 例如说,Dubbo 请求超时,调用的 Dubbo 服务系统异常 | ||
| 229 | - */ | ||
| 230 | - @ExceptionHandler(value = GlobalException.class) | ||
| 231 | - public CommonResult<?> globalExceptionHandler(HttpServletRequest req, GlobalException ex) { | ||
| 232 | - // 系统异常时,才打印异常日志 | ||
| 233 | - if (INTERNAL_SERVER_ERROR.getCode().equals(ex.getCode())) { | ||
| 234 | - // 插入异常日志 | ||
| 235 | - this.createExceptionLog(req, ex); | ||
| 236 | - // 普通全局异常,打印 info 日志即可 | ||
| 237 | - } else { | ||
| 238 | - log.info("[globalExceptionHandler]", ex); | ||
| 239 | - } | ||
| 240 | - // 返回 ERROR CommonResult | ||
| 241 | - return CommonResult.error(ex); | ||
| 242 | - } | ||
| 243 | - | ||
| 244 | - /** | ||
| 245 | * 处理系统异常,兜底处理所有的一切 | 222 | * 处理系统异常,兜底处理所有的一切 |
| 246 | */ | 223 | */ |
| 247 | @ExceptionHandler(value = Exception.class) | 224 | @ExceptionHandler(value = Exception.class) |
| @@ -250,7 +227,7 @@ public class GlobalExceptionHandler { | @@ -250,7 +227,7 @@ public class GlobalExceptionHandler { | ||
| 250 | // 插入异常日志 | 227 | // 插入异常日志 |
| 251 | this.createExceptionLog(req, ex); | 228 | this.createExceptionLog(req, ex); |
| 252 | // 返回 ERROR CommonResult | 229 | // 返回 ERROR CommonResult |
| 253 | - return CommonResult.error(INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMessage()); | 230 | + return CommonResult.error(INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); |
| 254 | } | 231 | } |
| 255 | 232 | ||
| 256 | private void createExceptionLog(HttpServletRequest req, Throwable e) { | 233 | private void createExceptionLog(HttpServletRequest req, Throwable e) { |
src/main/java/cn/iocoder/dashboard/modules/system/controller/dict/SysDictDataController.java
| @@ -61,7 +61,7 @@ public class SysDictDataController { | @@ -61,7 +61,7 @@ public class SysDictDataController { | ||
| 61 | @GetMapping("/list-all-simple") | 61 | @GetMapping("/list-all-simple") |
| 62 | @ApiOperation(value = "获得全部字典数据列表", notes = "一般用于管理后台缓存字典数据在本地") | 62 | @ApiOperation(value = "获得全部字典数据列表", notes = "一般用于管理后台缓存字典数据在本地") |
| 63 | // 无需添加权限认证,因为前端全局都需要 | 63 | // 无需添加权限认证,因为前端全局都需要 |
| 64 | - public CommonResult<List<SysDictDataSimpleVO>> getSimpleDictDatas() { | 64 | + public CommonResult<List<SysDictDataSimpleRespVO>> getSimpleDictDatas() { |
| 65 | List<SysDictDataDO> list = dictDataService.getDictDatas(); | 65 | List<SysDictDataDO> list = dictDataService.getDictDatas(); |
| 66 | return success(SysDictDataConvert.INSTANCE.convertList(list)); | 66 | return success(SysDictDataConvert.INSTANCE.convertList(list)); |
| 67 | } | 67 | } |
src/main/java/cn/iocoder/dashboard/modules/system/controller/dict/SysDictTypeController.java
| @@ -41,7 +41,7 @@ public class SysDictTypeController { | @@ -41,7 +41,7 @@ public class SysDictTypeController { | ||
| 41 | return success(dictTypeId); | 41 | return success(dictTypeId); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | - @PostMapping("update") | 44 | + @PutMapping("/update") |
| 45 | @ApiOperation("修改字典类型") | 45 | @ApiOperation("修改字典类型") |
| 46 | @PreAuthorize("@ss.hasPermission('system:dict:update')") | 46 | @PreAuthorize("@ss.hasPermission('system:dict:update')") |
| 47 | public CommonResult<Boolean> updateDictType(@Valid @RequestBody SysDictTypeUpdateReqVO reqVO) { | 47 | public CommonResult<Boolean> updateDictType(@Valid @RequestBody SysDictTypeUpdateReqVO reqVO) { |
src/main/java/cn/iocoder/dashboard/modules/system/controller/dict/vo/data/SysDictDataSimpleVO.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/controller/dict/vo/data/SysDictDataSimpleRespVO.java
| @@ -4,9 +4,9 @@ import io.swagger.annotations.ApiModel; | @@ -4,9 +4,9 @@ import io.swagger.annotations.ApiModel; | ||
| 4 | import io.swagger.annotations.ApiModelProperty; | 4 | import io.swagger.annotations.ApiModelProperty; |
| 5 | import lombok.Data; | 5 | import lombok.Data; |
| 6 | 6 | ||
| 7 | -@ApiModel("数据字典精简 VO") | 7 | +@ApiModel("数据字典精简 Response VO") |
| 8 | @Data | 8 | @Data |
| 9 | -public class SysDictDataSimpleVO { | 9 | +public class SysDictDataSimpleRespVO { |
| 10 | 10 | ||
| 11 | @ApiModelProperty(value = "字典类型", required = true, example = "gender") | 11 | @ApiModelProperty(value = "字典类型", required = true, example = "gender") |
| 12 | private String dictType; | 12 | private String dictType; |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SmsCallbackController.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.URLUtil; | ||
| 4 | +import cn.hutool.extra.servlet.ServletUtil; | ||
| 5 | +import cn.iocoder.dashboard.common.pojo.CommonResult; | ||
| 6 | +import cn.iocoder.dashboard.framework.logger.operatelog.core.annotations.OperateLog; | ||
| 7 | +import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum; | ||
| 8 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService; | ||
| 9 | +import io.swagger.annotations.Api; | ||
| 10 | +import io.swagger.annotations.ApiImplicitParam; | ||
| 11 | +import io.swagger.annotations.ApiOperation; | ||
| 12 | +import org.springframework.web.bind.annotation.PostMapping; | ||
| 13 | +import org.springframework.web.bind.annotation.RequestMapping; | ||
| 14 | +import org.springframework.web.bind.annotation.RequestParam; | ||
| 15 | +import org.springframework.web.bind.annotation.RestController; | ||
| 16 | + | ||
| 17 | +import javax.annotation.Resource; | ||
| 18 | +import javax.servlet.http.HttpServletRequest; | ||
| 19 | + | ||
| 20 | +import static cn.iocoder.dashboard.common.pojo.CommonResult.success; | ||
| 21 | + | ||
| 22 | +@Api(tags = "短信回调") | ||
| 23 | +@RestController | ||
| 24 | +@RequestMapping("/system/sms/callback") | ||
| 25 | +public class SmsCallbackController { | ||
| 26 | + | ||
| 27 | + @Resource | ||
| 28 | + private SysSmsService smsService; | ||
| 29 | + | ||
| 30 | + @PostMapping("/sms/yunpian") | ||
| 31 | + @ApiOperation(value = "云片短信的回调", notes = "参见 https://www.yunpian.com/official/document/sms/zh_cn/domestic_push_report 文档") | ||
| 32 | + @ApiImplicitParam(name = "sms_status", value = "发送状态", required = true, example = "[{具体内容}]", dataTypeClass = Long.class) | ||
| 33 | + @OperateLog(enable = false) | ||
| 34 | + public String receiveYunpianSmsStatus(@RequestParam("sms_status") String smsStatus) throws Throwable { | ||
| 35 | + String text = URLUtil.decode(smsStatus); // decode 解码参数,因为它被 encode | ||
| 36 | + smsService.receiveSmsStatus(SmsChannelEnum.YUN_PIAN.getCode(), text); | ||
| 37 | + return "SUCCESS"; // 约定返回 SUCCESS 为成功 | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + @PostMapping("/sms/aliyun") | ||
| 41 | + @ApiOperation(value = "阿里云短信的回调", notes = "参见 https://help.aliyun.com/document_detail/120998.html 文档") | ||
| 42 | + @OperateLog(enable = false) | ||
| 43 | + public CommonResult<Boolean> receiveAliyunSmsStatus(HttpServletRequest request) throws Throwable { | ||
| 44 | + String text = ServletUtil.getBody(request); | ||
| 45 | + smsService.receiveSmsStatus(SmsChannelEnum.ALIYUN.getCode(), text); | ||
| 46 | + return success(true); | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SysSmsChannelController.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.common.pojo.CommonResult; | ||
| 4 | +import cn.iocoder.dashboard.common.pojo.PageResult; | ||
| 5 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.*; | ||
| 6 | +import cn.iocoder.dashboard.modules.system.convert.sms.SysSmsChannelConvert; | ||
| 7 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO; | ||
| 8 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService; | ||
| 9 | +import io.swagger.annotations.Api; | ||
| 10 | +import io.swagger.annotations.ApiImplicitParam; | ||
| 11 | +import io.swagger.annotations.ApiOperation; | ||
| 12 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
| 13 | +import org.springframework.web.bind.annotation.*; | ||
| 14 | + | ||
| 15 | +import javax.annotation.Resource; | ||
| 16 | +import javax.validation.Valid; | ||
| 17 | +import java.util.Comparator; | ||
| 18 | +import java.util.List; | ||
| 19 | + | ||
| 20 | +import static cn.iocoder.dashboard.common.pojo.CommonResult.success; | ||
| 21 | + | ||
| 22 | +@Api(tags = "短信渠道") | ||
| 23 | +@RestController | ||
| 24 | +@RequestMapping("system/sms-channel") | ||
| 25 | +public class SysSmsChannelController { | ||
| 26 | + | ||
| 27 | + @Resource | ||
| 28 | + private SysSmsChannelService smsChannelService; | ||
| 29 | + | ||
| 30 | + @PostMapping("/create") | ||
| 31 | + @ApiOperation("创建短信渠道") | ||
| 32 | + @PreAuthorize("@ss.hasPermission('system:sms-channel:create')") | ||
| 33 | + public CommonResult<Long> createSmsChannel(@Valid @RequestBody SysSmsChannelCreateReqVO createReqVO) { | ||
| 34 | + return success(smsChannelService.createSmsChannel(createReqVO)); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + @PutMapping("/update") | ||
| 38 | + @ApiOperation("更新短信渠道") | ||
| 39 | + @PreAuthorize("@ss.hasPermission('system:sms-channel:update')") | ||
| 40 | + public CommonResult<Boolean> updateSmsChannel(@Valid @RequestBody SysSmsChannelUpdateReqVO updateReqVO) { | ||
| 41 | + smsChannelService.updateSmsChannel(updateReqVO); | ||
| 42 | + return success(true); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + @DeleteMapping("/delete") | ||
| 46 | + @ApiOperation("删除短信渠道") | ||
| 47 | + @ApiImplicitParam(name = "id", value = "编号", required = true) | ||
| 48 | + @PreAuthorize("@ss.hasPermission('system:sms-channel:delete')") | ||
| 49 | + public CommonResult<Boolean> deleteSmsChannel(@RequestParam("id") Long id) { | ||
| 50 | + smsChannelService.deleteSmsChannel(id); | ||
| 51 | + return success(true); | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + @GetMapping("/get") | ||
| 55 | + @ApiOperation("获得短信渠道") | ||
| 56 | + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) | ||
| 57 | + @PreAuthorize("@ss.hasPermission('system:sms-channel:query')") | ||
| 58 | + public CommonResult<SysSmsChannelRespVO> getSmsChannel(@RequestParam("id") Long id) { | ||
| 59 | + SysSmsChannelDO smsChannel = smsChannelService.getSmsChannel(id); | ||
| 60 | + return success(SysSmsChannelConvert.INSTANCE.convert(smsChannel)); | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + @GetMapping("/page") | ||
| 64 | + @ApiOperation("获得短信渠道分页") | ||
| 65 | + @PreAuthorize("@ss.hasPermission('system:sms-channel:query')") | ||
| 66 | + public CommonResult<PageResult<SysSmsChannelRespVO>> getSmsChannelPage(@Valid SysSmsChannelPageReqVO pageVO) { | ||
| 67 | + PageResult<SysSmsChannelDO> pageResult = smsChannelService.getSmsChannelPage(pageVO); | ||
| 68 | + return success(SysSmsChannelConvert.INSTANCE.convertPage(pageResult)); | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + @GetMapping("/list-all-simple") | ||
| 72 | + @ApiOperation(value = "获得短信渠道精简列表", notes = "包含被禁用的短信渠道") | ||
| 73 | + public CommonResult<List<SysSmsChannelSimpleRespVO>> getSimpleSmsChannels() { | ||
| 74 | + List<SysSmsChannelDO> list = smsChannelService.getSmsChannelList(); | ||
| 75 | + // 排序后,返回给前端 | ||
| 76 | + list.sort(Comparator.comparing(SysSmsChannelDO::getId)); | ||
| 77 | + return success(SysSmsChannelConvert.INSTANCE.convertList03(list)); | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SysSmsLogController.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.common.pojo.CommonResult; | ||
| 4 | +import cn.iocoder.dashboard.common.pojo.PageResult; | ||
| 5 | +import cn.iocoder.dashboard.framework.excel.core.util.ExcelUtils; | ||
| 6 | +import cn.iocoder.dashboard.framework.logger.operatelog.core.annotations.OperateLog; | ||
| 7 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogExcelVO; | ||
| 8 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogExportReqVO; | ||
| 9 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogPageReqVO; | ||
| 10 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogRespVO; | ||
| 11 | +import cn.iocoder.dashboard.modules.system.convert.sms.SysSmsLogConvert; | ||
| 12 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsLogDO; | ||
| 13 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsLogService; | ||
| 14 | +import io.swagger.annotations.Api; | ||
| 15 | +import io.swagger.annotations.ApiOperation; | ||
| 16 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
| 17 | +import org.springframework.validation.annotation.Validated; | ||
| 18 | +import org.springframework.web.bind.annotation.GetMapping; | ||
| 19 | +import org.springframework.web.bind.annotation.RequestMapping; | ||
| 20 | +import org.springframework.web.bind.annotation.RestController; | ||
| 21 | + | ||
| 22 | +import javax.annotation.Resource; | ||
| 23 | +import javax.servlet.http.HttpServletResponse; | ||
| 24 | +import javax.validation.Valid; | ||
| 25 | +import java.io.IOException; | ||
| 26 | +import java.util.List; | ||
| 27 | + | ||
| 28 | +import static cn.iocoder.dashboard.common.pojo.CommonResult.success; | ||
| 29 | +import static cn.iocoder.dashboard.framework.logger.operatelog.core.enums.OperateTypeEnum.EXPORT; | ||
| 30 | + | ||
| 31 | +@Api(tags = "短信日志") | ||
| 32 | +@RestController | ||
| 33 | +@RequestMapping("/system/sms-log") | ||
| 34 | +@Validated | ||
| 35 | +public class SysSmsLogController { | ||
| 36 | + | ||
| 37 | + @Resource | ||
| 38 | + private SysSmsLogService smsLogService; | ||
| 39 | + | ||
| 40 | + @GetMapping("/page") | ||
| 41 | + @ApiOperation("获得短信日志分页") | ||
| 42 | + @PreAuthorize("@ss.hasPermission('system:sms-log:query')") | ||
| 43 | + public CommonResult<PageResult<SysSmsLogRespVO>> getSmsLogPage(@Valid SysSmsLogPageReqVO pageVO) { | ||
| 44 | + PageResult<SysSmsLogDO> pageResult = smsLogService.getSmsLogPage(pageVO); | ||
| 45 | + return success(SysSmsLogConvert.INSTANCE.convertPage(pageResult)); | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + @GetMapping("/export-excel") | ||
| 49 | + @ApiOperation("导出短信日志 Excel") | ||
| 50 | + @PreAuthorize("@ss.hasPermission('system:sms-log:export')") | ||
| 51 | + @OperateLog(type = EXPORT) | ||
| 52 | + public void exportSmsLogExcel(@Valid SysSmsLogExportReqVO exportReqVO, | ||
| 53 | + HttpServletResponse response) throws IOException { | ||
| 54 | + List<SysSmsLogDO> list = smsLogService.getSmsLogList(exportReqVO); | ||
| 55 | + // 导出 Excel | ||
| 56 | + List<SysSmsLogExcelVO> datas = SysSmsLogConvert.INSTANCE.convertList02(list); | ||
| 57 | + ExcelUtils.write(response, "短信日志.xls", "数据", SysSmsLogExcelVO.class, datas); | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SysSmsTemplateController.http
0 → 100644
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SysSmsTemplateController.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.common.pojo.CommonResult; | ||
| 4 | +import cn.iocoder.dashboard.common.pojo.PageResult; | ||
| 5 | +import cn.iocoder.dashboard.framework.excel.core.util.ExcelUtils; | ||
| 6 | +import cn.iocoder.dashboard.framework.logger.operatelog.core.annotations.OperateLog; | ||
| 7 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.*; | ||
| 8 | +import cn.iocoder.dashboard.modules.system.convert.sms.SysSmsTemplateConvert; | ||
| 9 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; | ||
| 10 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService; | ||
| 11 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsTemplateService; | ||
| 12 | +import io.swagger.annotations.Api; | ||
| 13 | +import io.swagger.annotations.ApiImplicitParam; | ||
| 14 | +import io.swagger.annotations.ApiOperation; | ||
| 15 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
| 16 | +import org.springframework.web.bind.annotation.*; | ||
| 17 | + | ||
| 18 | +import javax.annotation.Resource; | ||
| 19 | +import javax.servlet.http.HttpServletResponse; | ||
| 20 | +import javax.validation.Valid; | ||
| 21 | +import java.io.IOException; | ||
| 22 | +import java.util.List; | ||
| 23 | + | ||
| 24 | +import static cn.iocoder.dashboard.common.pojo.CommonResult.success; | ||
| 25 | +import static cn.iocoder.dashboard.framework.logger.operatelog.core.enums.OperateTypeEnum.EXPORT; | ||
| 26 | + | ||
| 27 | +@Api("短信模板") | ||
| 28 | +@RestController | ||
| 29 | +@RequestMapping("/system/sms-template") | ||
| 30 | +public class SysSmsTemplateController { | ||
| 31 | + | ||
| 32 | + @Resource | ||
| 33 | + private SysSmsTemplateService smsTemplateService; | ||
| 34 | + @Resource | ||
| 35 | + private SysSmsService smsService; | ||
| 36 | + | ||
| 37 | + @PostMapping("/create") | ||
| 38 | + @ApiOperation("创建短信模板") | ||
| 39 | + @PreAuthorize("@ss.hasPermission('system:sms-template:create')") | ||
| 40 | + public CommonResult<Long> createSmsTemplate(@Valid @RequestBody SysSmsTemplateCreateReqVO createReqVO) { | ||
| 41 | + return success(smsTemplateService.createSmsTemplate(createReqVO)); | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + @PutMapping("/update") | ||
| 45 | + @ApiOperation("更新短信模板") | ||
| 46 | + @PreAuthorize("@ss.hasPermission('system:sms-template:update')") | ||
| 47 | + public CommonResult<Boolean> updateSmsTemplate(@Valid @RequestBody SysSmsTemplateUpdateReqVO updateReqVO) { | ||
| 48 | + smsTemplateService.updateSmsTemplate(updateReqVO); | ||
| 49 | + return success(true); | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + @DeleteMapping("/delete") | ||
| 53 | + @ApiOperation("删除短信模板") | ||
| 54 | + @ApiImplicitParam(name = "id", value = "编号", required = true) | ||
| 55 | + @PreAuthorize("@ss.hasPermission('system:sms-template:delete')") | ||
| 56 | + public CommonResult<Boolean> deleteSmsTemplate(@RequestParam("id") Long id) { | ||
| 57 | + smsTemplateService.deleteSmsTemplate(id); | ||
| 58 | + return success(true); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + @GetMapping("/get") | ||
| 62 | + @ApiOperation("获得短信模板") | ||
| 63 | + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) | ||
| 64 | + @PreAuthorize("@ss.hasPermission('system:sms-template:query')") | ||
| 65 | + public CommonResult<SysSmsTemplateRespVO> getSmsTemplate(@RequestParam("id") Long id) { | ||
| 66 | + SysSmsTemplateDO smsTemplate = smsTemplateService.getSmsTemplate(id); | ||
| 67 | + return success(SysSmsTemplateConvert.INSTANCE.convert(smsTemplate)); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + @GetMapping("/page") | ||
| 71 | + @ApiOperation("获得短信模板分页") | ||
| 72 | + @PreAuthorize("@ss.hasPermission('system:sms-template:query')") | ||
| 73 | + public CommonResult<PageResult<SysSmsTemplateRespVO>> getSmsTemplatePage(@Valid SysSmsTemplatePageReqVO pageVO) { | ||
| 74 | + PageResult<SysSmsTemplateDO> pageResult = smsTemplateService.getSmsTemplatePage(pageVO); | ||
| 75 | + return success(SysSmsTemplateConvert.INSTANCE.convertPage(pageResult)); | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + @GetMapping("/export-excel") | ||
| 79 | + @ApiOperation("导出短信模板 Excel") | ||
| 80 | + @PreAuthorize("@ss.hasPermission('system:sms-template:export')") | ||
| 81 | + @OperateLog(type = EXPORT) | ||
| 82 | + public void exportSmsTemplateExcel(@Valid SysSmsTemplateExportReqVO exportReqVO, | ||
| 83 | + HttpServletResponse response) throws IOException { | ||
| 84 | + List<SysSmsTemplateDO> list = smsTemplateService.getSmsTemplateList(exportReqVO); | ||
| 85 | + // 导出 Excel | ||
| 86 | + List<SysSmsTemplateExcelVO> datas = SysSmsTemplateConvert.INSTANCE.convertList02(list); | ||
| 87 | + ExcelUtils.write(response, "短信模板.xls", "数据", SysSmsTemplateExcelVO.class, datas); | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + @PostMapping("/send-sms") | ||
| 91 | + @ApiOperation("发送短信") | ||
| 92 | + @PreAuthorize("@ss.hasPermission('system:sms-template:send-sms')") | ||
| 93 | + public CommonResult<Long> sendSms(@Valid @RequestBody SysSmsTemplateSendReqVO sendReqVO) { | ||
| 94 | + return success(smsService.sendSingleSms(sendReqVO.getMobile(), null, null, | ||
| 95 | + sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams())); | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/channel/SysSmsChannelBaseVO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms.vo.channel; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModelProperty; | ||
| 4 | +import lombok.Data; | ||
| 5 | +import org.hibernate.validator.constraints.URL; | ||
| 6 | + | ||
| 7 | +import javax.validation.constraints.NotNull; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | +* 短信渠道 Base VO,提供给添加、修改、详细的子 VO 使用 | ||
| 11 | +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 | ||
| 12 | +*/ | ||
| 13 | +@Data | ||
| 14 | +public class SysSmsChannelBaseVO { | ||
| 15 | + | ||
| 16 | + @ApiModelProperty(value = "短信签名", required = true, example = "芋道源码") | ||
| 17 | + @NotNull(message = "短信签名不能为空") | ||
| 18 | + private String signature; | ||
| 19 | + | ||
| 20 | + @ApiModelProperty(value = "启用状态", required = true, example = "1") | ||
| 21 | + @NotNull(message = "启用状态不能为空") | ||
| 22 | + private Integer status; | ||
| 23 | + | ||
| 24 | + @ApiModelProperty(value = "备注", example = "好吃!") | ||
| 25 | + private String remark; | ||
| 26 | + | ||
| 27 | + @ApiModelProperty(value = "短信 API 的账号", required = true, example = "yudao") | ||
| 28 | + @NotNull(message = "短信 API 的账号不能为空") | ||
| 29 | + private String apiKey; | ||
| 30 | + | ||
| 31 | + @ApiModelProperty(value = "短信 API 的秘钥", example = "yuanma") | ||
| 32 | + private String apiSecret; | ||
| 33 | + | ||
| 34 | + @ApiModelProperty(value = "短信发送回调 URL", example = "http://www.iocoder.cn") | ||
| 35 | + @URL(message = "回调 URL 格式不正确") | ||
| 36 | + private String callbackUrl; | ||
| 37 | + | ||
| 38 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/channel/SysSmsChannelCreateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms.vo.channel; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModel; | ||
| 4 | +import io.swagger.annotations.ApiModelProperty; | ||
| 5 | +import lombok.Data; | ||
| 6 | +import lombok.EqualsAndHashCode; | ||
| 7 | +import lombok.ToString; | ||
| 8 | + | ||
| 9 | +import javax.validation.constraints.NotNull; | ||
| 10 | + | ||
| 11 | +@ApiModel("短信渠道创建 Request VO") | ||
| 12 | +@Data | ||
| 13 | +@EqualsAndHashCode(callSuper = true) | ||
| 14 | +@ToString(callSuper = true) | ||
| 15 | +public class SysSmsChannelCreateReqVO extends SysSmsChannelBaseVO { | ||
| 16 | + | ||
| 17 | + @ApiModelProperty(value = "渠道编码", required = true, example = "YUN_PIAN", notes = "参见 SmsChannelEnum 枚举类") | ||
| 18 | + @NotNull(message = "渠道编码不能为空") | ||
| 19 | + private String code; | ||
| 20 | + | ||
| 21 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/channel/SysSmsChannelPageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms.vo.channel; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.common.pojo.PageParam; | ||
| 4 | +import io.swagger.annotations.ApiModel; | ||
| 5 | +import io.swagger.annotations.ApiModelProperty; | ||
| 6 | +import lombok.Data; | ||
| 7 | +import lombok.EqualsAndHashCode; | ||
| 8 | +import lombok.ToString; | ||
| 9 | +import org.springframework.format.annotation.DateTimeFormat; | ||
| 10 | + | ||
| 11 | +import java.util.Date; | ||
| 12 | + | ||
| 13 | +import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | ||
| 14 | + | ||
| 15 | +@ApiModel("短信渠道分页 Request VO") | ||
| 16 | +@Data | ||
| 17 | +@EqualsAndHashCode(callSuper = true) | ||
| 18 | +@ToString(callSuper = true) | ||
| 19 | +public class SysSmsChannelPageReqVO extends PageParam { | ||
| 20 | + | ||
| 21 | + @ApiModelProperty(value = "任务状态", example = "1") | ||
| 22 | + private Integer status; | ||
| 23 | + | ||
| 24 | + @ApiModelProperty(value = "短信签名", example = "芋道源码", notes = "模糊匹配") | ||
| 25 | + private String signature; | ||
| 26 | + | ||
| 27 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||
| 28 | + @ApiModelProperty(value = "开始创建时间") | ||
| 29 | + private Date beginCreateTime; | ||
| 30 | + | ||
| 31 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||
| 32 | + @ApiModelProperty(value = "结束创建时间") | ||
| 33 | + private Date endCreateTime; | ||
| 34 | + | ||
| 35 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/channel/SysSmsChannelRespVO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms.vo.channel; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModel; | ||
| 4 | +import io.swagger.annotations.ApiModelProperty; | ||
| 5 | +import lombok.Data; | ||
| 6 | +import lombok.EqualsAndHashCode; | ||
| 7 | +import lombok.ToString; | ||
| 8 | + | ||
| 9 | +import java.util.Date; | ||
| 10 | + | ||
| 11 | +@ApiModel("短信渠道 Response VO") | ||
| 12 | +@Data | ||
| 13 | +@EqualsAndHashCode(callSuper = true) | ||
| 14 | +@ToString(callSuper = true) | ||
| 15 | +public class SysSmsChannelRespVO extends SysSmsChannelBaseVO { | ||
| 16 | + | ||
| 17 | + @ApiModelProperty(value = "编号", required = true, example = "1024") | ||
| 18 | + private Long id; | ||
| 19 | + | ||
| 20 | + @ApiModelProperty(value = "渠道编码", required = true, example = "YUN_PIAN", notes = "参见 SmsChannelEnum 枚举类") | ||
| 21 | + private String code; | ||
| 22 | + | ||
| 23 | + @ApiModelProperty(value = "创建时间", required = true) | ||
| 24 | + private Date createTime; | ||
| 25 | + | ||
| 26 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/channel/SysSmsChannelSimpleRespVO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms.vo.channel; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModel; | ||
| 4 | +import io.swagger.annotations.ApiModelProperty; | ||
| 5 | +import lombok.Data; | ||
| 6 | + | ||
| 7 | +import javax.validation.constraints.NotNull; | ||
| 8 | + | ||
| 9 | +@ApiModel("短信渠道精简 Response VO") | ||
| 10 | +@Data | ||
| 11 | +public class SysSmsChannelSimpleRespVO { | ||
| 12 | + | ||
| 13 | + @ApiModelProperty(value = "编号", required = true, example = "1024") | ||
| 14 | + @NotNull(message = "编号不能为空") | ||
| 15 | + private Long id; | ||
| 16 | + | ||
| 17 | + @ApiModelProperty(value = "短信签名", required = true, example = "芋道源码") | ||
| 18 | + @NotNull(message = "短信签名不能为空") | ||
| 19 | + private String signature; | ||
| 20 | + | ||
| 21 | + @ApiModelProperty(value = "渠道编码", required = true, example = "YUN_PIAN", notes = "参见 SmsChannelEnum 枚举类") | ||
| 22 | + private String code; | ||
| 23 | + | ||
| 24 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/channel/SysSmsChannelUpdateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms.vo.channel; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModel; | ||
| 4 | +import io.swagger.annotations.ApiModelProperty; | ||
| 5 | +import lombok.Data; | ||
| 6 | +import lombok.EqualsAndHashCode; | ||
| 7 | +import lombok.ToString; | ||
| 8 | + | ||
| 9 | +import javax.validation.constraints.NotNull; | ||
| 10 | + | ||
| 11 | +@ApiModel("短信渠道更新 Request VO") | ||
| 12 | +@Data | ||
| 13 | +@EqualsAndHashCode(callSuper = true) | ||
| 14 | +@ToString(callSuper = true) | ||
| 15 | +public class SysSmsChannelUpdateReqVO extends SysSmsChannelBaseVO { | ||
| 16 | + | ||
| 17 | + @ApiModelProperty(value = "编号", required = true, example = "1024") | ||
| 18 | + @NotNull(message = "编号不能为空") | ||
| 19 | + private Long id; | ||
| 20 | + | ||
| 21 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/log/SysSmsLogExcelVO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms.vo.log; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.framework.excel.core.annotations.DictFormat; | ||
| 4 | +import cn.iocoder.dashboard.framework.excel.core.convert.DictConvert; | ||
| 5 | +import cn.iocoder.dashboard.framework.excel.core.convert.JsonConvert; | ||
| 6 | +import com.alibaba.excel.annotation.ExcelProperty; | ||
| 7 | +import lombok.Data; | ||
| 8 | + | ||
| 9 | +import java.util.Date; | ||
| 10 | +import java.util.Map; | ||
| 11 | + | ||
| 12 | +import static cn.iocoder.dashboard.modules.system.enums.dict.SysDictTypeEnum.*; | ||
| 13 | + | ||
| 14 | +/** | ||
| 15 | + * 短信日志 Excel VO | ||
| 16 | + * | ||
| 17 | + * @author 芋道源码 | ||
| 18 | + */ | ||
| 19 | +@Data | ||
| 20 | +public class SysSmsLogExcelVO { | ||
| 21 | + | ||
| 22 | + @ExcelProperty("编号") | ||
| 23 | + private Long id; | ||
| 24 | + | ||
| 25 | + @ExcelProperty("短信渠道编号") | ||
| 26 | + private Long channelId; | ||
| 27 | + | ||
| 28 | + @ExcelProperty("短信渠道编码") | ||
| 29 | + private String channelCode; | ||
| 30 | + | ||
| 31 | + @ExcelProperty("模板编号") | ||
| 32 | + private Long templateId; | ||
| 33 | + | ||
| 34 | + @ExcelProperty("模板编码") | ||
| 35 | + private String templateCode; | ||
| 36 | + | ||
| 37 | + @ExcelProperty(value = "短信类型", converter = DictConvert.class) | ||
| 38 | + @DictFormat(SYS_SMS_TEMPLATE_TYPE) | ||
| 39 | + private Integer templateType; | ||
| 40 | + | ||
| 41 | + @ExcelProperty("短信内容") | ||
| 42 | + private String templateContent; | ||
| 43 | + | ||
| 44 | + @ExcelProperty(value = "短信参数", converter = JsonConvert.class) | ||
| 45 | + private Map<String, Object> templateParams; | ||
| 46 | + | ||
| 47 | + @ExcelProperty("短信 API 的模板编号") | ||
| 48 | + private String apiTemplateId; | ||
| 49 | + | ||
| 50 | + @ExcelProperty("手机号") | ||
| 51 | + private String mobile; | ||
| 52 | + | ||
| 53 | + @ExcelProperty("用户编号") | ||
| 54 | + private Long userId; | ||
| 55 | + | ||
| 56 | + @ExcelProperty(value = "用户类型", converter = DictConvert.class) | ||
| 57 | + @DictFormat(USER_TYPE) | ||
| 58 | + private Integer userType; | ||
| 59 | + | ||
| 60 | + @ExcelProperty(value = "发送状态", converter = DictConvert.class) | ||
| 61 | + @DictFormat(SYS_SMS_SEND_STATUS) | ||
| 62 | + private Integer sendStatus; | ||
| 63 | + | ||
| 64 | + @ExcelProperty("发送时间") | ||
| 65 | + private Date sendTime; | ||
| 66 | + | ||
| 67 | + @ExcelProperty("发送结果的编码") | ||
| 68 | + private Integer sendCode; | ||
| 69 | + | ||
| 70 | + @ExcelProperty("发送结果的提示") | ||
| 71 | + private String sendMsg; | ||
| 72 | + | ||
| 73 | + @ExcelProperty("短信 API 发送结果的编码") | ||
| 74 | + private String apiSendCode; | ||
| 75 | + | ||
| 76 | + @ExcelProperty("短信 API 发送失败的提示") | ||
| 77 | + private String apiSendMsg; | ||
| 78 | + | ||
| 79 | + @ExcelProperty("短信 API 发送返回的唯一请求 ID") | ||
| 80 | + private String apiRequestId; | ||
| 81 | + | ||
| 82 | + @ExcelProperty("短信 API 发送返回的序号") | ||
| 83 | + private String apiSerialNo; | ||
| 84 | + | ||
| 85 | + @ExcelProperty(value = "接收状态", converter = DictConvert.class) | ||
| 86 | + @DictFormat(SYS_SMS_RECEIVE_STATUS) | ||
| 87 | + private Integer receiveStatus; | ||
| 88 | + | ||
| 89 | + @ExcelProperty("接收时间") | ||
| 90 | + private Date receiveTime; | ||
| 91 | + | ||
| 92 | + @ExcelProperty("API 接收结果的编码") | ||
| 93 | + private String apiReceiveCode; | ||
| 94 | + | ||
| 95 | + @ExcelProperty("API 接收结果的说明") | ||
| 96 | + private String apiReceiveMsg; | ||
| 97 | + | ||
| 98 | + @ExcelProperty("创建时间") | ||
| 99 | + private Date createTime; | ||
| 100 | + | ||
| 101 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/log/SysSmsLogExportReqVO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms.vo.log; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModel; | ||
| 4 | +import io.swagger.annotations.ApiModelProperty; | ||
| 5 | +import lombok.Data; | ||
| 6 | +import org.springframework.format.annotation.DateTimeFormat; | ||
| 7 | + | ||
| 8 | +import java.util.Date; | ||
| 9 | + | ||
| 10 | +import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | ||
| 11 | + | ||
| 12 | +@ApiModel(value = "短信日志 Excel 导出 Request VO", description = "参数和 SysSmsLogPageReqVO 是一致的") | ||
| 13 | +@Data | ||
| 14 | +public class SysSmsLogExportReqVO { | ||
| 15 | + | ||
| 16 | + @ApiModelProperty(value = "短信渠道编号", example = "10") | ||
| 17 | + private Long channelId; | ||
| 18 | + | ||
| 19 | + @ApiModelProperty(value = "模板编号", example = "20") | ||
| 20 | + private Long templateId; | ||
| 21 | + | ||
| 22 | + @ApiModelProperty(value = "手机号", example = "15601691300") | ||
| 23 | + private String mobile; | ||
| 24 | + | ||
| 25 | + @ApiModelProperty(value = "发送状态", example = "1") | ||
| 26 | + private Integer sendStatus; | ||
| 27 | + | ||
| 28 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||
| 29 | + @ApiModelProperty(value = "开始发送时间") | ||
| 30 | + private Date beginSendTime; | ||
| 31 | + | ||
| 32 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||
| 33 | + @ApiModelProperty(value = "结束发送时间") | ||
| 34 | + private Date endSendTime; | ||
| 35 | + | ||
| 36 | + @ApiModelProperty(value = "接收状态", example = "0") | ||
| 37 | + private Integer receiveStatus; | ||
| 38 | + | ||
| 39 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||
| 40 | + @ApiModelProperty(value = "开始接收时间") | ||
| 41 | + private Date beginReceiveTime; | ||
| 42 | + | ||
| 43 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||
| 44 | + @ApiModelProperty(value = "结束接收时间") | ||
| 45 | + private Date endReceiveTime; | ||
| 46 | + | ||
| 47 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/log/SysSmsLogPageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms.vo.log; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.common.pojo.PageParam; | ||
| 4 | +import io.swagger.annotations.ApiModel; | ||
| 5 | +import io.swagger.annotations.ApiModelProperty; | ||
| 6 | +import lombok.Data; | ||
| 7 | +import lombok.EqualsAndHashCode; | ||
| 8 | +import lombok.ToString; | ||
| 9 | +import org.springframework.format.annotation.DateTimeFormat; | ||
| 10 | + | ||
| 11 | +import java.util.Date; | ||
| 12 | + | ||
| 13 | +import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | ||
| 14 | + | ||
| 15 | +@ApiModel("短信日志分页 Request VO") | ||
| 16 | +@Data | ||
| 17 | +@EqualsAndHashCode(callSuper = true) | ||
| 18 | +@ToString(callSuper = true) | ||
| 19 | +public class SysSmsLogPageReqVO extends PageParam { | ||
| 20 | + | ||
| 21 | + @ApiModelProperty(value = "短信渠道编号", example = "10") | ||
| 22 | + private Long channelId; | ||
| 23 | + | ||
| 24 | + @ApiModelProperty(value = "模板编号", example = "20") | ||
| 25 | + private Long templateId; | ||
| 26 | + | ||
| 27 | + @ApiModelProperty(value = "手机号", example = "15601691300") | ||
| 28 | + private String mobile; | ||
| 29 | + | ||
| 30 | + @ApiModelProperty(value = "发送状态", example = "1", notes = "参见 SysSmsSendStatusEnum 枚举类") | ||
| 31 | + private Integer sendStatus; | ||
| 32 | + | ||
| 33 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||
| 34 | + @ApiModelProperty(value = "开始发送时间") | ||
| 35 | + private Date beginSendTime; | ||
| 36 | + | ||
| 37 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||
| 38 | + @ApiModelProperty(value = "结束发送时间") | ||
| 39 | + private Date endSendTime; | ||
| 40 | + | ||
| 41 | + @ApiModelProperty(value = "接收状态", example = "0", notes = "参见 SysSmsReceiveStatusEnum 枚举类") | ||
| 42 | + private Integer receiveStatus; | ||
| 43 | + | ||
| 44 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||
| 45 | + @ApiModelProperty(value = "开始接收时间") | ||
| 46 | + private Date beginReceiveTime; | ||
| 47 | + | ||
| 48 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||
| 49 | + @ApiModelProperty(value = "结束接收时间") | ||
| 50 | + private Date endReceiveTime; | ||
| 51 | + | ||
| 52 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/log/SysSmsLogRespVO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms.vo.log; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModel; | ||
| 4 | +import io.swagger.annotations.ApiModelProperty; | ||
| 5 | +import lombok.Data; | ||
| 6 | + | ||
| 7 | +import java.util.Date; | ||
| 8 | +import java.util.Map; | ||
| 9 | + | ||
| 10 | +@ApiModel("短信日志 Response VO") | ||
| 11 | +@Data | ||
| 12 | +public class SysSmsLogRespVO { | ||
| 13 | + | ||
| 14 | + @ApiModelProperty(value = "编号", required = true, example = "1024") | ||
| 15 | + private Long id; | ||
| 16 | + | ||
| 17 | + @ApiModelProperty(value = "短信渠道编号", required = true, example = "10") | ||
| 18 | + private Long channelId; | ||
| 19 | + | ||
| 20 | + @ApiModelProperty(value = "短信渠道编码", required = true, example = "ALIYUN") | ||
| 21 | + private String channelCode; | ||
| 22 | + | ||
| 23 | + @ApiModelProperty(value = "模板编号", required = true, example = "20") | ||
| 24 | + private Long templateId; | ||
| 25 | + | ||
| 26 | + @ApiModelProperty(value = "模板编码", required = true, example = "test-01") | ||
| 27 | + private String templateCode; | ||
| 28 | + | ||
| 29 | + @ApiModelProperty(value = "短信类型", required = true, example = "1") | ||
| 30 | + private Integer templateType; | ||
| 31 | + | ||
| 32 | + @ApiModelProperty(value = "短信内容", required = true, example = "你好,你的验证码是 1024") | ||
| 33 | + private String templateContent; | ||
| 34 | + | ||
| 35 | + @ApiModelProperty(value = "短信参数", required = true, example = "name,code") | ||
| 36 | + private Map<String, Object> templateParams; | ||
| 37 | + | ||
| 38 | + @ApiModelProperty(value = "短信 API 的模板编号", required = true, example = "SMS_207945135") | ||
| 39 | + private String apiTemplateId; | ||
| 40 | + | ||
| 41 | + @ApiModelProperty(value = "手机号", required = true, example = "15601691300") | ||
| 42 | + private String mobile; | ||
| 43 | + | ||
| 44 | + @ApiModelProperty(value = "用户编号", example = "10") | ||
| 45 | + private Long userId; | ||
| 46 | + | ||
| 47 | + @ApiModelProperty(value = "用户类型", example = "1") | ||
| 48 | + private Integer userType; | ||
| 49 | + | ||
| 50 | + @ApiModelProperty(value = "发送状态", required = true, example = "1") | ||
| 51 | + private Integer sendStatus; | ||
| 52 | + | ||
| 53 | + @ApiModelProperty(value = "发送时间") | ||
| 54 | + private Date sendTime; | ||
| 55 | + | ||
| 56 | + @ApiModelProperty(value = "发送结果的编码", example = "0") | ||
| 57 | + private Integer sendCode; | ||
| 58 | + | ||
| 59 | + @ApiModelProperty(value = "发送结果的提示", example = "成功") | ||
| 60 | + private String sendMsg; | ||
| 61 | + | ||
| 62 | + @ApiModelProperty(value = "短信 API 发送结果的编码", example = "SUCCESS") | ||
| 63 | + private String apiSendCode; | ||
| 64 | + | ||
| 65 | + @ApiModelProperty(value = "短信 API 发送失败的提示", example = "成功") | ||
| 66 | + private String apiSendMsg; | ||
| 67 | + | ||
| 68 | + @ApiModelProperty(value = "短信 API 发送返回的唯一请求 ID", example = "3837C6D3-B96F-428C-BBB2-86135D4B5B99") | ||
| 69 | + private String apiRequestId; | ||
| 70 | + | ||
| 71 | + @ApiModelProperty(value = "短信 API 发送返回的序号", example = "62923244790") | ||
| 72 | + private String apiSerialNo; | ||
| 73 | + | ||
| 74 | + @ApiModelProperty(value = "接收状态", required = true, example = "0") | ||
| 75 | + private Integer receiveStatus; | ||
| 76 | + | ||
| 77 | + @ApiModelProperty(value = "接收时间") | ||
| 78 | + private Date receiveTime; | ||
| 79 | + | ||
| 80 | + @ApiModelProperty(value = "API 接收结果的编码", example = "DELIVRD") | ||
| 81 | + private String apiReceiveCode; | ||
| 82 | + | ||
| 83 | + @ApiModelProperty(value = "API 接收结果的说明", example = "用户接收成功") | ||
| 84 | + private String apiReceiveMsg; | ||
| 85 | + | ||
| 86 | + @ApiModelProperty(value = "创建时间", required = true) | ||
| 87 | + private Date createTime; | ||
| 88 | + | ||
| 89 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/template/SysSmsTemplateBaseVO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms.vo.template; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModelProperty; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +import javax.validation.constraints.NotNull; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | +* 短信模板 Base VO,提供给添加、修改、详细的子 VO 使用 | ||
| 10 | +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 | ||
| 11 | +*/ | ||
| 12 | +@Data | ||
| 13 | +public class SysSmsTemplateBaseVO { | ||
| 14 | + | ||
| 15 | + @ApiModelProperty(value = "短信类型", required = true, example = "1", notes = "参见 SysSmsTemplateTypeEnum 枚举类") | ||
| 16 | + @NotNull(message = "短信类型不能为空") | ||
| 17 | + private Integer type; | ||
| 18 | + | ||
| 19 | + @ApiModelProperty(value = "开启状态", required = true, example = "1", notes = "参见 CommonStatusEnum 枚举类") | ||
| 20 | + @NotNull(message = "开启状态不能为空") | ||
| 21 | + private Integer status; | ||
| 22 | + | ||
| 23 | + @ApiModelProperty(value = "模板编码", required = true, example = "test_01") | ||
| 24 | + @NotNull(message = "模板编码不能为空") | ||
| 25 | + private String code; | ||
| 26 | + | ||
| 27 | + @ApiModelProperty(value = "模板名称", required = true, example = "yudao") | ||
| 28 | + @NotNull(message = "模板名称不能为空") | ||
| 29 | + private String name; | ||
| 30 | + | ||
| 31 | + @ApiModelProperty(value = "模板内容", required = true, example = "你好,{name}。你长的太{like}啦!") | ||
| 32 | + @NotNull(message = "模板内容不能为空") | ||
| 33 | + private String content; | ||
| 34 | + | ||
| 35 | + @ApiModelProperty(value = "备注", example = "哈哈哈") | ||
| 36 | + private String remark; | ||
| 37 | + | ||
| 38 | + @ApiModelProperty(value = "短信 API 的模板编号", required = true, example = "4383920") | ||
| 39 | + @NotNull(message = "短信 API 的模板编号不能为空") | ||
| 40 | + private String apiTemplateId; | ||
| 41 | + | ||
| 42 | + @ApiModelProperty(value = "短信渠道编号", required = true, example = "10") | ||
| 43 | + @NotNull(message = "短信渠道编号不能为空") | ||
| 44 | + private Long channelId; | ||
| 45 | + | ||
| 46 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/template/SysSmsTemplateCreateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms.vo.template; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModel; | ||
| 4 | +import lombok.Data; | ||
| 5 | +import lombok.EqualsAndHashCode; | ||
| 6 | +import lombok.ToString; | ||
| 7 | + | ||
| 8 | +@ApiModel("短信模板创建 Request VO") | ||
| 9 | +@Data | ||
| 10 | +@EqualsAndHashCode(callSuper = true) | ||
| 11 | +@ToString(callSuper = true) | ||
| 12 | +public class SysSmsTemplateCreateReqVO extends SysSmsTemplateBaseVO { | ||
| 13 | + | ||
| 14 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/template/SysSmsTemplateExcelVO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms.vo.template; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.framework.excel.core.annotations.DictFormat; | ||
| 4 | +import cn.iocoder.dashboard.framework.excel.core.convert.DictConvert; | ||
| 5 | +import com.alibaba.excel.annotation.ExcelProperty; | ||
| 6 | +import lombok.Data; | ||
| 7 | + | ||
| 8 | +import java.util.Date; | ||
| 9 | + | ||
| 10 | +import static cn.iocoder.dashboard.modules.system.enums.dict.SysDictTypeEnum.*; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 短信模板 Excel VO | ||
| 14 | + * | ||
| 15 | + * @author 芋道源码 | ||
| 16 | + */ | ||
| 17 | +@Data | ||
| 18 | +public class SysSmsTemplateExcelVO { | ||
| 19 | + | ||
| 20 | + @ExcelProperty("编号") | ||
| 21 | + private Long id; | ||
| 22 | + | ||
| 23 | + @ExcelProperty(value = "短信签名", converter = DictConvert.class) | ||
| 24 | + @DictFormat(SYS_SMS_TEMPLATE_TYPE) | ||
| 25 | + private Integer type; | ||
| 26 | + | ||
| 27 | + @ExcelProperty(value = "开启状态", converter = DictConvert.class) | ||
| 28 | + @DictFormat(SYS_COMMON_STATUS) | ||
| 29 | + private Integer status; | ||
| 30 | + | ||
| 31 | + @ExcelProperty("模板编码") | ||
| 32 | + private String code; | ||
| 33 | + | ||
| 34 | + @ExcelProperty("模板名称") | ||
| 35 | + private String name; | ||
| 36 | + | ||
| 37 | + @ExcelProperty("模板内容") | ||
| 38 | + private String content; | ||
| 39 | + | ||
| 40 | + @ExcelProperty("备注") | ||
| 41 | + private String remark; | ||
| 42 | + | ||
| 43 | + @ExcelProperty("短信 API 的模板编号") | ||
| 44 | + private String apiTemplateId; | ||
| 45 | + | ||
| 46 | + @ExcelProperty("短信渠道编号") | ||
| 47 | + private Long channelId; | ||
| 48 | + | ||
| 49 | + @ExcelProperty(value = "短信渠道编码", converter = DictConvert.class) | ||
| 50 | + @DictFormat(SYS_SMS_CHANNEL_CODE) | ||
| 51 | + private String channelCode; | ||
| 52 | + | ||
| 53 | + @ExcelProperty("创建时间") | ||
| 54 | + private Date createTime; | ||
| 55 | + | ||
| 56 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/template/SysSmsTemplateExportReqVO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms.vo.template; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModel; | ||
| 4 | +import io.swagger.annotations.ApiModelProperty; | ||
| 5 | +import lombok.Data; | ||
| 6 | +import org.springframework.format.annotation.DateTimeFormat; | ||
| 7 | + | ||
| 8 | +import java.util.Date; | ||
| 9 | + | ||
| 10 | +import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | ||
| 11 | + | ||
| 12 | +@ApiModel(value = "短信模板 Excel 导出 Request VO", description = "参数和 SysSmsTemplatePageReqVO 是一致的") | ||
| 13 | +@Data | ||
| 14 | +public class SysSmsTemplateExportReqVO { | ||
| 15 | + | ||
| 16 | + @ApiModelProperty(value = "短信签名", example = "1") | ||
| 17 | + private Integer type; | ||
| 18 | + | ||
| 19 | + @ApiModelProperty(value = "开启状态", example = "1") | ||
| 20 | + private Integer status; | ||
| 21 | + | ||
| 22 | + @ApiModelProperty(value = "模板编码", example = "test_01", notes = "模糊匹配") | ||
| 23 | + private String code; | ||
| 24 | + | ||
| 25 | + @ApiModelProperty(value = "模板内容", example = "你好,{name}。你长的太{like}啦!", notes = "模糊匹配") | ||
| 26 | + private String content; | ||
| 27 | + | ||
| 28 | + @ApiModelProperty(value = "短信 API 的模板编号", example = "4383920", notes = "模糊匹配") | ||
| 29 | + private String apiTemplateId; | ||
| 30 | + | ||
| 31 | + @ApiModelProperty(value = "短信渠道编号", example = "10") | ||
| 32 | + private Long channelId; | ||
| 33 | + | ||
| 34 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||
| 35 | + @ApiModelProperty(value = "开始创建时间") | ||
| 36 | + private Date beginCreateTime; | ||
| 37 | + | ||
| 38 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||
| 39 | + @ApiModelProperty(value = "结束创建时间") | ||
| 40 | + private Date endCreateTime; | ||
| 41 | + | ||
| 42 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/template/SysSmsTemplatePageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms.vo.template; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.common.pojo.PageParam; | ||
| 4 | +import io.swagger.annotations.ApiModel; | ||
| 5 | +import io.swagger.annotations.ApiModelProperty; | ||
| 6 | +import lombok.Data; | ||
| 7 | +import lombok.EqualsAndHashCode; | ||
| 8 | +import lombok.ToString; | ||
| 9 | +import org.springframework.format.annotation.DateTimeFormat; | ||
| 10 | + | ||
| 11 | +import java.util.Date; | ||
| 12 | + | ||
| 13 | +import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | ||
| 14 | + | ||
| 15 | +@ApiModel("短信模板分页 Request VO") | ||
| 16 | +@Data | ||
| 17 | +@EqualsAndHashCode(callSuper = true) | ||
| 18 | +@ToString(callSuper = true) | ||
| 19 | +public class SysSmsTemplatePageReqVO extends PageParam { | ||
| 20 | + | ||
| 21 | + @ApiModelProperty(value = "短信签名", example = "1") | ||
| 22 | + private Integer type; | ||
| 23 | + | ||
| 24 | + @ApiModelProperty(value = "开启状态", example = "1") | ||
| 25 | + private Integer status; | ||
| 26 | + | ||
| 27 | + @ApiModelProperty(value = "模板编码", example = "test_01", notes = "模糊匹配") | ||
| 28 | + private String code; | ||
| 29 | + | ||
| 30 | + @ApiModelProperty(value = "模板内容", example = "你好,{name}。你长的太{like}啦!", notes = "模糊匹配") | ||
| 31 | + private String content; | ||
| 32 | + | ||
| 33 | + @ApiModelProperty(value = "短信 API 的模板编号", example = "4383920", notes = "模糊匹配") | ||
| 34 | + private String apiTemplateId; | ||
| 35 | + | ||
| 36 | + @ApiModelProperty(value = "短信渠道编号", example = "10") | ||
| 37 | + private Long channelId; | ||
| 38 | + | ||
| 39 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||
| 40 | + @ApiModelProperty(value = "开始创建时间") | ||
| 41 | + private Date beginCreateTime; | ||
| 42 | + | ||
| 43 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||
| 44 | + @ApiModelProperty(value = "结束创建时间") | ||
| 45 | + private Date endCreateTime; | ||
| 46 | + | ||
| 47 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/template/SysSmsTemplateRespVO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms.vo.template; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModel; | ||
| 4 | +import io.swagger.annotations.ApiModelProperty; | ||
| 5 | +import lombok.Data; | ||
| 6 | +import lombok.EqualsAndHashCode; | ||
| 7 | +import lombok.ToString; | ||
| 8 | + | ||
| 9 | +import java.util.Date; | ||
| 10 | +import java.util.List; | ||
| 11 | + | ||
| 12 | +@ApiModel("短信模板 Response VO") | ||
| 13 | +@Data | ||
| 14 | +@EqualsAndHashCode(callSuper = true) | ||
| 15 | +@ToString(callSuper = true) | ||
| 16 | +public class SysSmsTemplateRespVO extends SysSmsTemplateBaseVO { | ||
| 17 | + | ||
| 18 | + @ApiModelProperty(value = "编号", required = true, example = "1024") | ||
| 19 | + private Long id; | ||
| 20 | + | ||
| 21 | + @ApiModelProperty(value = "短信渠道编码", required = true, example = "ALIYUN") | ||
| 22 | + private String channelCode; | ||
| 23 | + | ||
| 24 | + @ApiModelProperty(value = "参数数组", example = "name,code") | ||
| 25 | + private List<String> params; | ||
| 26 | + | ||
| 27 | + @ApiModelProperty(value = "创建时间", required = true) | ||
| 28 | + private Date createTime; | ||
| 29 | + | ||
| 30 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/template/SysSmsTemplateSendReqVO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms.vo.template; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModel; | ||
| 4 | +import io.swagger.annotations.ApiModelProperty; | ||
| 5 | +import lombok.Data; | ||
| 6 | + | ||
| 7 | +import javax.validation.constraints.NotNull; | ||
| 8 | +import java.util.Map; | ||
| 9 | + | ||
| 10 | +@ApiModel("短信模板的发送 Request VO") | ||
| 11 | +@Data | ||
| 12 | +public class SysSmsTemplateSendReqVO { | ||
| 13 | + | ||
| 14 | + @ApiModelProperty(value = "手机号", required = true, example = "15601691300") | ||
| 15 | + @NotNull(message = "手机号不能为空") | ||
| 16 | + private String mobile; | ||
| 17 | + | ||
| 18 | + @ApiModelProperty(value = "模板编码", required = true, example = "test_01") | ||
| 19 | + @NotNull(message = "模板编码不能为空") | ||
| 20 | + private String templateCode; | ||
| 21 | + | ||
| 22 | + @ApiModelProperty(value = "模板参数") | ||
| 23 | + private Map<String, Object> templateParams; | ||
| 24 | + | ||
| 25 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/template/SysSmsTemplateUpdateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms.vo.template; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModel; | ||
| 4 | +import io.swagger.annotations.ApiModelProperty; | ||
| 5 | +import lombok.Data; | ||
| 6 | +import lombok.EqualsAndHashCode; | ||
| 7 | +import lombok.ToString; | ||
| 8 | + | ||
| 9 | +import javax.validation.constraints.NotNull; | ||
| 10 | + | ||
| 11 | +@ApiModel("短信模板更新 Request VO") | ||
| 12 | +@Data | ||
| 13 | +@EqualsAndHashCode(callSuper = true) | ||
| 14 | +@ToString(callSuper = true) | ||
| 15 | +public class SysSmsTemplateUpdateReqVO extends SysSmsTemplateBaseVO { | ||
| 16 | + | ||
| 17 | + @ApiModelProperty(value = "编号", required = true, example = "1024") | ||
| 18 | + @NotNull(message = "编号不能为空") | ||
| 19 | + private Long id; | ||
| 20 | + | ||
| 21 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/convert/dict/SysDictDataConvert.java
| @@ -13,7 +13,7 @@ public interface SysDictDataConvert { | @@ -13,7 +13,7 @@ public interface SysDictDataConvert { | ||
| 13 | 13 | ||
| 14 | SysDictDataConvert INSTANCE = Mappers.getMapper(SysDictDataConvert.class); | 14 | SysDictDataConvert INSTANCE = Mappers.getMapper(SysDictDataConvert.class); |
| 15 | 15 | ||
| 16 | - List<SysDictDataSimpleVO> convertList(List<SysDictDataDO> list); | 16 | + List<SysDictDataSimpleRespVO> convertList(List<SysDictDataDO> list); |
| 17 | 17 | ||
| 18 | SysDictDataRespVO convert(SysDictDataDO bean); | 18 | SysDictDataRespVO convert(SysDictDataDO bean); |
| 19 | 19 |
src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SysSmsChannelConvert.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.convert.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; | ||
| 5 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelCreateReqVO; | ||
| 6 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelRespVO; | ||
| 7 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelSimpleRespVO; | ||
| 8 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelUpdateReqVO; | ||
| 9 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO; | ||
| 10 | +import org.mapstruct.Mapper; | ||
| 11 | +import org.mapstruct.factory.Mappers; | ||
| 12 | + | ||
| 13 | +import java.util.List; | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * 短信渠道 Convert | ||
| 17 | + * | ||
| 18 | + * @author 芋道源码 | ||
| 19 | + */ | ||
| 20 | +@Mapper | ||
| 21 | +public interface SysSmsChannelConvert { | ||
| 22 | + | ||
| 23 | + SysSmsChannelConvert INSTANCE = Mappers.getMapper(SysSmsChannelConvert.class); | ||
| 24 | + | ||
| 25 | + SysSmsChannelDO convert(SysSmsChannelCreateReqVO bean); | ||
| 26 | + | ||
| 27 | + SysSmsChannelDO convert(SysSmsChannelUpdateReqVO bean); | ||
| 28 | + | ||
| 29 | + SysSmsChannelRespVO convert(SysSmsChannelDO bean); | ||
| 30 | + | ||
| 31 | + List<SysSmsChannelRespVO> convertList(List<SysSmsChannelDO> list); | ||
| 32 | + | ||
| 33 | + PageResult<SysSmsChannelRespVO> convertPage(PageResult<SysSmsChannelDO> page); | ||
| 34 | + | ||
| 35 | + List<SmsChannelProperties> convertList02(List<SysSmsChannelDO> list); | ||
| 36 | + | ||
| 37 | + List<SysSmsChannelSimpleRespVO> convertList03(List<SysSmsChannelDO> list); | ||
| 38 | + | ||
| 39 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SysSmsLogConvert.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.convert.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogExcelVO; | ||
| 5 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogRespVO; | ||
| 6 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsLogDO; | ||
| 7 | +import org.mapstruct.Mapper; | ||
| 8 | +import org.mapstruct.factory.Mappers; | ||
| 9 | + | ||
| 10 | +import java.util.List; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 短信日志 Convert | ||
| 14 | + * | ||
| 15 | + * @author 芋道源码 | ||
| 16 | + */ | ||
| 17 | +@Mapper | ||
| 18 | +public interface SysSmsLogConvert { | ||
| 19 | + | ||
| 20 | + SysSmsLogConvert INSTANCE = Mappers.getMapper(SysSmsLogConvert.class); | ||
| 21 | + | ||
| 22 | + SysSmsLogRespVO convert(SysSmsLogDO bean); | ||
| 23 | + | ||
| 24 | + List<SysSmsLogRespVO> convertList(List<SysSmsLogDO> list); | ||
| 25 | + | ||
| 26 | + PageResult<SysSmsLogRespVO> convertPage(PageResult<SysSmsLogDO> page); | ||
| 27 | + | ||
| 28 | + List<SysSmsLogExcelVO> convertList02(List<SysSmsLogDO> list); | ||
| 29 | + | ||
| 30 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SysSmsTemplateConvert.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.convert.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplateCreateReqVO; | ||
| 5 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplateExcelVO; | ||
| 6 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplateRespVO; | ||
| 7 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplateUpdateReqVO; | ||
| 8 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; | ||
| 9 | +import org.mapstruct.Mapper; | ||
| 10 | +import org.mapstruct.factory.Mappers; | ||
| 11 | + | ||
| 12 | +import java.util.List; | ||
| 13 | + | ||
| 14 | +@Mapper | ||
| 15 | +public interface SysSmsTemplateConvert { | ||
| 16 | + | ||
| 17 | + SysSmsTemplateConvert INSTANCE = Mappers.getMapper(SysSmsTemplateConvert.class); | ||
| 18 | + | ||
| 19 | + SysSmsTemplateDO convert(SysSmsTemplateCreateReqVO bean); | ||
| 20 | + | ||
| 21 | + SysSmsTemplateDO convert(SysSmsTemplateUpdateReqVO bean); | ||
| 22 | + | ||
| 23 | + SysSmsTemplateRespVO convert(SysSmsTemplateDO bean); | ||
| 24 | + | ||
| 25 | + List<SysSmsTemplateRespVO> convertList(List<SysSmsTemplateDO> list); | ||
| 26 | + | ||
| 27 | + PageResult<SysSmsTemplateRespVO> convertPage(PageResult<SysSmsTemplateDO> page); | ||
| 28 | + | ||
| 29 | + List<SysSmsTemplateExcelVO> convertList02(List<SysSmsTemplateDO> list); | ||
| 30 | + | ||
| 31 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsChannelDO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.dal.dataobject.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.common.enums.CommonStatusEnum; | ||
| 4 | +import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; | ||
| 5 | +import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum; | ||
| 6 | +import com.baomidou.mybatisplus.annotation.TableName; | ||
| 7 | +import lombok.Data; | ||
| 8 | +import lombok.EqualsAndHashCode; | ||
| 9 | +import lombok.ToString; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * 短信渠道 DO | ||
| 13 | + * | ||
| 14 | + * @author zzf | ||
| 15 | + * @since 2021-01-25 | ||
| 16 | + */ | ||
| 17 | +@TableName(value = "sys_sms_channel", autoResultMap = true) | ||
| 18 | +@Data | ||
| 19 | +@EqualsAndHashCode(callSuper = true) | ||
| 20 | +@ToString(callSuper = true) | ||
| 21 | +public class SysSmsChannelDO extends BaseDO { | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * 渠道编号 | ||
| 25 | + */ | ||
| 26 | + private Long id; | ||
| 27 | + /** | ||
| 28 | + * 短信签名 | ||
| 29 | + */ | ||
| 30 | + private String signature; | ||
| 31 | + /** | ||
| 32 | + * 渠道编码 | ||
| 33 | + * | ||
| 34 | + * 枚举 {@link SmsChannelEnum} | ||
| 35 | + */ | ||
| 36 | + private String code; | ||
| 37 | + /** | ||
| 38 | + * 启用状态 | ||
| 39 | + * | ||
| 40 | + * 枚举 {@link CommonStatusEnum} | ||
| 41 | + */ | ||
| 42 | + private Integer status; | ||
| 43 | + /** | ||
| 44 | + * 备注 | ||
| 45 | + */ | ||
| 46 | + private String remark; | ||
| 47 | + /** | ||
| 48 | + * 短信 API 的账号 | ||
| 49 | + */ | ||
| 50 | + private String apiKey; | ||
| 51 | + /** | ||
| 52 | + * 短信 API 的秘钥 | ||
| 53 | + */ | ||
| 54 | + private String apiSecret; | ||
| 55 | + /** | ||
| 56 | + * 短信发送回调 URL | ||
| 57 | + */ | ||
| 58 | + private String callbackUrl; | ||
| 59 | + | ||
| 60 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsLogDO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.dal.dataobject.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.common.enums.UserTypeEnum; | ||
| 4 | +import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; | ||
| 5 | +import cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants; | ||
| 6 | +import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsReceiveStatusEnum; | ||
| 7 | +import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum; | ||
| 8 | +import com.baomidou.mybatisplus.annotation.TableField; | ||
| 9 | +import com.baomidou.mybatisplus.annotation.TableName; | ||
| 10 | +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; | ||
| 11 | +import lombok.*; | ||
| 12 | + | ||
| 13 | +import java.util.Date; | ||
| 14 | +import java.util.Map; | ||
| 15 | + | ||
| 16 | +/** | ||
| 17 | + * 短信日志 DO | ||
| 18 | + * | ||
| 19 | + * @author zzf | ||
| 20 | + * @since 2021-01-25 | ||
| 21 | + */ | ||
| 22 | +@TableName(value = "sys_sms_log", autoResultMap = true) | ||
| 23 | +@Data | ||
| 24 | +@EqualsAndHashCode(callSuper = true) | ||
| 25 | +@ToString(callSuper = true) | ||
| 26 | +@Builder | ||
| 27 | +@AllArgsConstructor | ||
| 28 | +@NoArgsConstructor | ||
| 29 | +public class SysSmsLogDO extends BaseDO { | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * 自增编号 | ||
| 33 | + */ | ||
| 34 | + private Long id; | ||
| 35 | + | ||
| 36 | + // ========= 渠道相关字段 ========= | ||
| 37 | + | ||
| 38 | + /** | ||
| 39 | + * 短信渠道编号 | ||
| 40 | + * | ||
| 41 | + * 关联 {@link SysSmsChannelDO#getId()} | ||
| 42 | + */ | ||
| 43 | + private Long channelId; | ||
| 44 | + /** | ||
| 45 | + * 短信渠道编码 | ||
| 46 | + * | ||
| 47 | + * 冗余 {@link SysSmsChannelDO#getCode()} | ||
| 48 | + */ | ||
| 49 | + private String channelCode; | ||
| 50 | + | ||
| 51 | + // ========= 模板相关字段 ========= | ||
| 52 | + | ||
| 53 | + /** | ||
| 54 | + * 模板编号 | ||
| 55 | + * | ||
| 56 | + * 关联 {@link SysSmsTemplateDO#getId()} | ||
| 57 | + */ | ||
| 58 | + private Long templateId; | ||
| 59 | + /** | ||
| 60 | + * 模板编码 | ||
| 61 | + * | ||
| 62 | + * 冗余 {@link SysSmsTemplateDO#getCode()} | ||
| 63 | + */ | ||
| 64 | + private String templateCode; | ||
| 65 | + /** | ||
| 66 | + * 短信类型 | ||
| 67 | + * | ||
| 68 | + * 冗余 {@link SysSmsTemplateDO#getType()} | ||
| 69 | + */ | ||
| 70 | + private Integer templateType; | ||
| 71 | + /** | ||
| 72 | + * 基于 {@link SysSmsTemplateDO#getContent()} 格式化后的内容 | ||
| 73 | + */ | ||
| 74 | + private String templateContent; | ||
| 75 | + /** | ||
| 76 | + * 基于 {@link SysSmsTemplateDO#getParams()} 输入后的参数 | ||
| 77 | + */ | ||
| 78 | + @TableField(typeHandler = JacksonTypeHandler.class) | ||
| 79 | + private Map<String, Object> templateParams; | ||
| 80 | + /** | ||
| 81 | + * 短信 API 的模板编号 | ||
| 82 | + * | ||
| 83 | + * 冗余 {@link SysSmsTemplateDO#getApiTemplateId()} | ||
| 84 | + */ | ||
| 85 | + private String apiTemplateId; | ||
| 86 | + | ||
| 87 | + // ========= 手机相关字段 ========= | ||
| 88 | + | ||
| 89 | + /** | ||
| 90 | + * 手机号 | ||
| 91 | + */ | ||
| 92 | + private String mobile; | ||
| 93 | + /** | ||
| 94 | + * 用户编号 | ||
| 95 | + */ | ||
| 96 | + private Long userId; | ||
| 97 | + /** | ||
| 98 | + * 用户类型 | ||
| 99 | + * | ||
| 100 | + * 枚举 {@link UserTypeEnum} | ||
| 101 | + */ | ||
| 102 | + private Integer userType; | ||
| 103 | + | ||
| 104 | + // ========= 发送相关字段 ========= | ||
| 105 | + | ||
| 106 | + /** | ||
| 107 | + * 发送状态 | ||
| 108 | + * | ||
| 109 | + * 枚举 {@link SysSmsSendStatusEnum} | ||
| 110 | + */ | ||
| 111 | + private Integer sendStatus; | ||
| 112 | + /** | ||
| 113 | + * 发送时间 | ||
| 114 | + */ | ||
| 115 | + private Date sendTime; | ||
| 116 | + /** | ||
| 117 | + * 发送结果的编码 | ||
| 118 | + * | ||
| 119 | + * 枚举 {@link SmsFrameworkErrorCodeConstants} | ||
| 120 | + */ | ||
| 121 | + private Integer sendCode; | ||
| 122 | + /** | ||
| 123 | + * 发送结果的提示 | ||
| 124 | + * | ||
| 125 | + * 一般情况下,使用 {@link SmsFrameworkErrorCodeConstants} | ||
| 126 | + * 异常情况下,通过格式化 Exception 的提示存储 | ||
| 127 | + */ | ||
| 128 | + private String sendMsg; | ||
| 129 | + /** | ||
| 130 | + * 短信 API 发送结果的编码 | ||
| 131 | + * | ||
| 132 | + * 由于第三方的错误码可能是字符串,所以使用 String 类型 | ||
| 133 | + */ | ||
| 134 | + private String apiSendCode; | ||
| 135 | + /** | ||
| 136 | + * 短信 API 发送失败的提示 | ||
| 137 | + */ | ||
| 138 | + private String apiSendMsg; | ||
| 139 | + /** | ||
| 140 | + * 短信 API 发送返回的唯一请求 ID | ||
| 141 | + * | ||
| 142 | + * 用于和短信 API 进行定位于排错 | ||
| 143 | + */ | ||
| 144 | + private String apiRequestId; | ||
| 145 | + /** | ||
| 146 | + * 短信 API 发送返回的序号 | ||
| 147 | + * | ||
| 148 | + * 用于和短信 API 平台的发送记录关联 | ||
| 149 | + */ | ||
| 150 | + private String apiSerialNo; | ||
| 151 | + | ||
| 152 | + // ========= 接收相关字段 ========= | ||
| 153 | + | ||
| 154 | + /** | ||
| 155 | + * 接收状态 | ||
| 156 | + * | ||
| 157 | + * 枚举 {@link SysSmsReceiveStatusEnum} | ||
| 158 | + */ | ||
| 159 | + private Integer receiveStatus; | ||
| 160 | + /** | ||
| 161 | + * 接收时间 | ||
| 162 | + */ | ||
| 163 | + private Date receiveTime; | ||
| 164 | + /** | ||
| 165 | + * 短信 API 接收结果的编码 | ||
| 166 | + */ | ||
| 167 | + private String apiReceiveCode; | ||
| 168 | + /** | ||
| 169 | + * 短信 API 接收结果的提示 | ||
| 170 | + */ | ||
| 171 | + private String apiReceiveMsg; | ||
| 172 | + | ||
| 173 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsTemplateDO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.dal.dataobject.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.common.enums.CommonStatusEnum; | ||
| 4 | +import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; | ||
| 5 | +import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsTemplateTypeEnum; | ||
| 6 | +import com.baomidou.mybatisplus.annotation.TableField; | ||
| 7 | +import com.baomidou.mybatisplus.annotation.TableName; | ||
| 8 | +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; | ||
| 9 | +import lombok.Data; | ||
| 10 | +import lombok.EqualsAndHashCode; | ||
| 11 | +import lombok.ToString; | ||
| 12 | + | ||
| 13 | +import java.util.List; | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * 短信模板 DO | ||
| 17 | + * | ||
| 18 | + * @author zzf | ||
| 19 | + * @since 2021-01-25 | ||
| 20 | + */ | ||
| 21 | +@TableName(value = "sys_sms_template", autoResultMap = true) | ||
| 22 | +@Data | ||
| 23 | +@EqualsAndHashCode(callSuper = true) | ||
| 24 | +@ToString(callSuper = true) | ||
| 25 | +public class SysSmsTemplateDO extends BaseDO { | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * 自增编号 | ||
| 29 | + */ | ||
| 30 | + private Long id; | ||
| 31 | + | ||
| 32 | + // ========= 模板相关字段 ========= | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 短信类型 | ||
| 36 | + * | ||
| 37 | + * 枚举 {@link SysSmsTemplateTypeEnum} | ||
| 38 | + */ | ||
| 39 | + private Integer type; | ||
| 40 | + /** | ||
| 41 | + * 启用状态 | ||
| 42 | + * | ||
| 43 | + * 枚举 {@link CommonStatusEnum} | ||
| 44 | + */ | ||
| 45 | + private Integer status; | ||
| 46 | + /** | ||
| 47 | + * 模板编码,保证唯一 | ||
| 48 | + */ | ||
| 49 | + private String code; | ||
| 50 | + /** | ||
| 51 | + * 模板名称 | ||
| 52 | + */ | ||
| 53 | + private String name; | ||
| 54 | + /** | ||
| 55 | + * 模板内容 | ||
| 56 | + * | ||
| 57 | + * 内容的参数,使用 {} 包括,例如说 {name} | ||
| 58 | + */ | ||
| 59 | + private String content; | ||
| 60 | + /** | ||
| 61 | + * 参数数组(自动根据内容生成) | ||
| 62 | + */ | ||
| 63 | + @TableField(typeHandler = JacksonTypeHandler.class) | ||
| 64 | + private List<String> params; | ||
| 65 | + /** | ||
| 66 | + * 备注 | ||
| 67 | + */ | ||
| 68 | + private String remark; | ||
| 69 | + /** | ||
| 70 | + * 短信 API 的模板编号 | ||
| 71 | + */ | ||
| 72 | + private String apiTemplateId; | ||
| 73 | + | ||
| 74 | + // ========= 渠道相关字段 ========= | ||
| 75 | + | ||
| 76 | + /** | ||
| 77 | + * 短信渠道编号 | ||
| 78 | + * | ||
| 79 | + * 关联 {@link SysSmsChannelDO#getId()} | ||
| 80 | + */ | ||
| 81 | + private Long channelId; | ||
| 82 | + /** | ||
| 83 | + * 短信渠道编码 | ||
| 84 | + * | ||
| 85 | + * 冗余 {@link SysSmsChannelDO#getCode()} | ||
| 86 | + */ | ||
| 87 | + private String channelCode; | ||
| 88 | + | ||
| 89 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsChannelMapper.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.dal.mysql.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX; | ||
| 5 | +import cn.iocoder.dashboard.framework.mybatis.core.query.QueryWrapperX; | ||
| 6 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelPageReqVO; | ||
| 7 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO; | ||
| 8 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||
| 9 | +import org.apache.ibatis.annotations.Mapper; | ||
| 10 | +import org.apache.ibatis.annotations.Select; | ||
| 11 | + | ||
| 12 | +import java.util.Date; | ||
| 13 | +import java.util.List; | ||
| 14 | + | ||
| 15 | +@Mapper | ||
| 16 | +public interface SysSmsChannelMapper extends BaseMapperX<SysSmsChannelDO> { | ||
| 17 | + | ||
| 18 | + default PageResult<SysSmsChannelDO> selectPage(SysSmsChannelPageReqVO reqVO) { | ||
| 19 | + return selectPage(reqVO, new QueryWrapperX<SysSmsChannelDO>() | ||
| 20 | + .likeIfPresent("signature", reqVO.getSignature()) | ||
| 21 | + .eqIfPresent("status", reqVO.getStatus()) | ||
| 22 | + .betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime()) | ||
| 23 | + .orderByDesc("id")); | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + @Select("SELECT id FROM sys_sms_channel WHERE update_time > #{maxUpdateTime} LIMIT 1") | ||
| 27 | + Long selectExistsByUpdateTimeAfter(Date maxUpdateTime); | ||
| 28 | + | ||
| 29 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsLogMapper.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.dal.mysql.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX; | ||
| 5 | +import cn.iocoder.dashboard.framework.mybatis.core.query.QueryWrapperX; | ||
| 6 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogExportReqVO; | ||
| 7 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogPageReqVO; | ||
| 8 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsLogDO; | ||
| 9 | +import org.apache.ibatis.annotations.Mapper; | ||
| 10 | + | ||
| 11 | +import java.util.List; | ||
| 12 | + | ||
| 13 | +@Mapper | ||
| 14 | +public interface SysSmsLogMapper extends BaseMapperX<SysSmsLogDO> { | ||
| 15 | + | ||
| 16 | + default PageResult<SysSmsLogDO> selectPage(SysSmsLogPageReqVO reqVO) { | ||
| 17 | + return selectPage(reqVO, new QueryWrapperX<SysSmsLogDO>() | ||
| 18 | + .eqIfPresent("channel_id", reqVO.getChannelId()) | ||
| 19 | + .eqIfPresent("template_id", reqVO.getTemplateId()) | ||
| 20 | + .likeIfPresent("mobile", reqVO.getMobile()) | ||
| 21 | + .eqIfPresent("send_status", reqVO.getSendStatus()) | ||
| 22 | + .betweenIfPresent("send_time", reqVO.getBeginSendTime(), reqVO.getEndSendTime()) | ||
| 23 | + .eqIfPresent("receive_status", reqVO.getReceiveStatus()) | ||
| 24 | + .betweenIfPresent("receive_time", reqVO.getBeginReceiveTime(), reqVO.getEndReceiveTime()) | ||
| 25 | + .orderByDesc("id")); | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + default List<SysSmsLogDO> selectList(SysSmsLogExportReqVO reqVO) { | ||
| 29 | + return selectList(new QueryWrapperX<SysSmsLogDO>() | ||
| 30 | + .eqIfPresent("channel_id", reqVO.getChannelId()) | ||
| 31 | + .eqIfPresent("template_id", reqVO.getTemplateId()) | ||
| 32 | + .likeIfPresent("mobile", reqVO.getMobile()) | ||
| 33 | + .eqIfPresent("send_status", reqVO.getSendStatus()) | ||
| 34 | + .betweenIfPresent("send_time", reqVO.getBeginSendTime(), reqVO.getEndSendTime()) | ||
| 35 | + .eqIfPresent("receive_status", reqVO.getReceiveStatus()) | ||
| 36 | + .betweenIfPresent("receive_time", reqVO.getBeginReceiveTime(), reqVO.getEndReceiveTime()) | ||
| 37 | + .orderByDesc("id")); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsTemplateMapper.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.dal.mysql.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX; | ||
| 5 | +import cn.iocoder.dashboard.framework.mybatis.core.query.QueryWrapperX; | ||
| 6 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplateExportReqVO; | ||
| 7 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.template.SysSmsTemplatePageReqVO; | ||
| 8 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; | ||
| 9 | +import org.apache.ibatis.annotations.Mapper; | ||
| 10 | +import org.apache.ibatis.annotations.Select; | ||
| 11 | + | ||
| 12 | +import java.util.Date; | ||
| 13 | +import java.util.List; | ||
| 14 | + | ||
| 15 | +@Mapper | ||
| 16 | +public interface SysSmsTemplateMapper extends BaseMapperX<SysSmsTemplateDO> { | ||
| 17 | + | ||
| 18 | + default SysSmsTemplateDO selectByCode(String code) { | ||
| 19 | + return selectOne("code", code); | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + default PageResult<SysSmsTemplateDO> selectPage(SysSmsTemplatePageReqVO reqVO) { | ||
| 23 | + return selectPage(reqVO, new QueryWrapperX<SysSmsTemplateDO>() | ||
| 24 | + .eqIfPresent("type", reqVO.getType()) | ||
| 25 | + .eqIfPresent("status", reqVO.getStatus()) | ||
| 26 | + .likeIfPresent("code", reqVO.getCode()) | ||
| 27 | + .likeIfPresent("content", reqVO.getContent()) | ||
| 28 | + .likeIfPresent("api_template_id", reqVO.getApiTemplateId()) | ||
| 29 | + .eqIfPresent("channel_id", reqVO.getChannelId()) | ||
| 30 | + .betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime()) | ||
| 31 | + .orderByDesc("id")); | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + default List<SysSmsTemplateDO> selectList(SysSmsTemplateExportReqVO reqVO) { | ||
| 35 | + return selectList(new QueryWrapperX<SysSmsTemplateDO>() | ||
| 36 | + .eqIfPresent("type", reqVO.getType()) | ||
| 37 | + .eqIfPresent("status", reqVO.getStatus()) | ||
| 38 | + .likeIfPresent("code", reqVO.getCode()) | ||
| 39 | + .likeIfPresent("content", reqVO.getContent()) | ||
| 40 | + .likeIfPresent("api_template_id", reqVO.getApiTemplateId()) | ||
| 41 | + .eqIfPresent("channel_id", reqVO.getChannelId()) | ||
| 42 | + .betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime()) | ||
| 43 | + .orderByDesc("id")); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + default Integer selectCountByChannelId(Long channelId) { | ||
| 47 | + return selectCount("channel_id", channelId); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + @Select("SELECT id FROM sys_sms_template WHERE update_time > #{maxUpdateTime} LIMIT 1") | ||
| 51 | + Long selectExistsByUpdateTimeAfter(Date maxUpdateTime); | ||
| 52 | + | ||
| 53 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/dal/redis/RedisKeyConstants.java
0 → 100644
src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java
| @@ -30,8 +30,8 @@ public interface SysErrorCodeConstants { | @@ -30,8 +30,8 @@ public interface SysErrorCodeConstants { | ||
| 30 | 30 | ||
| 31 | // ========== 角色模块 1002003000 ========== | 31 | // ========== 角色模块 1002003000 ========== |
| 32 | ErrorCode ROLE_NOT_EXISTS = new ErrorCode(1002003000, "角色不存在"); | 32 | ErrorCode ROLE_NOT_EXISTS = new ErrorCode(1002003000, "角色不存在"); |
| 33 | - ErrorCode ROLE_NAME_DUPLICATE = new ErrorCode(1002003001, "已经存在名为【{}}】的角色"); | ||
| 34 | - ErrorCode ROLE_CODE_DUPLICATE = new ErrorCode(1002003002, "已经存在编码为【{}}】的角色"); | 33 | + ErrorCode ROLE_NAME_DUPLICATE = new ErrorCode(1002003001, "已经存在名为【{}】的角色"); |
| 34 | + ErrorCode ROLE_CODE_DUPLICATE = new ErrorCode(1002003002, "已经存在编码为【{}】的角色"); | ||
| 35 | ErrorCode ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE = new ErrorCode(1002003004, "不能操作类型为系统内置的角色"); | 35 | ErrorCode ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE = new ErrorCode(1002003004, "不能操作类型为系统内置的角色"); |
| 36 | 36 | ||
| 37 | // ========== 用户模块 1002004000 ========== | 37 | // ========== 用户模块 1002004000 ========== |
| @@ -78,4 +78,18 @@ public interface SysErrorCodeConstants { | @@ -78,4 +78,18 @@ public interface SysErrorCodeConstants { | ||
| 78 | ErrorCode FILE_UPLOAD_FAILED = new ErrorCode(1002009002, "文件上传失败"); | 78 | ErrorCode FILE_UPLOAD_FAILED = new ErrorCode(1002009002, "文件上传失败"); |
| 79 | ErrorCode FILE_IS_EMPTY= new ErrorCode(1002009003, "文件为空"); | 79 | ErrorCode FILE_IS_EMPTY= new ErrorCode(1002009003, "文件为空"); |
| 80 | 80 | ||
| 81 | + // ========== 短信渠道 1002011000 ========== | ||
| 82 | + ErrorCode SMS_CHANNEL_NOT_EXISTS = new ErrorCode(1002011000, "短信渠道不存在"); | ||
| 83 | + ErrorCode SMS_CHANNEL_DISABLE = new ErrorCode(1002011001, "短信渠道不处于开启状态,不允许选择"); | ||
| 84 | + ErrorCode SMS_CHANNEL_HAS_CHILDREN = new ErrorCode(1002011002, "无法删除,该短信渠道还有短信模板"); | ||
| 85 | + | ||
| 86 | + // ========== 短信模板 1002011000 ========== | ||
| 87 | + ErrorCode SMS_TEMPLATE_NOT_EXISTS = new ErrorCode(1002011000, "短信模板不存在"); | ||
| 88 | + ErrorCode SMS_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1002011001, "已经存在编码为【{}】的短信模板"); | ||
| 89 | + | ||
| 90 | + // ========== 短信发送 1002012000 ========== | ||
| 91 | + ErrorCode SMS_SEND_MOBILE_NOT_EXISTS = new ErrorCode(1002012000, "手机号不存在"); | ||
| 92 | + ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1002012001, "模板参数({})缺失"); | ||
| 93 | + | ||
| 94 | + | ||
| 81 | } | 95 | } |
src/main/java/cn/iocoder/dashboard/modules/system/enums/dict/SysDictTypeEnum.java
| @@ -18,6 +18,10 @@ public enum SysDictTypeEnum { | @@ -18,6 +18,10 @@ public enum SysDictTypeEnum { | ||
| 18 | SYS_LOGIN_RESULT("sys_login_result"), // 登陆结果 | 18 | SYS_LOGIN_RESULT("sys_login_result"), // 登陆结果 |
| 19 | SYS_CONFIG_TYPE("sys_config_type"), // 参数配置类型 | 19 | SYS_CONFIG_TYPE("sys_config_type"), // 参数配置类型 |
| 20 | SYS_BOOLEAN_STRING("sys_boolean_string"), // Boolean 是否类型 | 20 | SYS_BOOLEAN_STRING("sys_boolean_string"), // Boolean 是否类型 |
| 21 | + SYS_SMS_CHANNEL_CODE("sys_sms_channel_code"), // 短信渠道编码 | ||
| 22 | + SYS_SMS_TEMPLATE_TYPE("sys_sms_template_type"), // 短信模板类型 | ||
| 23 | + SYS_SMS_SEND_STATUS("sys_sms_send_status"), // 短信发送状态 | ||
| 24 | + SYS_SMS_RECEIVE_STATUS("sys_sms_receive_status"), // 短信接收状态 | ||
| 21 | 25 | ||
| 22 | INF_REDIS_TIMEOUT_TYPE("inf_redis_timeout_type"), // Redis 超时类型 | 26 | INF_REDIS_TIMEOUT_TYPE("inf_redis_timeout_type"), // Redis 超时类型 |
| 23 | INF_JOB_STATUS("inf_job_status"), // 定时任务状态的枚举 | 27 | INF_JOB_STATUS("inf_job_status"), // 定时任务状态的枚举 |
src/main/java/cn/iocoder/dashboard/modules/system/enums/sms/SysSmsReceiveStatusEnum.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.enums.sms; | ||
| 2 | + | ||
| 3 | +import lombok.AllArgsConstructor; | ||
| 4 | +import lombok.Getter; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 短信的接收状态枚举 | ||
| 8 | + * | ||
| 9 | + * @author 芋道源码 | ||
| 10 | + * @date 2021/2/1 13:39 | ||
| 11 | + */ | ||
| 12 | +@Getter | ||
| 13 | +@AllArgsConstructor | ||
| 14 | +public enum SysSmsReceiveStatusEnum { | ||
| 15 | + | ||
| 16 | + INIT(0), // 初始化 | ||
| 17 | + SUCCESS(10), // 接收成功 | ||
| 18 | + FAILURE(20), // 接收失败 | ||
| 19 | + ; | ||
| 20 | + | ||
| 21 | + private final int status; | ||
| 22 | + | ||
| 23 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/enums/sms/SysSmsSendStatusEnum.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.enums.sms; | ||
| 2 | + | ||
| 3 | +import lombok.AllArgsConstructor; | ||
| 4 | +import lombok.Getter; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 短信的发送状态枚举 | ||
| 8 | + * | ||
| 9 | + * @author zzf | ||
| 10 | + * @date 2021/2/1 13:39 | ||
| 11 | + */ | ||
| 12 | +@Getter | ||
| 13 | +@AllArgsConstructor | ||
| 14 | +public enum SysSmsSendStatusEnum { | ||
| 15 | + | ||
| 16 | + INIT(0), // 初始化 | ||
| 17 | + SUCCESS(10), // 发送成功 | ||
| 18 | + FAILURE(20), // 发送失败 | ||
| 19 | + IGNORE(30), // 忽略,即不发送 | ||
| 20 | + ; | ||
| 21 | + | ||
| 22 | + private final int status; | ||
| 23 | + | ||
| 24 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/enums/sms/SysSmsTemplateTypeEnum.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.enums.sms; | ||
| 2 | + | ||
| 3 | +import lombok.AllArgsConstructor; | ||
| 4 | +import lombok.Getter; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 短信的模板类型枚举 | ||
| 8 | + * | ||
| 9 | + * @author 芋道源码 | ||
| 10 | + */ | ||
| 11 | +@Getter | ||
| 12 | +@AllArgsConstructor | ||
| 13 | +public enum SysSmsTemplateTypeEnum { | ||
| 14 | + | ||
| 15 | + VERIFICATION_CODE(1), // 验证码 | ||
| 16 | + NOTICE(2), // 通知 | ||
| 17 | + PROMOTION(3), // 营销 | ||
| 18 | + ; | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * 类型 | ||
| 22 | + */ | ||
| 23 | + private final int type; | ||
| 24 | + | ||
| 25 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SysSmsChannelRefreshConsumer.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.mq.consumer.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener; | ||
| 4 | +import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsChannelRefreshMessage; | ||
| 5 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService; | ||
| 6 | +import lombok.extern.slf4j.Slf4j; | ||
| 7 | +import org.springframework.stereotype.Component; | ||
| 8 | + | ||
| 9 | +import javax.annotation.Resource; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * 针对 {@link SysSmsChannelRefreshMessage} 的消费者 | ||
| 13 | + * | ||
| 14 | + * @author 芋道源码 | ||
| 15 | + */ | ||
| 16 | +@Component | ||
| 17 | +@Slf4j | ||
| 18 | +public class SysSmsChannelRefreshConsumer extends AbstractChannelMessageListener<SysSmsChannelRefreshMessage> { | ||
| 19 | + | ||
| 20 | + @Resource | ||
| 21 | + private SysSmsChannelService smsChannelService; | ||
| 22 | + | ||
| 23 | + @Override | ||
| 24 | + public void onMessage(SysSmsChannelRefreshMessage message) { | ||
| 25 | + log.info("[onMessage][收到 SmsChannel 刷新消息]"); | ||
| 26 | + smsChannelService.initSmsClients(); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SysSmsSendConsumer.java
| @@ -2,16 +2,29 @@ package cn.iocoder.dashboard.modules.system.mq.consumer.sms; | @@ -2,16 +2,29 @@ package cn.iocoder.dashboard.modules.system.mq.consumer.sms; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.dashboard.framework.redis.core.stream.AbstractStreamMessageListener; | 3 | import cn.iocoder.dashboard.framework.redis.core.stream.AbstractStreamMessageListener; |
| 4 | import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage; | 4 | import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage; |
| 5 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService; | ||
| 5 | import lombok.extern.slf4j.Slf4j; | 6 | import lombok.extern.slf4j.Slf4j; |
| 6 | import org.springframework.stereotype.Component; | 7 | import org.springframework.stereotype.Component; |
| 7 | 8 | ||
| 9 | +import javax.annotation.Resource; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * 针对 {@link SysSmsSendMessage} 的消费者 | ||
| 13 | + * | ||
| 14 | + * @author zzf | ||
| 15 | + * @date 2021/3/9 16:35 | ||
| 16 | + */ | ||
| 8 | @Component | 17 | @Component |
| 9 | @Slf4j | 18 | @Slf4j |
| 10 | public class SysSmsSendConsumer extends AbstractStreamMessageListener<SysSmsSendMessage> { | 19 | public class SysSmsSendConsumer extends AbstractStreamMessageListener<SysSmsSendMessage> { |
| 11 | 20 | ||
| 21 | + @Resource | ||
| 22 | + private SysSmsService smsService; | ||
| 23 | + | ||
| 12 | @Override | 24 | @Override |
| 13 | public void onMessage(SysSmsSendMessage message) { | 25 | public void onMessage(SysSmsSendMessage message) { |
| 14 | log.info("[onMessage][消息内容({})]", message); | 26 | log.info("[onMessage][消息内容({})]", message); |
| 27 | + smsService.doSendSms(message); | ||
| 15 | } | 28 | } |
| 16 | 29 | ||
| 17 | } | 30 | } |
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SysSmsTemplateRefreshConsumer.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.mq.consumer.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener; | ||
| 4 | +import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsTemplateRefreshMessage; | ||
| 5 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsTemplateService; | ||
| 6 | +import lombok.extern.slf4j.Slf4j; | ||
| 7 | +import org.springframework.stereotype.Component; | ||
| 8 | + | ||
| 9 | +import javax.annotation.Resource; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * 针对 {@link SysSmsTemplateRefreshMessage} 的消费者 | ||
| 13 | + * | ||
| 14 | + * @author 芋道源码 | ||
| 15 | + */ | ||
| 16 | +@Component | ||
| 17 | +@Slf4j | ||
| 18 | +public class SysSmsTemplateRefreshConsumer extends AbstractChannelMessageListener<SysSmsTemplateRefreshMessage> { | ||
| 19 | + | ||
| 20 | + @Resource | ||
| 21 | + private SysSmsTemplateService smsTemplateService; | ||
| 22 | + | ||
| 23 | + @Override | ||
| 24 | + public void onMessage(SysSmsTemplateRefreshMessage message) { | ||
| 25 | + log.info("[onMessage][收到 SmsTemplate 刷新消息]"); | ||
| 26 | + smsTemplateService.initLocalCache(); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/mq/message/sms/SysSmsChannelRefreshMessage.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.mq.message.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.framework.redis.core.pubsub.ChannelMessage; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 短信渠道的数据刷新 Message | ||
| 8 | + */ | ||
| 9 | +@Data | ||
| 10 | +public class SysSmsChannelRefreshMessage implements ChannelMessage { | ||
| 11 | + | ||
| 12 | + @Override | ||
| 13 | + public String getChannel() { | ||
| 14 | + return "system.sms-channel.refresh"; | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/mq/message/sms/SysSmsSendMessage.java
| 1 | package cn.iocoder.dashboard.modules.system.mq.message.sms; | 1 | package cn.iocoder.dashboard.modules.system.mq.message.sms; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.dashboard.common.core.KeyValue; | ||
| 3 | import cn.iocoder.dashboard.framework.redis.core.stream.StreamMessage; | 4 | import cn.iocoder.dashboard.framework.redis.core.stream.StreamMessage; |
| 4 | import lombok.Data; | 5 | import lombok.Data; |
| 5 | 6 | ||
| 6 | import javax.validation.constraints.NotNull; | 7 | import javax.validation.constraints.NotNull; |
| 7 | -import java.util.Map; | 8 | +import java.util.List; |
| 8 | 9 | ||
| 9 | /** | 10 | /** |
| 10 | * 短信发送消息 | 11 | * 短信发送消息 |
| @@ -15,28 +16,29 @@ import java.util.Map; | @@ -15,28 +16,29 @@ import java.util.Map; | ||
| 15 | public class SysSmsSendMessage implements StreamMessage { | 16 | public class SysSmsSendMessage implements StreamMessage { |
| 16 | 17 | ||
| 17 | /** | 18 | /** |
| 19 | + * 短信日志编号 | ||
| 20 | + */ | ||
| 21 | + @NotNull(message = "短信日志编号不能为空") | ||
| 22 | + private Long logId; | ||
| 23 | + /** | ||
| 18 | * 手机号 | 24 | * 手机号 |
| 19 | */ | 25 | */ |
| 20 | @NotNull(message = "手机号不能为空") | 26 | @NotNull(message = "手机号不能为空") |
| 21 | private String mobile; | 27 | private String mobile; |
| 22 | /** | 28 | /** |
| 23 | - * 短信模板编号 | 29 | + * 短信渠道编号 |
| 24 | */ | 30 | */ |
| 25 | - @NotNull(message = "短信模板编号不能为空") | ||
| 26 | - private String templateCode; | 31 | + @NotNull(message = "短信渠道编号不能为空") |
| 32 | + private Long channelId; | ||
| 27 | /** | 33 | /** |
| 28 | - * 短信模板参数 | 34 | + * 短信 API 的模板编号 |
| 29 | */ | 35 | */ |
| 30 | - private Map<String, Object> templateParams; | ||
| 31 | - | 36 | + @NotNull(message = "短信 API 的模板编号不能为空") |
| 37 | + private String apiTemplateId; | ||
| 32 | /** | 38 | /** |
| 33 | - * 用户编号,允许空 | ||
| 34 | - */ | ||
| 35 | - private Integer userId; | ||
| 36 | - /** | ||
| 37 | - * 用户类型,允许空 | 39 | + * 短信模板参数 |
| 38 | */ | 40 | */ |
| 39 | - private Integer userType; | 41 | + private List<KeyValue<String, Object>> templateParams; |
| 40 | 42 | ||
| 41 | @Override | 43 | @Override |
| 42 | public String getStreamKey() { | 44 | public String getStreamKey() { |
src/main/java/cn/iocoder/dashboard/modules/system/mq/message/sms/SysSmsTemplateRefreshMessage.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.mq.message.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.framework.redis.core.pubsub.ChannelMessage; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 短信模板的数据刷新 Message | ||
| 8 | + */ | ||
| 9 | +@Data | ||
| 10 | +public class SysSmsTemplateRefreshMessage implements ChannelMessage { | ||
| 11 | + | ||
| 12 | + @Override | ||
| 13 | + public String getChannel() { | ||
| 14 | + return "system.sms-template.refresh"; | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/mq/producer/permission/SysRoleProducer.java
| @@ -9,6 +9,8 @@ import javax.annotation.Resource; | @@ -9,6 +9,8 @@ import javax.annotation.Resource; | ||
| 9 | 9 | ||
| 10 | /** | 10 | /** |
| 11 | * Role 角色相关消息的 Producer | 11 | * Role 角色相关消息的 Producer |
| 12 | + * | ||
| 13 | + * @author 芋道源码 | ||
| 12 | */ | 14 | */ |
| 13 | @Component | 15 | @Component |
| 14 | public class SysRoleProducer { | 16 | public class SysRoleProducer { |
src/main/java/cn/iocoder/dashboard/modules/system/mq/producer/sms/SysSmsProducer.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.mq.producer.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.common.core.KeyValue; | ||
| 4 | +import cn.iocoder.dashboard.framework.redis.core.util.RedisMessageUtils; | ||
| 5 | +import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsChannelRefreshMessage; | ||
| 6 | +import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage; | ||
| 7 | +import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsTemplateRefreshMessage; | ||
| 8 | +import lombok.extern.slf4j.Slf4j; | ||
| 9 | +import org.springframework.data.redis.core.StringRedisTemplate; | ||
| 10 | +import org.springframework.stereotype.Component; | ||
| 11 | + | ||
| 12 | +import javax.annotation.Resource; | ||
| 13 | +import java.util.List; | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * Sms 短信相关消息的 Producer | ||
| 17 | + * | ||
| 18 | + * @author zzf | ||
| 19 | + * @date 2021/3/9 16:35 | ||
| 20 | + */ | ||
| 21 | +@Slf4j | ||
| 22 | +@Component | ||
| 23 | +public class SysSmsProducer { | ||
| 24 | + | ||
| 25 | + @Resource | ||
| 26 | + private StringRedisTemplate stringRedisTemplate; | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 发送 {@link SysSmsSendMessage} 消息 | ||
| 30 | + * | ||
| 31 | + * @param logId 短信日志编号 | ||
| 32 | + * @param mobile 手机号 | ||
| 33 | + * @param channelId 渠道编号 | ||
| 34 | + * @param apiTemplateId 短信模板编号 | ||
| 35 | + * @param templateParams 短信模板参数 | ||
| 36 | + */ | ||
| 37 | + public void sendSmsSendMessage(Long logId, String mobile, | ||
| 38 | + Long channelId, String apiTemplateId, List<KeyValue<String, Object>> templateParams) { | ||
| 39 | + SysSmsSendMessage message = new SysSmsSendMessage().setLogId(logId).setMobile(mobile); | ||
| 40 | + message.setChannelId(channelId).setApiTemplateId(apiTemplateId).setTemplateParams(templateParams); | ||
| 41 | + RedisMessageUtils.sendStreamMessage(stringRedisTemplate, message); | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + /** | ||
| 45 | + * 发送 {@link SysSmsChannelRefreshMessage} 消息 | ||
| 46 | + */ | ||
| 47 | + public void sendSmsChannelRefreshMessage() { | ||
| 48 | + SysSmsChannelRefreshMessage message = new SysSmsChannelRefreshMessage(); | ||
| 49 | + RedisMessageUtils.sendChannelMessage(stringRedisTemplate, message); | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + /** | ||
| 53 | + * 发送 {@link SysSmsTemplateRefreshMessage} 消息 | ||
| 54 | + */ | ||
| 55 | + public void sendSmsTemplateRefreshMessage() { | ||
| 56 | + SysSmsTemplateRefreshMessage message = new SysSmsTemplateRefreshMessage(); | ||
| 57 | + RedisMessageUtils.sendChannelMessage(stringRedisTemplate, message); | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysPermissionService.java
| @@ -18,7 +18,7 @@ import java.util.Set; | @@ -18,7 +18,7 @@ import java.util.Set; | ||
| 18 | public interface SysPermissionService extends SecurityPermissionFrameworkService { | 18 | public interface SysPermissionService extends SecurityPermissionFrameworkService { |
| 19 | 19 | ||
| 20 | /** | 20 | /** |
| 21 | - * 初始化 | 21 | + * 初始化权限的本地缓存 |
| 22 | */ | 22 | */ |
| 23 | void initLocalCache(); | 23 | void initLocalCache(); |
| 24 | 24 |
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysMenuServiceImpl.java
| @@ -15,6 +15,7 @@ import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysMenuProduce | @@ -15,6 +15,7 @@ import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysMenuProduce | ||
| 15 | import cn.iocoder.dashboard.modules.system.service.permission.SysMenuService; | 15 | import cn.iocoder.dashboard.modules.system.service.permission.SysMenuService; |
| 16 | import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService; | 16 | import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService; |
| 17 | import cn.iocoder.dashboard.util.collection.CollectionUtils; | 17 | import cn.iocoder.dashboard.util.collection.CollectionUtils; |
| 18 | +import com.google.common.annotations.VisibleForTesting; | ||
| 18 | import com.google.common.collect.ImmutableMap; | 19 | import com.google.common.collect.ImmutableMap; |
| 19 | import com.google.common.collect.ImmutableMultimap; | 20 | import com.google.common.collect.ImmutableMultimap; |
| 20 | import com.google.common.collect.Multimap; | 21 | import com.google.common.collect.Multimap; |
| @@ -168,10 +169,6 @@ public class SysMenuServiceImpl implements SysMenuService { | @@ -168,10 +169,6 @@ public class SysMenuServiceImpl implements SysMenuService { | ||
| 168 | */ | 169 | */ |
| 169 | @Transactional(rollbackFor = Exception.class) | 170 | @Transactional(rollbackFor = Exception.class) |
| 170 | public void deleteMenu(Long menuId) { | 171 | public void deleteMenu(Long menuId) { |
| 171 | - // 校验更新的菜单是否存在 | ||
| 172 | - if (menuMapper.selectById(menuId) == null) { | ||
| 173 | - throw ServiceExceptionUtil.exception(MENU_NOT_EXISTS); | ||
| 174 | - } | ||
| 175 | // 校验是否还有子菜单 | 172 | // 校验是否还有子菜单 |
| 176 | if (menuMapper.selectCountByParentId(menuId) > 0) { | 173 | if (menuMapper.selectCountByParentId(menuId) > 0) { |
| 177 | throw ServiceExceptionUtil.exception(MENU_EXISTS_CHILDREN); | 174 | throw ServiceExceptionUtil.exception(MENU_EXISTS_CHILDREN); |
| @@ -250,7 +247,8 @@ public class SysMenuServiceImpl implements SysMenuService { | @@ -250,7 +247,8 @@ public class SysMenuServiceImpl implements SysMenuService { | ||
| 250 | * @param parentId 父菜单编号 | 247 | * @param parentId 父菜单编号 |
| 251 | * @param childId 当前菜单编号 | 248 | * @param childId 当前菜单编号 |
| 252 | */ | 249 | */ |
| 253 | - private void checkParentResource(Long parentId, Long childId) { | 250 | + @VisibleForTesting |
| 251 | + public void checkParentResource(Long parentId, Long childId) { | ||
| 254 | if (parentId == null || MenuIdEnum.ROOT.getId().equals(parentId)) { | 252 | if (parentId == null || MenuIdEnum.ROOT.getId().equals(parentId)) { |
| 255 | return; | 253 | return; |
| 256 | } | 254 | } |
| @@ -279,7 +277,8 @@ public class SysMenuServiceImpl implements SysMenuService { | @@ -279,7 +277,8 @@ public class SysMenuServiceImpl implements SysMenuService { | ||
| 279 | * @param parentId 父菜单编号 | 277 | * @param parentId 父菜单编号 |
| 280 | * @param id 菜单编号 | 278 | * @param id 菜单编号 |
| 281 | */ | 279 | */ |
| 282 | - private void checkResource(Long parentId, String name, Long id) { | 280 | + @VisibleForTesting |
| 281 | + public void checkResource(Long parentId, String name, Long id) { | ||
| 283 | SysMenuDO menu = menuMapper.selectByParentIdAndName(parentId, name); | 282 | SysMenuDO menu = menuMapper.selectByParentIdAndName(parentId, name); |
| 284 | if (menu == null) { | 283 | if (menu == null) { |
| 285 | return; | 284 | return; |
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysPermissionServiceImpl.java
| @@ -3,7 +3,6 @@ package cn.iocoder.dashboard.modules.system.service.permission.impl; | @@ -3,7 +3,6 @@ package cn.iocoder.dashboard.modules.system.service.permission.impl; | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | import cn.hutool.core.collection.CollectionUtil; | 4 | import cn.hutool.core.collection.CollectionUtil; |
| 5 | import cn.hutool.core.util.ArrayUtil; | 5 | import cn.hutool.core.util.ArrayUtil; |
| 6 | -import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; | ||
| 7 | import cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils; | 6 | import cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils; |
| 8 | import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysRoleMenuMapper; | 7 | import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysRoleMenuMapper; |
| 9 | import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysUserRoleMapper; | 8 | import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysUserRoleMapper; |
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysRoleServiceImpl.java
| @@ -18,6 +18,7 @@ import cn.iocoder.dashboard.modules.system.enums.permission.SysRoleTypeEnum; | @@ -18,6 +18,7 @@ import cn.iocoder.dashboard.modules.system.enums.permission.SysRoleTypeEnum; | ||
| 18 | import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysRoleProducer; | 18 | import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysRoleProducer; |
| 19 | import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService; | 19 | import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService; |
| 20 | import cn.iocoder.dashboard.modules.system.service.permission.SysRoleService; | 20 | import cn.iocoder.dashboard.modules.system.service.permission.SysRoleService; |
| 21 | +import com.google.common.annotations.VisibleForTesting; | ||
| 21 | import com.google.common.collect.ImmutableMap; | 22 | import com.google.common.collect.ImmutableMap; |
| 22 | import lombok.extern.slf4j.Slf4j; | 23 | import lombok.extern.slf4j.Slf4j; |
| 23 | import org.springframework.lang.Nullable; | 24 | import org.springframework.lang.Nullable; |
| @@ -58,7 +59,7 @@ public class SysRoleServiceImpl implements SysRoleService { | @@ -58,7 +59,7 @@ public class SysRoleServiceImpl implements SysRoleService { | ||
| 58 | */ | 59 | */ |
| 59 | private volatile Map<Long, SysRoleDO> roleCache; | 60 | private volatile Map<Long, SysRoleDO> roleCache; |
| 60 | /** | 61 | /** |
| 61 | - * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新 | 62 | + * 缓存角色的最大更新时间,用于后续的增量轮询,判断是否有更新 |
| 62 | */ | 63 | */ |
| 63 | private volatile Date maxUpdateTime; | 64 | private volatile Date maxUpdateTime; |
| 64 | 65 | ||
| @@ -77,7 +78,7 @@ public class SysRoleServiceImpl implements SysRoleService { | @@ -77,7 +78,7 @@ public class SysRoleServiceImpl implements SysRoleService { | ||
| 77 | @Override | 78 | @Override |
| 78 | @PostConstruct | 79 | @PostConstruct |
| 79 | public void initLocalCache() { | 80 | public void initLocalCache() { |
| 80 | - // 获取菜单列表,如果有更新 | 81 | + // 获取角色列表,如果有更新 |
| 81 | List<SysRoleDO> roleList = this.loadRoleIfUpdate(maxUpdateTime); | 82 | List<SysRoleDO> roleList = this.loadRoleIfUpdate(maxUpdateTime); |
| 82 | if (CollUtil.isEmpty(roleList)) { | 83 | if (CollUtil.isEmpty(roleList)) { |
| 83 | return; | 84 | return; |
| @@ -98,23 +99,23 @@ public class SysRoleServiceImpl implements SysRoleService { | @@ -98,23 +99,23 @@ public class SysRoleServiceImpl implements SysRoleService { | ||
| 98 | } | 99 | } |
| 99 | 100 | ||
| 100 | /** | 101 | /** |
| 101 | - * 如果菜单发生变化,从数据库中获取最新的全量菜单。 | 102 | + * 如果角色发生变化,从数据库中获取最新的全量角色。 |
| 102 | * 如果未发生变化,则返回空 | 103 | * 如果未发生变化,则返回空 |
| 103 | * | 104 | * |
| 104 | - * @param maxUpdateTime 当前菜单的最大更新时间 | ||
| 105 | - * @return 菜单列表 | 105 | + * @param maxUpdateTime 当前角色的最大更新时间 |
| 106 | + * @return 角色列表 | ||
| 106 | */ | 107 | */ |
| 107 | private List<SysRoleDO> loadRoleIfUpdate(Date maxUpdateTime) { | 108 | private List<SysRoleDO> loadRoleIfUpdate(Date maxUpdateTime) { |
| 108 | // 第一步,判断是否要更新。 | 109 | // 第一步,判断是否要更新。 |
| 109 | if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据 | 110 | if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据 |
| 110 | - log.info("[loadRoleIfUpdate][首次加载全量菜单]"); | ||
| 111 | - } else { // 判断数据库中是否有更新的菜单 | 111 | + log.info("[loadRoleIfUpdate][首次加载全量角色]"); |
| 112 | + } else { // 判断数据库中是否有更新的角色 | ||
| 112 | if (!roleMapper.selectExistsByUpdateTimeAfter(maxUpdateTime)) { | 113 | if (!roleMapper.selectExistsByUpdateTimeAfter(maxUpdateTime)) { |
| 113 | return null; | 114 | return null; |
| 114 | } | 115 | } |
| 115 | - log.info("[loadRoleIfUpdate][增量加载全量菜单]"); | 116 | + log.info("[loadRoleIfUpdate][增量加载全量角色]"); |
| 116 | } | 117 | } |
| 117 | - // 第二步,如果有更新,则从数据库加载所有菜单 | 118 | + // 第二步,如果有更新,则从数据库加载所有角色 |
| 118 | return roleMapper.selectList(); | 119 | return roleMapper.selectList(); |
| 119 | } | 120 | } |
| 120 | 121 | ||
| @@ -245,7 +246,8 @@ public class SysRoleServiceImpl implements SysRoleService { | @@ -245,7 +246,8 @@ public class SysRoleServiceImpl implements SysRoleService { | ||
| 245 | * @param code 角色额编码 | 246 | * @param code 角色额编码 |
| 246 | * @param id 角色编号 | 247 | * @param id 角色编号 |
| 247 | */ | 248 | */ |
| 248 | - private void checkDuplicateRole(String name, String code, Long id) { | 249 | + @VisibleForTesting |
| 250 | + public void checkDuplicateRole(String name, String code, Long id) { | ||
| 249 | // 1. 该 name 名字被其它角色所使用 | 251 | // 1. 该 name 名字被其它角色所使用 |
| 250 | SysRoleDO role = roleMapper.selectByName(name); | 252 | SysRoleDO role = roleMapper.selectByName(name); |
| 251 | if (role != null && !role.getId().equals(id)) { | 253 | if (role != null && !role.getId().equals(id)) { |
| @@ -258,7 +260,7 @@ public class SysRoleServiceImpl implements SysRoleService { | @@ -258,7 +260,7 @@ public class SysRoleServiceImpl implements SysRoleService { | ||
| 258 | // 该 code 编码被其它角色所使用 | 260 | // 该 code 编码被其它角色所使用 |
| 259 | role = roleMapper.selectByCode(code); | 261 | role = roleMapper.selectByCode(code); |
| 260 | if (role != null && !role.getId().equals(id)) { | 262 | if (role != null && !role.getId().equals(id)) { |
| 261 | - throw ServiceExceptionUtil.exception(ROLE_CODE_DUPLICATE, name); | 263 | + throw ServiceExceptionUtil.exception(ROLE_CODE_DUPLICATE, code); |
| 262 | } | 264 | } |
| 263 | } | 265 | } |
| 264 | 266 | ||
| @@ -267,7 +269,8 @@ public class SysRoleServiceImpl implements SysRoleService { | @@ -267,7 +269,8 @@ public class SysRoleServiceImpl implements SysRoleService { | ||
| 267 | * | 269 | * |
| 268 | * @param id 角色编号 | 270 | * @param id 角色编号 |
| 269 | */ | 271 | */ |
| 270 | - private void checkUpdateRole(Long id) { | 272 | + @VisibleForTesting |
| 273 | + public void checkUpdateRole(Long id) { | ||
| 271 | SysRoleDO roleDO = roleMapper.selectById(id); | 274 | SysRoleDO roleDO = roleMapper.selectById(id); |
| 272 | if (roleDO == null) { | 275 | if (roleDO == null) { |
| 273 | throw ServiceExceptionUtil.exception(ROLE_NOT_EXISTS); | 276 | throw ServiceExceptionUtil.exception(ROLE_NOT_EXISTS); |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsChannelService.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelCreateReqVO; | ||
| 5 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelPageReqVO; | ||
| 6 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.channel.SysSmsChannelUpdateReqVO; | ||
| 7 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO; | ||
| 8 | + | ||
| 9 | +import javax.validation.Valid; | ||
| 10 | +import java.util.Collection; | ||
| 11 | +import java.util.List; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * 短信渠道Service接口 | ||
| 15 | + * | ||
| 16 | + * @author zzf | ||
| 17 | + * @date 2021/1/25 9:24 | ||
| 18 | + */ | ||
| 19 | +public interface SysSmsChannelService { | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 初始化短信客户端 | ||
| 23 | + */ | ||
| 24 | + void initSmsClients(); | ||
| 25 | + | ||
| 26 | + /** | ||
| 27 | + * 创建短信渠道 | ||
| 28 | + * | ||
| 29 | + * @param createReqVO 创建信息 | ||
| 30 | + * @return 编号 | ||
| 31 | + */ | ||
| 32 | + Long createSmsChannel(@Valid SysSmsChannelCreateReqVO createReqVO); | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 更新短信渠道 | ||
| 36 | + * | ||
| 37 | + * @param updateReqVO 更新信息 | ||
| 38 | + */ | ||
| 39 | + void updateSmsChannel(@Valid SysSmsChannelUpdateReqVO updateReqVO); | ||
| 40 | + | ||
| 41 | + /** | ||
| 42 | + * 删除短信渠道 | ||
| 43 | + * | ||
| 44 | + * @param id 编号 | ||
| 45 | + */ | ||
| 46 | + void deleteSmsChannel(Long id); | ||
| 47 | + | ||
| 48 | + /** | ||
| 49 | + * 获得短信渠道 | ||
| 50 | + * | ||
| 51 | + * @param id 编号 | ||
| 52 | + * @return 短信渠道 | ||
| 53 | + */ | ||
| 54 | + SysSmsChannelDO getSmsChannel(Long id); | ||
| 55 | + | ||
| 56 | + /** | ||
| 57 | + * 获得短信渠道列表 | ||
| 58 | + * | ||
| 59 | + * @param ids 编号 | ||
| 60 | + * @return 短信渠道列表 | ||
| 61 | + */ | ||
| 62 | + List<SysSmsChannelDO> getSmsChannelList(Collection<Long> ids); | ||
| 63 | + | ||
| 64 | + /** | ||
| 65 | + * 获得所有短信渠道列表 | ||
| 66 | + * | ||
| 67 | + * @return 短信渠道列表 | ||
| 68 | + */ | ||
| 69 | + List<SysSmsChannelDO> getSmsChannelList(); | ||
| 70 | + | ||
| 71 | + /** | ||
| 72 | + * 获得短信渠道分页 | ||
| 73 | + * | ||
| 74 | + * @param pageReqVO 分页查询 | ||
| 75 | + * @return 短信渠道分页 | ||
| 76 | + */ | ||
| 77 | + PageResult<SysSmsChannelDO> getSmsChannelPage(SysSmsChannelPageReqVO pageReqVO); | ||
| 78 | + | ||
| 79 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsLogService.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogExportReqVO; | ||
| 5 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.log.SysSmsLogPageReqVO; | ||
| 6 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsLogDO; | ||
| 7 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; | ||
| 8 | + | ||
| 9 | +import java.util.Date; | ||
| 10 | +import java.util.List; | ||
| 11 | +import java.util.Map; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * 短信日志 Service 实现类 | ||
| 15 | + * | ||
| 16 | + * @author zzf | ||
| 17 | + * @date 13:48 2021/3/2 | ||
| 18 | + */ | ||
| 19 | +public interface SysSmsLogService { | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 创建短信日志 | ||
| 23 | + * | ||
| 24 | + * @param mobile 手机号 | ||
| 25 | + * @param userId 用户编号 | ||
| 26 | + * @param userType 用户类型 | ||
| 27 | + * @param isSend 是否发送 | ||
| 28 | + * @param template 短信模板 | ||
| 29 | + * @param templateContent 短信内容 | ||
| 30 | + * @param templateParams 短信参数 | ||
| 31 | + * @return 发送日志编号 | ||
| 32 | + */ | ||
| 33 | + Long createSmsLog(String mobile, Long userId, Integer userType, Boolean isSend, | ||
| 34 | + SysSmsTemplateDO template, String templateContent, Map<String, Object> templateParams); | ||
| 35 | + | ||
| 36 | + /** | ||
| 37 | + * 更新日志的发送结果 | ||
| 38 | + * | ||
| 39 | + * @param id 日志编号 | ||
| 40 | + * @param sendCode 发送结果的编码 | ||
| 41 | + * @param sendMsg 发送结果的提示 | ||
| 42 | + * @param apiSendCode 短信 API 发送结果的编码 | ||
| 43 | + * @param apiSendMsg 短信 API 发送失败的提示 | ||
| 44 | + * @param apiRequestId 短信 API 发送返回的唯一请求 ID | ||
| 45 | + * @param apiSerialNo 短信 API 发送返回的序号 | ||
| 46 | + */ | ||
| 47 | + void updateSmsSendResult(Long id, Integer sendCode, String sendMsg, | ||
| 48 | + String apiSendCode, String apiSendMsg, String apiRequestId, String apiSerialNo); | ||
| 49 | + | ||
| 50 | + /** | ||
| 51 | + * 更新日志的接收结果 | ||
| 52 | + * | ||
| 53 | + * @param id 日志编号 | ||
| 54 | + * @param success 是否接收成功 | ||
| 55 | + * @param receiveTime 用户接收时间 | ||
| 56 | + * @param apiReceiveCode API 接收结果的编码 | ||
| 57 | + * @param apiReceiveMsg API 接收结果的说明 | ||
| 58 | + */ | ||
| 59 | + void updateSmsReceiveResult(Long id, Boolean success, Date receiveTime, String apiReceiveCode, String apiReceiveMsg); | ||
| 60 | + | ||
| 61 | + /** | ||
| 62 | + * 获得短信日志分页 | ||
| 63 | + * | ||
| 64 | + * @param pageReqVO 分页查询 | ||
| 65 | + * @return 短信日志分页 | ||
| 66 | + */ | ||
| 67 | + PageResult<SysSmsLogDO> getSmsLogPage(SysSmsLogPageReqVO pageReqVO); | ||
| 68 | + | ||
| 69 | + /** | ||
| 70 | + * 获得短信日志列表, 用于 Excel 导出 | ||
| 71 | + * | ||
| 72 | + * @param exportReqVO 查询条件 | ||
| 73 | + * @return 短信日志列表 | ||
| 74 | + */ | ||
| 75 | + List<SysSmsLogDO> getSmsLogList(SysSmsLogExportReqVO exportReqVO); | ||
| 76 | + | ||
| 77 | +} |
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
| @@ -358,7 +358,7 @@ public class SysUserServiceImpl implements SysUserService { | @@ -358,7 +358,7 @@ public class SysUserServiceImpl implements SysUserService { | ||
| 358 | } | 358 | } |
| 359 | // 如果存在,判断是否允许更新 | 359 | // 如果存在,判断是否允许更新 |
| 360 | if (!isUpdateSupport) { | 360 | if (!isUpdateSupport) { |
| 361 | - respVO.getFailureUsernames().put(importUser.getUsername(), USER_USERNAME_EXISTS.getMessage()); | 361 | + respVO.getFailureUsernames().put(importUser.getUsername(), USER_USERNAME_EXISTS.getMsg()); |
| 362 | return; | 362 | return; |
| 363 | } | 363 | } |
| 364 | SysUserDO updateUser = SysUserConvert.INSTANCE.convert(importUser); | 364 | SysUserDO updateUser = SysUserConvert.INSTANCE.convert(importUser); |
src/main/java/cn/iocoder/dashboard/util/collection/CollectionUtils.java
| @@ -4,8 +4,10 @@ import cn.hutool.core.collection.CollUtil; | @@ -4,8 +4,10 @@ import cn.hutool.core.collection.CollUtil; | ||
| 4 | import cn.hutool.core.collection.CollectionUtil; | 4 | import cn.hutool.core.collection.CollectionUtil; |
| 5 | 5 | ||
| 6 | import java.util.*; | 6 | import java.util.*; |
| 7 | +import java.util.function.BinaryOperator; | ||
| 7 | import java.util.function.Function; | 8 | import java.util.function.Function; |
| 8 | import java.util.function.Predicate; | 9 | import java.util.function.Predicate; |
| 10 | +import java.util.function.Supplier; | ||
| 9 | import java.util.stream.Collectors; | 11 | import java.util.stream.Collectors; |
| 10 | 12 | ||
| 11 | /** | 13 | /** |
| @@ -30,6 +32,20 @@ public class CollectionUtils { | @@ -30,6 +32,20 @@ public class CollectionUtils { | ||
| 30 | return from.stream().filter(predicate).collect(Collectors.toList()); | 32 | return from.stream().filter(predicate).collect(Collectors.toList()); |
| 31 | } | 33 | } |
| 32 | 34 | ||
| 35 | + public static <T, R> List<T> distinct(Collection<T> from, Function<T, R> keyMapper) { | ||
| 36 | + if (CollUtil.isEmpty(from)) { | ||
| 37 | + return new ArrayList<>(); | ||
| 38 | + } | ||
| 39 | + return distinct(from, keyMapper, (t1, t2) -> t1); | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + public static <T, R> List<T> distinct(Collection<T> from, Function<T, R> keyMapper, BinaryOperator<T> cover) { | ||
| 43 | + if (CollUtil.isEmpty(from)) { | ||
| 44 | + return new ArrayList<>(); | ||
| 45 | + } | ||
| 46 | + return new ArrayList<>(convertMap(from, keyMapper, Function.identity(), cover).values()); | ||
| 47 | + } | ||
| 48 | + | ||
| 33 | public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func) { | 49 | public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func) { |
| 34 | if (CollUtil.isEmpty(from)) { | 50 | if (CollUtil.isEmpty(from)) { |
| 35 | return new ArrayList<>(); | 51 | return new ArrayList<>(); |
| @@ -48,30 +64,57 @@ public class CollectionUtils { | @@ -48,30 +64,57 @@ public class CollectionUtils { | ||
| 48 | if (CollUtil.isEmpty(from)) { | 64 | if (CollUtil.isEmpty(from)) { |
| 49 | return new HashMap<>(); | 65 | return new HashMap<>(); |
| 50 | } | 66 | } |
| 51 | - return from.stream().collect(Collectors.toMap(keyFunc, item -> item)); | 67 | + return convertMap(from, keyFunc, Function.identity()); |
| 68 | + } | ||
| 69 | + | ||
| 70 | + public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc, Supplier<? extends Map<K, T>> supplier) { | ||
| 71 | + if (CollUtil.isEmpty(from)) { | ||
| 72 | + return supplier.get(); | ||
| 73 | + } | ||
| 74 | + return convertMap(from, keyFunc, Function.identity(), supplier); | ||
| 52 | } | 75 | } |
| 53 | 76 | ||
| 54 | public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) { | 77 | public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) { |
| 55 | if (CollUtil.isEmpty(from)) { | 78 | if (CollUtil.isEmpty(from)) { |
| 56 | return new HashMap<>(); | 79 | return new HashMap<>(); |
| 57 | } | 80 | } |
| 58 | - return from.stream().collect(Collectors.toMap(keyFunc, valueFunc)); | 81 | + return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1); |
| 82 | + } | ||
| 83 | + | ||
| 84 | + public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction) { | ||
| 85 | + if (CollUtil.isEmpty(from)) { | ||
| 86 | + return new HashMap<>(); | ||
| 87 | + } | ||
| 88 | + return convertMap(from, keyFunc, valueFunc, mergeFunction, HashMap::new); | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, Supplier<? extends Map<K, V>> supplier) { | ||
| 92 | + if (CollUtil.isEmpty(from)) { | ||
| 93 | + return supplier.get(); | ||
| 94 | + } | ||
| 95 | + return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1, supplier); | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction, Supplier<? extends Map<K, V>> supplier) { | ||
| 99 | + if (CollUtil.isEmpty(from)) { | ||
| 100 | + return new HashMap<>(); | ||
| 101 | + } | ||
| 102 | + return from.stream().collect(Collectors.toMap(keyFunc, valueFunc, mergeFunction, supplier)); | ||
| 59 | } | 103 | } |
| 60 | 104 | ||
| 61 | public static <T, K> Map<K, List<T>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc) { | 105 | public static <T, K> Map<K, List<T>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc) { |
| 62 | if (CollUtil.isEmpty(from)) { | 106 | if (CollUtil.isEmpty(from)) { |
| 63 | return new HashMap<>(); | 107 | return new HashMap<>(); |
| 64 | } | 108 | } |
| 65 | - return from.stream().collect(Collectors.groupingBy(keyFunc, | ||
| 66 | - Collectors.mapping(t -> t, Collectors.toList()))); | 109 | + return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(t -> t, Collectors.toList()))); |
| 67 | } | 110 | } |
| 68 | 111 | ||
| 69 | public static <T, K, V> Map<K, List<V>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) { | 112 | public static <T, K, V> Map<K, List<V>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) { |
| 70 | if (CollUtil.isEmpty(from)) { | 113 | if (CollUtil.isEmpty(from)) { |
| 71 | return new HashMap<>(); | 114 | return new HashMap<>(); |
| 72 | } | 115 | } |
| 73 | - return from.stream().collect(Collectors.groupingBy(keyFunc, | ||
| 74 | - Collectors.mapping(valueFunc, Collectors.toList()))); | 116 | + return from.stream() |
| 117 | + .collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toList()))); | ||
| 75 | } | 118 | } |
| 76 | 119 | ||
| 77 | // 暂时没想好名字,先以 2 结尾噶 | 120 | // 暂时没想好名字,先以 2 结尾噶 |
src/main/java/cn/iocoder/dashboard/util/collection/MapUtils.java
| @@ -2,6 +2,8 @@ package cn.iocoder.dashboard.util.collection; | @@ -2,6 +2,8 @@ package cn.iocoder.dashboard.util.collection; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | import cn.hutool.core.collection.CollectionUtil; | 4 | import cn.hutool.core.collection.CollectionUtil; |
| 5 | +import cn.iocoder.dashboard.common.core.KeyValue; | ||
| 6 | +import com.google.common.collect.Maps; | ||
| 5 | import com.google.common.collect.Multimap; | 7 | import com.google.common.collect.Multimap; |
| 6 | 8 | ||
| 7 | import java.util.ArrayList; | 9 | import java.util.ArrayList; |
| @@ -55,4 +57,10 @@ public class MapUtils { | @@ -55,4 +57,10 @@ public class MapUtils { | ||
| 55 | consumer.accept(value); | 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,6 +9,11 @@ import java.util.Date; | ||
| 9 | */ | 9 | */ |
| 10 | public class DateUtils { | 10 | public class DateUtils { |
| 11 | 11 | ||
| 12 | + /** | ||
| 13 | + * 时区 - 默认 | ||
| 14 | + */ | ||
| 15 | + public static final String TIME_ZONE_DEFAULT = "GMT+8"; | ||
| 16 | + | ||
| 12 | public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss"; | 17 | public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss"; |
| 13 | 18 | ||
| 14 | public static Date addTime(Duration duration) { | 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 +7,8 @@ import com.fasterxml.jackson.core.type.TypeReference; | ||
| 7 | import com.fasterxml.jackson.databind.ObjectMapper; | 7 | import com.fasterxml.jackson.databind.ObjectMapper; |
| 8 | 8 | ||
| 9 | import java.io.IOException; | 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 | * JSON 工具类 | 14 | * JSON 工具类 |
| @@ -20,7 +21,7 @@ public class JsonUtils { | @@ -20,7 +21,7 @@ public class JsonUtils { | ||
| 20 | 21 | ||
| 21 | /** | 22 | /** |
| 22 | * 初始化 objectMapper 属性 | 23 | * 初始化 objectMapper 属性 |
| 23 | - * | 24 | + * <p> |
| 24 | * 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean | 25 | * 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean |
| 25 | * | 26 | * |
| 26 | * @param objectMapper ObjectMapper 对象 | 27 | * @param objectMapper ObjectMapper 对象 |
| @@ -59,7 +60,7 @@ public class JsonUtils { | @@ -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 | try { | 64 | try { |
| 64 | return objectMapper.readValue(text, typeReference); | 65 | return objectMapper.readValue(text, typeReference); |
| 65 | } catch (IOException e) { | 66 | } catch (IOException e) { |
| @@ -67,4 +68,15 @@ public class JsonUtils { | @@ -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 | package cn.iocoder.dashboard.util.string; | 1 | package cn.iocoder.dashboard.util.string; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.util.ObjectUtil; | ||
| 3 | import cn.hutool.core.util.StrUtil; | 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,4 +16,22 @@ public class StrUtils { | ||
| 13 | return StrUtil.maxLength(str, maxLength - 3); // -3 的原因,是该方法会补充 ... 恰好 | 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/codegen/java/service/service.vm
| @@ -63,7 +63,7 @@ public interface ${table.className}Service { | @@ -63,7 +63,7 @@ public interface ${table.className}Service { | ||
| 63 | * 获得${table.classComment}列表, 用于 Excel 导出 | 63 | * 获得${table.classComment}列表, 用于 Excel 导出 |
| 64 | * | 64 | * |
| 65 | * @param exportReqVO 查询条件 | 65 | * @param exportReqVO 查询条件 |
| 66 | - * @return ${table.classComment}分页 | 66 | + * @return ${table.classComment}列表 |
| 67 | */ | 67 | */ |
| 68 | List<${table.className}DO> get${simpleClassName}List(${table.className}ExportReqVO exportReqVO); | 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,7 +76,7 @@ | ||
| 76 | <template slot-scope="scope"> | 76 | <template slot-scope="scope"> |
| 77 | <span>{{ getDictDataLabel(DICT_TYPE.$dictType.toUpperCase(), scope.row.${column.javaField}) }}</span> | 77 | <span>{{ getDictDataLabel(DICT_TYPE.$dictType.toUpperCase(), scope.row.${column.javaField}) }}</span> |
| 78 | </template> | 78 | </template> |
| 79 | - </el-table-column>> | 79 | + </el-table-column> |
| 80 | #else | 80 | #else |
| 81 | <el-table-column label="${comment}" align="center" prop="${javaField}" /> | 81 | <el-table-column label="${comment}" align="center" prop="${javaField}" /> |
| 82 | #end | 82 | #end |
| @@ -137,7 +137,7 @@ | @@ -137,7 +137,7 @@ | ||
| 137 | </el-select> | 137 | </el-select> |
| 138 | </el-form-item> | 138 | </el-form-item> |
| 139 | #elseif($column.htmlType == "checkbox")## 多选框 | 139 | #elseif($column.htmlType == "checkbox")## 多选框 |
| 140 | - <el-form-item label="${comment}"> | 140 | + <el-form-item label="${comment}" prop="${javaField}"> |
| 141 | <el-checkbox-group v-model="form.${javaField}"> | 141 | <el-checkbox-group v-model="form.${javaField}"> |
| 142 | #if ("" != $dictType)## 有数据字典 | 142 | #if ("" != $dictType)## 有数据字典 |
| 143 | <el-checkbox v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())" | 143 | <el-checkbox v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())" |
| @@ -148,7 +148,7 @@ | @@ -148,7 +148,7 @@ | ||
| 148 | </el-checkbox-group> | 148 | </el-checkbox-group> |
| 149 | </el-form-item> | 149 | </el-form-item> |
| 150 | #elseif($column.htmlType == "radio")## 单选框 | 150 | #elseif($column.htmlType == "radio")## 单选框 |
| 151 | - <el-form-item label="${comment}"> | 151 | + <el-form-item label="${comment}" prop="${javaField}"> |
| 152 | <el-radio-group v-model="form.${javaField}"> | 152 | <el-radio-group v-model="form.${javaField}"> |
| 153 | #if ("" != $dictType)## 有数据字典 | 153 | #if ("" != $dictType)## 有数据字典 |
| 154 | <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())" | 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,7 +43,7 @@ public class RedisStreamTest { | ||
| 43 | for (int i = 0; i < 100; i++) { | 43 | for (int i = 0; i < 100; i++) { |
| 44 | // 创建消息 | 44 | // 创建消息 |
| 45 | SysSmsSendMessage message = new SysSmsSendMessage(); | 45 | SysSmsSendMessage message = new SysSmsSendMessage(); |
| 46 | - message.setMobile("15601691300").setTemplateCode("test:" + i); | 46 | + message.setMobile("15601691300").setApiTemplateId("test:" + i); |
| 47 | // 发送消息 | 47 | // 发送消息 |
| 48 | RedisMessageUtils.sendStreamMessage(stringRedisTemplate, message); | 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/resources/application-integration-test.yaml
| @@ -9,19 +9,15 @@ spring: | @@ -9,19 +9,15 @@ spring: | ||
| 9 | # 数据源配置项 | 9 | # 数据源配置项 |
| 10 | datasource: | 10 | datasource: |
| 11 | name: ruoyi-vue-pro | 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 | # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 | 17 | # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 |
| 22 | redis: | 18 | redis: |
| 23 | host: 127.0.0.1 # 地址 | 19 | host: 127.0.0.1 # 地址 |
| 24 | - port: 6379 # 端口(单元测试,使用 16379 端口) | 20 | + port: 6379 # 端口 |
| 25 | database: 0 # 数据库索引 | 21 | database: 0 # 数据库索引 |
| 26 | 22 | ||
| 27 | mybatis: | 23 | mybatis: |
src/test/java/cn/iocoder/dashboard/BaseMockitoUnitTest.java
0 → 100644
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/dept/SysDeptServiceTest.java
| @@ -270,4 +270,5 @@ class SysDeptServiceTest extends BaseDbUnitTest { | @@ -270,4 +270,5 @@ class SysDeptServiceTest extends BaseDbUnitTest { | ||
| 270 | }; | 270 | }; |
| 271 | return randomPojo(SysDeptDO.class, ArrayUtils.append(consumer, consumers)); | 271 | return randomPojo(SysDeptDO.class, ArrayUtils.append(consumer, consumers)); |
| 272 | } | 272 | } |
| 273 | + | ||
| 273 | } | 274 | } |
src/test/java/cn/iocoder/dashboard/modules/system/service/permission/SysMenuServiceTest.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.permission; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.bean.BeanUtil; | ||
| 4 | +import cn.hutool.core.lang.Assert; | ||
| 5 | +import cn.iocoder.dashboard.BaseDbUnitTest; | ||
| 6 | +import cn.iocoder.dashboard.common.enums.CommonStatusEnum; | ||
| 7 | +import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuCreateReqVO; | ||
| 8 | +import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuListReqVO; | ||
| 9 | +import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuUpdateReqVO; | ||
| 10 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.permission.SysMenuDO; | ||
| 11 | +import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysMenuMapper; | ||
| 12 | +import cn.iocoder.dashboard.modules.system.enums.permission.MenuTypeEnum; | ||
| 13 | +import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysMenuProducer; | ||
| 14 | +import cn.iocoder.dashboard.modules.system.service.permission.impl.SysMenuServiceImpl; | ||
| 15 | +import cn.iocoder.dashboard.util.AopTargetUtils; | ||
| 16 | +import cn.iocoder.dashboard.util.RandomUtils; | ||
| 17 | +import cn.iocoder.dashboard.util.object.ObjectUtils; | ||
| 18 | +import com.google.common.collect.Multimap; | ||
| 19 | +import org.junit.jupiter.api.Assertions; | ||
| 20 | +import org.junit.jupiter.api.Test; | ||
| 21 | +import org.springframework.boot.test.mock.mockito.MockBean; | ||
| 22 | +import org.springframework.context.annotation.Import; | ||
| 23 | + | ||
| 24 | +import javax.annotation.Resource; | ||
| 25 | +import java.util.*; | ||
| 26 | + | ||
| 27 | +import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; | ||
| 28 | +import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals; | ||
| 29 | +import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException; | ||
| 30 | +import static cn.iocoder.dashboard.util.RandomUtils.*; | ||
| 31 | +import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| 32 | +import static org.mockito.Mockito.verify; | ||
| 33 | + | ||
| 34 | +@Import(SysMenuServiceImpl.class) | ||
| 35 | +public class SysMenuServiceTest extends BaseDbUnitTest { | ||
| 36 | + | ||
| 37 | + @Resource | ||
| 38 | + private SysMenuServiceImpl sysMenuService; | ||
| 39 | + | ||
| 40 | + @MockBean | ||
| 41 | + private SysPermissionService sysPermissionService; | ||
| 42 | + | ||
| 43 | + @MockBean | ||
| 44 | + private SysMenuProducer sysMenuProducer; | ||
| 45 | + | ||
| 46 | + @Resource | ||
| 47 | + private SysMenuMapper menuMapper; | ||
| 48 | + | ||
| 49 | + @Test | ||
| 50 | + public void testInitLocalCache_success() throws Exception { | ||
| 51 | + SysMenuDO menuDO1 = createMenuDO(MenuTypeEnum.MENU, "xxxx", 0L); | ||
| 52 | + menuMapper.insert(menuDO1); | ||
| 53 | + SysMenuDO menuDO2 = createMenuDO(MenuTypeEnum.MENU, "xxxx", 0L); | ||
| 54 | + menuMapper.insert(menuDO2); | ||
| 55 | + | ||
| 56 | + // 调用 | ||
| 57 | + sysMenuService.initLocalCache(); | ||
| 58 | + | ||
| 59 | + // 获取代理对象 | ||
| 60 | + SysMenuServiceImpl target = (SysMenuServiceImpl) AopTargetUtils.getTarget(sysMenuService); | ||
| 61 | + | ||
| 62 | + Map<Long, SysMenuDO> menuCache = | ||
| 63 | + (Map<Long, SysMenuDO>) BeanUtil.getFieldValue(target, "menuCache"); | ||
| 64 | + Assert.isTrue(menuCache.size() == 2); | ||
| 65 | + assertPojoEquals(menuDO1, menuCache.get(menuDO1.getId())); | ||
| 66 | + assertPojoEquals(menuDO2, menuCache.get(menuDO2.getId())); | ||
| 67 | + | ||
| 68 | + Multimap<String, SysMenuDO> permissionMenuCache = | ||
| 69 | + (Multimap<String, SysMenuDO>) BeanUtil.getFieldValue(target, "permissionMenuCache"); | ||
| 70 | + Assert.isTrue(permissionMenuCache.size() == 2); | ||
| 71 | + assertPojoEquals(menuDO1, permissionMenuCache.get(menuDO1.getPermission())); | ||
| 72 | + assertPojoEquals(menuDO2, permissionMenuCache.get(menuDO2.getPermission())); | ||
| 73 | + | ||
| 74 | + Date maxUpdateTime = (Date) BeanUtil.getFieldValue(target, "maxUpdateTime"); | ||
| 75 | + assertEquals(ObjectUtils.max(menuDO1.getUpdateTime(), menuDO2.getUpdateTime()), maxUpdateTime); | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + @Test | ||
| 79 | + public void testCreateMenu_success() { | ||
| 80 | + //构造父目录 | ||
| 81 | + SysMenuDO menuDO = createMenuDO(MenuTypeEnum.MENU, "parent", 0L); | ||
| 82 | + menuMapper.insert(menuDO); | ||
| 83 | + Long parentId = menuDO.getId(); | ||
| 84 | + | ||
| 85 | + //调用 | ||
| 86 | + SysMenuCreateReqVO vo = randomPojo(SysMenuCreateReqVO.class, o -> { | ||
| 87 | + o.setParentId(parentId); | ||
| 88 | + o.setName("testSonName"); | ||
| 89 | + o.setType(MenuTypeEnum.MENU.getType()); | ||
| 90 | + o.setStatus(RandomUtils.randomCommonStatus()); | ||
| 91 | + }); | ||
| 92 | + Long menuId = sysMenuService.createMenu(vo); | ||
| 93 | + | ||
| 94 | + //断言 | ||
| 95 | + Assertions.assertNotNull(menuId); | ||
| 96 | + // 校验记录的属性是否正确 | ||
| 97 | + SysMenuDO ret = menuMapper.selectById(menuId); | ||
| 98 | + assertPojoEquals(vo, ret); | ||
| 99 | + // 校验调用 | ||
| 100 | + verify(sysMenuProducer).sendMenuRefreshMessage(); | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + @Test | ||
| 104 | + public void testUpdateMenu_success() { | ||
| 105 | + //构造父子目录 | ||
| 106 | + SysMenuDO sonMenuDO = initParentAndSonMenuDO(); | ||
| 107 | + Long sonId = sonMenuDO.getId(); | ||
| 108 | + Long parentId = sonMenuDO.getParentId(); | ||
| 109 | + | ||
| 110 | + //调用 | ||
| 111 | + SysMenuUpdateReqVO vo = RandomUtils.randomPojo(SysMenuUpdateReqVO.class, o -> { | ||
| 112 | + o.setId(sonId); | ||
| 113 | + o.setParentId(parentId); | ||
| 114 | + o.setType(MenuTypeEnum.MENU.getType()); | ||
| 115 | + o.setStatus(RandomUtils.randomCommonStatus()); | ||
| 116 | + o.setName("pppppp"); //修改名字 | ||
| 117 | + }); | ||
| 118 | + sysMenuService.updateMenu(vo); | ||
| 119 | + | ||
| 120 | + //断言 | ||
| 121 | + // 校验记录的属性是否正确 | ||
| 122 | + SysMenuDO ret = menuMapper.selectById(sonId); | ||
| 123 | + assertPojoEquals(vo, ret); | ||
| 124 | + // 校验调用 | ||
| 125 | + verify(sysMenuProducer).sendMenuRefreshMessage(); | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + @Test | ||
| 129 | + public void testUpdateMenu_sonIdNotExist() { | ||
| 130 | + Long sonId = 99999L; | ||
| 131 | + Long parentId = 10000L; | ||
| 132 | + | ||
| 133 | + //调用 | ||
| 134 | + SysMenuUpdateReqVO vo = RandomUtils.randomPojo(SysMenuUpdateReqVO.class, o -> { | ||
| 135 | + o.setId(sonId); | ||
| 136 | + o.setParentId(parentId); | ||
| 137 | + o.setType(MenuTypeEnum.MENU.getType()); | ||
| 138 | + o.setStatus(RandomUtils.randomCommonStatus()); | ||
| 139 | + }); | ||
| 140 | + //断言 | ||
| 141 | + assertServiceException(() -> sysMenuService.updateMenu(vo), MENU_NOT_EXISTS); | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + @Test | ||
| 145 | + public void testDeleteMenu_success() { | ||
| 146 | + SysMenuDO sonMenuDO = initParentAndSonMenuDO(); | ||
| 147 | + Long sonId = sonMenuDO.getId(); | ||
| 148 | + | ||
| 149 | + //调用 | ||
| 150 | + sysMenuService.deleteMenu(sonId); | ||
| 151 | + | ||
| 152 | + //断言 | ||
| 153 | + SysMenuDO menuDO = menuMapper.selectById(sonId); | ||
| 154 | + Assert.isNull(menuDO); | ||
| 155 | + verify(sysPermissionService).processMenuDeleted(sonId); | ||
| 156 | + verify(sysMenuProducer).sendMenuRefreshMessage(); | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | + @Test | ||
| 160 | + public void testDeleteMenu_menuNotExist() { | ||
| 161 | + Long sonId = 99999L; | ||
| 162 | + | ||
| 163 | + assertServiceException(() -> sysMenuService.deleteMenu(sonId), MENU_NOT_EXISTS); | ||
| 164 | + } | ||
| 165 | + | ||
| 166 | + @Test | ||
| 167 | + public void testDeleteMenu_existChildren() { | ||
| 168 | + SysMenuDO sonMenu = initParentAndSonMenuDO(); | ||
| 169 | + Long parentId = sonMenu.getParentId(); | ||
| 170 | + | ||
| 171 | + assertServiceException(() -> sysMenuService.deleteMenu(parentId), MENU_EXISTS_CHILDREN); | ||
| 172 | + } | ||
| 173 | + | ||
| 174 | + @Test | ||
| 175 | + public void testGetMenus_success() { | ||
| 176 | + Map<Long, SysMenuDO> idMenuMap = new HashMap<>(); | ||
| 177 | + SysMenuDO menuDO = createMenuDO(MenuTypeEnum.MENU, "parent", 0L); | ||
| 178 | + menuMapper.insert(menuDO); | ||
| 179 | + idMenuMap.put(menuDO.getId(), menuDO); | ||
| 180 | + | ||
| 181 | + SysMenuDO sonMenu = createMenuDO(MenuTypeEnum.MENU, "son", menuDO.getId()); | ||
| 182 | + menuMapper.insert(sonMenu); | ||
| 183 | + idMenuMap.put(sonMenu.getId(), sonMenu); | ||
| 184 | + | ||
| 185 | + //调用 | ||
| 186 | + List<SysMenuDO> menuDOS = sysMenuService.getMenus(); | ||
| 187 | + | ||
| 188 | + //断言 | ||
| 189 | + Assert.isTrue(menuDOS.size() == idMenuMap.size()); | ||
| 190 | + menuDOS.forEach(m -> assertPojoEquals(idMenuMap.get(m.getId()), m)); | ||
| 191 | + } | ||
| 192 | + | ||
| 193 | + @Test | ||
| 194 | + public void testGetMenusReqVo_success() { | ||
| 195 | + Map<Long, SysMenuDO> idMenuMap = new HashMap<>(); | ||
| 196 | + //用于验证可以模糊搜索名称包含"name",状态为1的menu | ||
| 197 | + SysMenuDO menu = createMenuDO(MenuTypeEnum.MENU, "name2", 0L, 1); | ||
| 198 | + menuMapper.insert(menu); | ||
| 199 | + idMenuMap.put(menu.getId(), menu); | ||
| 200 | + | ||
| 201 | + menu = createMenuDO(MenuTypeEnum.MENU, "11name111", 0L, 1); | ||
| 202 | + menuMapper.insert(menu); | ||
| 203 | + idMenuMap.put(menu.getId(), menu); | ||
| 204 | + | ||
| 205 | + menu = createMenuDO(MenuTypeEnum.MENU, "name", 0L, 1); | ||
| 206 | + menuMapper.insert(menu); | ||
| 207 | + idMenuMap.put(menu.getId(), menu); | ||
| 208 | + | ||
| 209 | + //以下是不符合搜索条件的的menu | ||
| 210 | + menu = createMenuDO(MenuTypeEnum.MENU, "xxxxxx", 0L, 1); | ||
| 211 | + menuMapper.insert(menu); | ||
| 212 | + menu = createMenuDO(MenuTypeEnum.MENU, "name", 0L, 2); | ||
| 213 | + menuMapper.insert(menu); | ||
| 214 | + | ||
| 215 | + //调用 | ||
| 216 | + SysMenuListReqVO reqVO = new SysMenuListReqVO(); | ||
| 217 | + reqVO.setStatus(1); | ||
| 218 | + reqVO.setName("name"); | ||
| 219 | + List<SysMenuDO> menuDOS = sysMenuService.getMenus(reqVO); | ||
| 220 | + | ||
| 221 | + //断言 | ||
| 222 | + Assert.isTrue(menuDOS.size() == idMenuMap.size()); | ||
| 223 | + menuDOS.forEach(m -> assertPojoEquals(idMenuMap.get(m.getId()), m)); | ||
| 224 | + } | ||
| 225 | + | ||
| 226 | + @Test | ||
| 227 | + public void testListMenusFromCache_success() throws Exception { | ||
| 228 | + Map<Long, SysMenuDO> mockCacheMap = new HashMap<>(); | ||
| 229 | + //获取代理对象 | ||
| 230 | + SysMenuServiceImpl target = (SysMenuServiceImpl) AopTargetUtils.getTarget(sysMenuService); | ||
| 231 | + BeanUtil.setFieldValue(target, "menuCache", mockCacheMap); | ||
| 232 | + | ||
| 233 | + Map<Long, SysMenuDO> idMenuMap = new HashMap<>(); | ||
| 234 | + //用于验证搜索类型为MENU,状态为1的menu | ||
| 235 | + SysMenuDO menuDO = createMenuDO(1L, MenuTypeEnum.MENU, "name", 0L, 1); | ||
| 236 | + mockCacheMap.put(menuDO.getId(), menuDO); | ||
| 237 | + idMenuMap.put(menuDO.getId(), menuDO); | ||
| 238 | + | ||
| 239 | + menuDO = createMenuDO(2L, MenuTypeEnum.MENU, "name", 0L, 1); | ||
| 240 | + mockCacheMap.put(menuDO.getId(), menuDO); | ||
| 241 | + idMenuMap.put(menuDO.getId(), menuDO); | ||
| 242 | + | ||
| 243 | + //以下是不符合搜索条件的menu | ||
| 244 | + menuDO = createMenuDO(3L, MenuTypeEnum.BUTTON, "name", 0L, 1); | ||
| 245 | + mockCacheMap.put(menuDO.getId(), menuDO); | ||
| 246 | + menuDO = createMenuDO(4L, MenuTypeEnum.MENU, "name", 0L, 2); | ||
| 247 | + mockCacheMap.put(menuDO.getId(), menuDO); | ||
| 248 | + | ||
| 249 | + List<SysMenuDO> menuDOS = sysMenuService.listMenusFromCache(Collections.singletonList(MenuTypeEnum.MENU.getType()), | ||
| 250 | + Collections.singletonList(CommonStatusEnum.DISABLE.getStatus())); | ||
| 251 | + Assert.isTrue(menuDOS.size() == idMenuMap.size()); | ||
| 252 | + menuDOS.forEach(m -> assertPojoEquals(idMenuMap.get(m.getId()), m)); | ||
| 253 | + } | ||
| 254 | + | ||
| 255 | + @Test | ||
| 256 | + public void testListMenusFromCache2_success() throws Exception { | ||
| 257 | + Map<Long, SysMenuDO> mockCacheMap = new HashMap<>(); | ||
| 258 | + //获取代理对象 | ||
| 259 | + SysMenuServiceImpl target = (SysMenuServiceImpl) AopTargetUtils.getTarget(sysMenuService); | ||
| 260 | + BeanUtil.setFieldValue(target, "menuCache", mockCacheMap); | ||
| 261 | + | ||
| 262 | + Map<Long, SysMenuDO> idMenuMap = new HashMap<>(); | ||
| 263 | + //验证搜索id为1, 类型为MENU, 状态为1 的menu | ||
| 264 | + SysMenuDO menuDO = createMenuDO(1L, MenuTypeEnum.MENU, "name", 0L, 1); | ||
| 265 | + mockCacheMap.put(menuDO.getId(), menuDO); | ||
| 266 | + idMenuMap.put(menuDO.getId(), menuDO); | ||
| 267 | + | ||
| 268 | + //以下是不符合搜索条件的menu | ||
| 269 | + menuDO = createMenuDO(2L, MenuTypeEnum.MENU, "name", 0L, 1); | ||
| 270 | + mockCacheMap.put(menuDO.getId(), menuDO); | ||
| 271 | + menuDO = createMenuDO(3L, MenuTypeEnum.BUTTON, "name", 0L, 1); | ||
| 272 | + mockCacheMap.put(menuDO.getId(), menuDO); | ||
| 273 | + menuDO = createMenuDO(4L, MenuTypeEnum.MENU, "name", 0L, 2); | ||
| 274 | + mockCacheMap.put(menuDO.getId(), menuDO); | ||
| 275 | + | ||
| 276 | + List<SysMenuDO> menuDOS = sysMenuService.listMenusFromCache(Collections.singletonList(1L), | ||
| 277 | + Collections.singletonList(MenuTypeEnum.MENU.getType()), Collections.singletonList(1)); | ||
| 278 | + Assert.isTrue(menuDOS.size() == idMenuMap.size()); | ||
| 279 | + menuDOS.forEach(menu -> assertPojoEquals(idMenuMap.get(menu.getId()), menu)); | ||
| 280 | + } | ||
| 281 | + | ||
| 282 | + @Test | ||
| 283 | + public void testCheckParentResource_success() { | ||
| 284 | + SysMenuDO menuDO = createMenuDO(MenuTypeEnum.MENU, "parent", 0L); | ||
| 285 | + menuMapper.insert(menuDO); | ||
| 286 | + Long parentId = menuDO.getId(); | ||
| 287 | + | ||
| 288 | + sysMenuService.checkParentResource(parentId, null); | ||
| 289 | + } | ||
| 290 | + | ||
| 291 | + @Test | ||
| 292 | + public void testCheckParentResource_canNotSetSelfToBeParent() { | ||
| 293 | + assertServiceException(() -> sysMenuService.checkParentResource(1L, 1L), MENU_PARENT_ERROR); | ||
| 294 | + } | ||
| 295 | + | ||
| 296 | + @Test | ||
| 297 | + public void testCheckParentResource_parentNotExist() { | ||
| 298 | + assertServiceException(() -> sysMenuService.checkParentResource(randomLongId(), null), MENU_PARENT_NOT_EXISTS); | ||
| 299 | + } | ||
| 300 | + | ||
| 301 | + @Test | ||
| 302 | + public void testCheckParentResource_parentTypeError() { | ||
| 303 | + SysMenuDO menuDO = createMenuDO(MenuTypeEnum.BUTTON, "parent", 0L); | ||
| 304 | + menuMapper.insert(menuDO); | ||
| 305 | + Long parentId = menuDO.getId(); | ||
| 306 | + | ||
| 307 | + assertServiceException(() -> sysMenuService.checkParentResource(parentId, null), MENU_PARENT_NOT_DIR_OR_MENU); | ||
| 308 | + } | ||
| 309 | + | ||
| 310 | + @Test | ||
| 311 | + public void testCheckResource_success() { | ||
| 312 | + SysMenuDO sonMenu = initParentAndSonMenuDO(); | ||
| 313 | + Long parentId = sonMenu.getParentId(); | ||
| 314 | + | ||
| 315 | + Long otherSonMenuId = randomLongId(); | ||
| 316 | + String otherSonMenuName = randomString(); | ||
| 317 | + | ||
| 318 | + sysMenuService.checkResource(parentId, otherSonMenuName, otherSonMenuId); | ||
| 319 | + } | ||
| 320 | + | ||
| 321 | + @Test | ||
| 322 | + public void testCheckResource_sonMenuNameDuplicate(){ | ||
| 323 | + SysMenuDO sonMenu=initParentAndSonMenuDO(); | ||
| 324 | + Long parentId=sonMenu.getParentId(); | ||
| 325 | + | ||
| 326 | + Long otherSonMenuId=randomLongId(); | ||
| 327 | + String otherSonMenuName=sonMenu.getName(); //相同名称 | ||
| 328 | + | ||
| 329 | + assertServiceException(() -> sysMenuService.checkResource(parentId, otherSonMenuName, otherSonMenuId), MENU_NAME_DUPLICATE); | ||
| 330 | + } | ||
| 331 | + | ||
| 332 | + /** | ||
| 333 | + * 构造父子目录,返回子目录 | ||
| 334 | + * | ||
| 335 | + * @return | ||
| 336 | + */ | ||
| 337 | + private SysMenuDO initParentAndSonMenuDO() { | ||
| 338 | + //构造父子目录 | ||
| 339 | + SysMenuDO menuDO = createMenuDO(MenuTypeEnum.MENU, "parent", 0L); | ||
| 340 | + menuMapper.insert(menuDO); | ||
| 341 | + Long parentId = menuDO.getId(); | ||
| 342 | + | ||
| 343 | + SysMenuDO sonMenuDO = createMenuDO(MenuTypeEnum.MENU, "testSonName", parentId); | ||
| 344 | + menuMapper.insert(sonMenuDO); | ||
| 345 | + return sonMenuDO; | ||
| 346 | + } | ||
| 347 | + | ||
| 348 | + private SysMenuDO createMenuDO(MenuTypeEnum typeEnum, String menuName, Long parentId) { | ||
| 349 | + return createMenuDO(typeEnum, menuName, parentId, RandomUtils.randomCommonStatus()); | ||
| 350 | + } | ||
| 351 | + | ||
| 352 | + private SysMenuDO createMenuDO(MenuTypeEnum typeEnum, String menuName, Long parentId, Integer status) { | ||
| 353 | + return createMenuDO(null, typeEnum, menuName, parentId, status); | ||
| 354 | + } | ||
| 355 | + | ||
| 356 | + private SysMenuDO createMenuDO(Long id, MenuTypeEnum typeEnum, String menuName, Long parentId, Integer status) { | ||
| 357 | + return RandomUtils.randomPojo(SysMenuDO.class, o -> { | ||
| 358 | + o.setId(id); | ||
| 359 | + o.setParentId(parentId); | ||
| 360 | + o.setType(typeEnum.getType()); | ||
| 361 | + o.setStatus(status); | ||
| 362 | + o.setName(menuName); | ||
| 363 | + }); | ||
| 364 | + } | ||
| 365 | + | ||
| 366 | +} |
src/test/java/cn/iocoder/dashboard/modules/system/service/permission/SysRoleServiceTest.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.permission; | ||
| 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.security.core.enums.DataScopeEnum; | ||
| 8 | +import cn.iocoder.dashboard.modules.system.controller.permission.vo.role.SysRoleCreateReqVO; | ||
| 9 | +import cn.iocoder.dashboard.modules.system.controller.permission.vo.role.SysRolePageReqVO; | ||
| 10 | +import cn.iocoder.dashboard.modules.system.controller.permission.vo.role.SysRoleUpdateReqVO; | ||
| 11 | +import cn.iocoder.dashboard.modules.system.dal.dataobject.permission.SysRoleDO; | ||
| 12 | +import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysRoleMapper; | ||
| 13 | +import cn.iocoder.dashboard.modules.system.enums.permission.SysRoleTypeEnum; | ||
| 14 | +import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysRoleProducer; | ||
| 15 | +import cn.iocoder.dashboard.modules.system.service.permission.impl.SysRoleServiceImpl; | ||
| 16 | +import cn.iocoder.dashboard.util.AopTargetUtils; | ||
| 17 | +import cn.iocoder.dashboard.util.AssertUtils; | ||
| 18 | +import cn.iocoder.dashboard.util.RandomUtils; | ||
| 19 | +import cn.iocoder.dashboard.util.object.ObjectUtils; | ||
| 20 | +import com.google.common.collect.Sets; | ||
| 21 | +import org.junit.jupiter.api.Assertions; | ||
| 22 | +import org.junit.jupiter.api.Test; | ||
| 23 | +import org.mockito.Mockito; | ||
| 24 | +import org.springframework.boot.test.mock.mockito.MockBean; | ||
| 25 | +import org.springframework.context.annotation.Import; | ||
| 26 | + | ||
| 27 | +import javax.annotation.Resource; | ||
| 28 | +import java.util.*; | ||
| 29 | +import java.util.stream.Collectors; | ||
| 30 | + | ||
| 31 | +import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; | ||
| 32 | +import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals; | ||
| 33 | +import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException; | ||
| 34 | +import static cn.iocoder.dashboard.util.RandomUtils.*; | ||
| 35 | +import static cn.iocoder.dashboard.util.object.ObjectUtils.max; | ||
| 36 | +import static org.junit.jupiter.api.Assertions.*; | ||
| 37 | +import static org.mockito.Mockito.verify; | ||
| 38 | + | ||
| 39 | +@Import(SysRoleServiceImpl.class) | ||
| 40 | +public class SysRoleServiceTest extends BaseDbUnitTest { | ||
| 41 | + | ||
| 42 | + @Resource | ||
| 43 | + private SysRoleServiceImpl sysRoleService; | ||
| 44 | + | ||
| 45 | + @Resource | ||
| 46 | + private SysRoleMapper roleMapper; | ||
| 47 | + | ||
| 48 | + @MockBean | ||
| 49 | + private SysPermissionService sysPermissionService; | ||
| 50 | + | ||
| 51 | + @MockBean | ||
| 52 | + private SysRoleProducer sysRoleProducer; | ||
| 53 | + | ||
| 54 | + @Test | ||
| 55 | + public void testInitLocalCache_success() throws Exception { | ||
| 56 | + SysRoleDO roleDO1 = createRoleDO("role1", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL); | ||
| 57 | + roleMapper.insert(roleDO1); | ||
| 58 | + SysRoleDO roleDO2 = createRoleDO("role2", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL); | ||
| 59 | + roleMapper.insert(roleDO2); | ||
| 60 | + | ||
| 61 | + //调用 | ||
| 62 | + sysRoleService.initLocalCache(); | ||
| 63 | + | ||
| 64 | + //断言 | ||
| 65 | + //获取代理对象 | ||
| 66 | + SysRoleServiceImpl target = (SysRoleServiceImpl) AopTargetUtils.getTarget(sysRoleService); | ||
| 67 | + | ||
| 68 | + Map<Long, SysRoleDO> roleCache = (Map<Long, SysRoleDO>) BeanUtil.getFieldValue(target, "roleCache"); | ||
| 69 | + assertPojoEquals(roleDO1, roleCache.get(roleDO1.getId())); | ||
| 70 | + assertPojoEquals(roleDO2, roleCache.get(roleDO2.getId())); | ||
| 71 | + | ||
| 72 | + Date maxUpdateTime = (Date) BeanUtil.getFieldValue(target, "maxUpdateTime"); | ||
| 73 | + assertEquals(max(roleDO1.getUpdateTime(), roleDO2.getUpdateTime()), maxUpdateTime); | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + @Test | ||
| 77 | + public void testCreateRole_success() { | ||
| 78 | + SysRoleCreateReqVO reqVO = randomPojo(SysRoleCreateReqVO.class, o -> { | ||
| 79 | + o.setCode("role_code"); | ||
| 80 | + o.setName("role_name"); | ||
| 81 | + o.setRemark("remark"); | ||
| 82 | + o.setType(SysRoleTypeEnum.CUSTOM.getType()); | ||
| 83 | + o.setSort(1); | ||
| 84 | + }); | ||
| 85 | + Long roleId = sysRoleService.createRole(reqVO); | ||
| 86 | + | ||
| 87 | + //断言 | ||
| 88 | + assertNotNull(roleId); | ||
| 89 | + SysRoleDO roleDO = roleMapper.selectById(roleId); | ||
| 90 | + assertPojoEquals(reqVO, roleDO); | ||
| 91 | + | ||
| 92 | + verify(sysRoleProducer).sendRoleRefreshMessage(); | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + @Test | ||
| 96 | + public void testUpdateRole_success() { | ||
| 97 | + SysRoleDO roleDO = createRoleDO("role_name", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL); | ||
| 98 | + roleMapper.insert(roleDO); | ||
| 99 | + Long roleId = roleDO.getId(); | ||
| 100 | + | ||
| 101 | + //调用 | ||
| 102 | + SysRoleUpdateReqVO reqVO = randomPojo(SysRoleUpdateReqVO.class, o -> { | ||
| 103 | + o.setId(roleId); | ||
| 104 | + o.setCode("role_code"); | ||
| 105 | + o.setName("update_name"); | ||
| 106 | + o.setType(SysRoleTypeEnum.SYSTEM.getType()); | ||
| 107 | + o.setSort(999); | ||
| 108 | + }); | ||
| 109 | + sysRoleService.updateRole(reqVO); | ||
| 110 | + | ||
| 111 | + //断言 | ||
| 112 | + SysRoleDO newRoleDO = roleMapper.selectById(roleId); | ||
| 113 | + assertPojoEquals(reqVO, newRoleDO); | ||
| 114 | + | ||
| 115 | + verify(sysRoleProducer).sendRoleRefreshMessage(); | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + @Test | ||
| 119 | + public void testUpdateRoleStatus_success() { | ||
| 120 | + SysRoleDO roleDO = createRoleDO("role_name", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL, CommonStatusEnum.ENABLE.getStatus()); | ||
| 121 | + roleMapper.insert(roleDO); | ||
| 122 | + Long roleId = roleDO.getId(); | ||
| 123 | + | ||
| 124 | + //调用 | ||
| 125 | + sysRoleService.updateRoleStatus(roleId, CommonStatusEnum.DISABLE.getStatus()); | ||
| 126 | + | ||
| 127 | + //断言 | ||
| 128 | + SysRoleDO newRoleDO = roleMapper.selectById(roleId); | ||
| 129 | + assertEquals(CommonStatusEnum.DISABLE.getStatus(), newRoleDO.getStatus()); | ||
| 130 | + | ||
| 131 | + verify(sysRoleProducer).sendRoleRefreshMessage(); | ||
| 132 | + } | ||
| 133 | + | ||
| 134 | + @Test | ||
| 135 | + public void testUpdateRoleDataScope_success() { | ||
| 136 | + SysRoleDO roleDO = createRoleDO("role_name", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL); | ||
| 137 | + roleMapper.insert(roleDO); | ||
| 138 | + Long roleId = roleDO.getId(); | ||
| 139 | + | ||
| 140 | + //调用 | ||
| 141 | + Set<Long> deptIdSet = Arrays.asList(1L, 2L, 3L, 4L, 5L).stream().collect(Collectors.toSet()); | ||
| 142 | + sysRoleService.updateRoleDataScope(roleId, DataScopeEnum.DEPT_CUSTOM.getScore(), deptIdSet); | ||
| 143 | + | ||
| 144 | + //断言 | ||
| 145 | + SysRoleDO newRoleDO = roleMapper.selectById(roleId); | ||
| 146 | + assertEquals(DataScopeEnum.DEPT_CUSTOM.getScore(), newRoleDO.getDataScope()); | ||
| 147 | + | ||
| 148 | + Set<Long> newDeptIdSet = newRoleDO.getDataScopeDeptIds(); | ||
| 149 | + assertTrue(deptIdSet.size() == newDeptIdSet.size()); | ||
| 150 | + deptIdSet.stream().forEach(d -> assertTrue(newDeptIdSet.contains(d))); | ||
| 151 | + | ||
| 152 | + verify(sysRoleProducer).sendRoleRefreshMessage(); | ||
| 153 | + } | ||
| 154 | + | ||
| 155 | + @Test | ||
| 156 | + public void testDeleteRole_success() { | ||
| 157 | + SysRoleDO roleDO = createRoleDO("role_name", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL); | ||
| 158 | + roleMapper.insert(roleDO); | ||
| 159 | + Long roleId = roleDO.getId(); | ||
| 160 | + | ||
| 161 | + //调用 | ||
| 162 | + sysRoleService.deleteRole(roleId); | ||
| 163 | + | ||
| 164 | + //断言 | ||
| 165 | + SysRoleDO newRoleDO = roleMapper.selectById(roleId); | ||
| 166 | + assertNull(newRoleDO); | ||
| 167 | + | ||
| 168 | + verify(sysRoleProducer).sendRoleRefreshMessage(); | ||
| 169 | + } | ||
| 170 | + | ||
| 171 | + @Test | ||
| 172 | + public void testGetRoles_success() { | ||
| 173 | + Map<Long, SysRoleDO> idRoleMap = new HashMap<>(); | ||
| 174 | + // 验证查询状态为1的角色 | ||
| 175 | + SysRoleDO roleDO1 = createRoleDO("role1", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1); | ||
| 176 | + roleMapper.insert(roleDO1); | ||
| 177 | + idRoleMap.put(roleDO1.getId(), roleDO1); | ||
| 178 | + | ||
| 179 | + SysRoleDO roleDO2 = createRoleDO("role2", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1); | ||
| 180 | + roleMapper.insert(roleDO2); | ||
| 181 | + idRoleMap.put(roleDO2.getId(), roleDO2); | ||
| 182 | + | ||
| 183 | + // 以下是排除的角色 | ||
| 184 | + SysRoleDO roleDO3 = createRoleDO("role3", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 2); | ||
| 185 | + roleMapper.insert(roleDO3); | ||
| 186 | + | ||
| 187 | + //调用 | ||
| 188 | + List<SysRoleDO> roles = sysRoleService.getRoles(Arrays.asList(1)); | ||
| 189 | + | ||
| 190 | + //断言 | ||
| 191 | + assertEquals(2, roles.size()); | ||
| 192 | + roles.stream().forEach(r -> assertPojoEquals(idRoleMap.get(r.getId()), r)); | ||
| 193 | + | ||
| 194 | + } | ||
| 195 | + | ||
| 196 | + @Test | ||
| 197 | + public void testGetRolePage_success() { | ||
| 198 | + Map<Long, SysRoleDO> idRoleMap = new HashMap<>(); | ||
| 199 | + // 验证名称包含"role", 状态为1,code为"code"的角色 | ||
| 200 | + // 第一页 | ||
| 201 | + SysRoleDO roleDO = createRoleDO("role1", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1, "code"); | ||
| 202 | + roleMapper.insert(roleDO); | ||
| 203 | + idRoleMap.put(roleDO.getId(), roleDO); | ||
| 204 | + // 第二页 | ||
| 205 | + roleDO = createRoleDO("role2", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1, "code"); | ||
| 206 | + roleMapper.insert(roleDO); | ||
| 207 | + | ||
| 208 | + // 以下是排除的角色 | ||
| 209 | + roleDO = createRoleDO("role3", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 2, "code"); | ||
| 210 | + roleMapper.insert(roleDO); | ||
| 211 | + roleDO = createRoleDO("role4", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1, "xxxxx"); | ||
| 212 | + roleMapper.insert(roleDO); | ||
| 213 | + | ||
| 214 | + //调用 | ||
| 215 | + SysRolePageReqVO reqVO = randomPojo(SysRolePageReqVO.class, o -> { | ||
| 216 | + o.setName("role"); | ||
| 217 | + o.setCode("code"); | ||
| 218 | + o.setStatus(1); | ||
| 219 | + o.setPageNo(1); | ||
| 220 | + o.setPageSize(1); | ||
| 221 | + o.setBeginTime(null); | ||
| 222 | + o.setEndTime(null); | ||
| 223 | + }); | ||
| 224 | + PageResult<SysRoleDO> result = sysRoleService.getRolePage(reqVO); | ||
| 225 | + assertEquals(2, result.getTotal()); | ||
| 226 | + result.getList().stream().forEach(r -> assertPojoEquals(idRoleMap.get(r.getId()), r)); | ||
| 227 | + } | ||
| 228 | + | ||
| 229 | + @Test | ||
| 230 | + public void testCheckDuplicateRole_success() { | ||
| 231 | + sysRoleService.checkDuplicateRole(randomString(), randomString(), null); | ||
| 232 | + } | ||
| 233 | + | ||
| 234 | + @Test | ||
| 235 | + public void testCheckDuplicateRole_nameDuplicate() { | ||
| 236 | + SysRoleDO roleDO = createRoleDO("role_name", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL); | ||
| 237 | + roleMapper.insert(roleDO); | ||
| 238 | + | ||
| 239 | + String duplicateName = "role_name"; | ||
| 240 | + | ||
| 241 | + assertServiceException(() -> sysRoleService.checkDuplicateRole(duplicateName, randomString(), null), ROLE_NAME_DUPLICATE, duplicateName); | ||
| 242 | + } | ||
| 243 | + | ||
| 244 | + @Test | ||
| 245 | + public void testCheckDuplicateRole_codeDuplicate() { | ||
| 246 | + SysRoleDO roleDO = randomPojo(SysRoleDO.class, o -> { | ||
| 247 | + o.setName("role_999"); | ||
| 248 | + o.setCode("code"); | ||
| 249 | + o.setType(SysRoleTypeEnum.CUSTOM.getType()); | ||
| 250 | + o.setStatus(1); | ||
| 251 | + o.setDataScope(DataScopeEnum.ALL.getScore()); | ||
| 252 | + }); | ||
| 253 | + roleMapper.insert(roleDO); | ||
| 254 | + | ||
| 255 | + String randomName = randomString(); | ||
| 256 | + String duplicateCode = "code"; | ||
| 257 | + | ||
| 258 | + assertServiceException(() -> sysRoleService.checkDuplicateRole(randomName, duplicateCode, null), ROLE_CODE_DUPLICATE, duplicateCode); | ||
| 259 | + } | ||
| 260 | + | ||
| 261 | + @Test | ||
| 262 | + public void testCheckUpdateRole_success() { | ||
| 263 | + SysRoleDO roleDO = createRoleDO("role_name", SysRoleTypeEnum.CUSTOM, DataScopeEnum.ALL); | ||
| 264 | + roleMapper.insert(roleDO); | ||
| 265 | + Long roleId = roleDO.getId(); | ||
| 266 | + | ||
| 267 | + sysRoleService.checkUpdateRole(roleId); | ||
| 268 | + } | ||
| 269 | + | ||
| 270 | + @Test | ||
| 271 | + public void testCheckUpdateRole_roleIdNotExist() { | ||
| 272 | + assertServiceException(() -> sysRoleService.checkUpdateRole(randomLongId()), ROLE_NOT_EXISTS); | ||
| 273 | + } | ||
| 274 | + | ||
| 275 | + @Test | ||
| 276 | + public void testCheckUpdateRole_systemRoleCanNotBeUpdate() { | ||
| 277 | + SysRoleDO roleDO = createRoleDO("role_name", SysRoleTypeEnum.SYSTEM, DataScopeEnum.ALL); | ||
| 278 | + roleMapper.insert(roleDO); | ||
| 279 | + Long roleId = roleDO.getId(); | ||
| 280 | + | ||
| 281 | + assertServiceException(() -> sysRoleService.checkUpdateRole(roleId), ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE); | ||
| 282 | + } | ||
| 283 | + | ||
| 284 | + private SysRoleDO createRoleDO(String name, SysRoleTypeEnum typeEnum, DataScopeEnum scopeEnum, Integer status) { | ||
| 285 | + return createRoleDO( name, typeEnum, scopeEnum, status, randomString()); | ||
| 286 | + } | ||
| 287 | + | ||
| 288 | + private SysRoleDO createRoleDO(String name, SysRoleTypeEnum typeEnum, DataScopeEnum scopeEnum, Integer status, String code) { | ||
| 289 | + return createRoleDO(null, name, typeEnum, scopeEnum, status, code); | ||
| 290 | + } | ||
| 291 | + | ||
| 292 | + private SysRoleDO createRoleDO(String name, SysRoleTypeEnum typeEnum, DataScopeEnum scopeEnum) { | ||
| 293 | + return createRoleDO(null, name, typeEnum, scopeEnum, randomCommonStatus(), randomString()); | ||
| 294 | + } | ||
| 295 | + | ||
| 296 | + private SysRoleDO createRoleDO(Long id, String name, SysRoleTypeEnum typeEnum, DataScopeEnum scopeEnum, Integer status, String code) { | ||
| 297 | + SysRoleDO roleDO = randomPojo(SysRoleDO.class, o -> { | ||
| 298 | + o.setId(id); | ||
| 299 | + o.setName(name); | ||
| 300 | + o.setType(typeEnum.getType()); | ||
| 301 | + o.setStatus(status); | ||
| 302 | + o.setDataScope(scopeEnum.getScore()); | ||
| 303 | + o.setCode(code); | ||
| 304 | + }); | ||
| 305 | + return roleDO; | ||
| 306 | + } | ||
| 307 | + | ||
| 308 | +} |
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 数据s | ||
| 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/util/AopTargetUtils.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.util; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.bean.BeanUtil; | ||
| 4 | +import org.springframework.aop.framework.AdvisedSupport; | ||
| 5 | +import org.springframework.aop.framework.AopProxy; | ||
| 6 | +import org.springframework.aop.support.AopUtils; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * Spring AOP 工具类 | ||
| 10 | + * | ||
| 11 | + * 参考波克尔 http://www.bubuko.com/infodetail-3471885.html 实现 | ||
| 12 | + */ | ||
| 13 | +public class AopTargetUtils { | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * 获取代理的目标对象 | ||
| 17 | + * | ||
| 18 | + * @param proxy 代理对象 | ||
| 19 | + * @return 目标对象 | ||
| 20 | + */ | ||
| 21 | + public static Object getTarget(Object proxy) throws Exception { | ||
| 22 | + // 不是代理对象 | ||
| 23 | + if (!AopUtils.isAopProxy(proxy)) { | ||
| 24 | + return proxy; | ||
| 25 | + } | ||
| 26 | + // Jdk 代理 | ||
| 27 | + if (AopUtils.isJdkDynamicProxy(proxy)) { | ||
| 28 | + return getJdkDynamicProxyTargetObject(proxy); | ||
| 29 | + } | ||
| 30 | + // Cglib 代理 | ||
| 31 | + return getCglibProxyTargetObject(proxy); | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + private static Object getCglibProxyTargetObject(Object proxy) throws Exception { | ||
| 35 | + Object dynamicAdvisedInterceptor = BeanUtil.getFieldValue(proxy, "CGLIB$CALLBACK_0"); | ||
| 36 | + AdvisedSupport advisedSupport = (AdvisedSupport) BeanUtil.getFieldValue(dynamicAdvisedInterceptor, "advised"); | ||
| 37 | + return advisedSupport.getTargetSource().getTarget(); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception { | ||
| 41 | + AopProxy aopProxy = (AopProxy) BeanUtil.getFieldValue(proxy, "h"); | ||
| 42 | + AdvisedSupport advisedSupport = (AdvisedSupport) BeanUtil.getFieldValue(aopProxy, "advised"); | ||
| 43 | + return advisedSupport.getTargetSource().getTarget(); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | +} |
src/test/java/cn/iocoder/dashboard/util/AssertUtils.java
| @@ -10,6 +10,7 @@ import org.junit.jupiter.api.function.Executable; | @@ -10,6 +10,7 @@ import org.junit.jupiter.api.function.Executable; | ||
| 10 | 10 | ||
| 11 | import java.lang.reflect.Field; | 11 | import java.lang.reflect.Field; |
| 12 | import java.util.Arrays; | 12 | import java.util.Arrays; |
| 13 | +import java.util.Objects; | ||
| 13 | 14 | ||
| 14 | import static org.junit.jupiter.api.Assertions.assertThrows; | 15 | import static org.junit.jupiter.api.Assertions.assertThrows; |
| 15 | 16 | ||
| @@ -51,6 +52,33 @@ public class AssertUtils { | @@ -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 | * 执行方法,校验抛出的 Service 是否符合条件 | 82 | * 执行方法,校验抛出的 Service 是否符合条件 |
| 55 | * | 83 | * |
| 56 | * @param executable 业务异常 | 84 | * @param executable 业务异常 |
| @@ -62,7 +90,7 @@ public class AssertUtils { | @@ -62,7 +90,7 @@ public class AssertUtils { | ||
| 62 | ServiceException serviceException = assertThrows(ServiceException.class, executable); | 90 | ServiceException serviceException = assertThrows(ServiceException.class, executable); |
| 63 | // 校验错误码 | 91 | // 校验错误码 |
| 64 | Assertions.assertEquals(errorCode.getCode(), serviceException.getCode(), "错误码不匹配"); | 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 | Assertions.assertEquals(message, serviceException.getMessage(), "错误提示不匹配"); | 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,9 +7,8 @@ import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO; | ||
| 7 | import uk.co.jemos.podam.api.PodamFactory; | 7 | import uk.co.jemos.podam.api.PodamFactory; |
| 8 | import uk.co.jemos.podam.api.PodamFactoryImpl; | 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 | import java.util.function.Consumer; | 12 | import java.util.function.Consumer; |
| 14 | import java.util.stream.Collectors; | 13 | import java.util.stream.Collectors; |
| 15 | import java.util.stream.Stream; | 14 | import java.util.stream.Stream; |
| @@ -87,4 +86,21 @@ public class RandomUtils { | @@ -87,4 +86,21 @@ public class RandomUtils { | ||
| 87 | return pojo; | 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
| @@ -19,3 +19,6 @@ DELETE FROM "sys_post"; | @@ -19,3 +19,6 @@ DELETE FROM "sys_post"; | ||
| 19 | DELETE FROM "sys_login_log"; | 19 | DELETE FROM "sys_login_log"; |
| 20 | DELETE FROM "sys_operate_log"; | 20 | DELETE FROM "sys_operate_log"; |
| 21 | DELETE FROM "sys_user"; | 21 | DELETE FROM "sys_user"; |
| 22 | +DELETE FROM "sys_sms_channel"; | ||
| 23 | +DELETE FROM "sys_sms_template"; | ||
| 24 | +DELETE FROM "sys_sms_log"; |
src/test/resources/sql/create_tables.sql
| @@ -47,8 +47,7 @@ CREATE TABLE IF NOT EXISTS "inf_job" ( | @@ -47,8 +47,7 @@ CREATE TABLE IF NOT EXISTS "inf_job" ( | ||
| 47 | PRIMARY KEY ("id") | 47 | PRIMARY KEY ("id") |
| 48 | ) COMMENT='定时任务表'; | 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 | "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '日志编号', | 51 | "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '日志编号', |
| 53 | "job_id" bigint(20) NOT NULL COMMENT '任务编号', | 52 | "job_id" bigint(20) NOT NULL COMMENT '任务编号', |
| 54 | "handler_name" varchar(64) NOT NULL COMMENT '处理器的名字', | 53 | "handler_name" varchar(64) NOT NULL COMMENT '处理器的名字', |
| @@ -192,8 +191,7 @@ CREATE TABLE IF NOT EXISTS `sys_user_session` ( | @@ -192,8 +191,7 @@ CREATE TABLE IF NOT EXISTS `sys_user_session` ( | ||
| 192 | PRIMARY KEY (`id`) | 191 | PRIMARY KEY (`id`) |
| 193 | ) COMMENT '用户在线 Session'; | 192 | ) COMMENT '用户在线 Session'; |
| 194 | 193 | ||
| 195 | -CREATE TABLE IF NOT EXISTS "sys_post" | ||
| 196 | -( | 194 | +CREATE TABLE IF NOT EXISTS "sys_post" ( |
| 197 | "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | 195 | "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, |
| 198 | "code" varchar(64) NOT NULL, | 196 | "code" varchar(64) NOT NULL, |
| 199 | "name" varchar(50) NOT NULL, | 197 | "name" varchar(50) NOT NULL, |
| @@ -208,7 +206,6 @@ CREATE TABLE IF NOT EXISTS "sys_post" | @@ -208,7 +206,6 @@ CREATE TABLE IF NOT EXISTS "sys_post" | ||
| 208 | PRIMARY KEY ("id") | 206 | PRIMARY KEY ("id") |
| 209 | ) COMMENT '岗位信息表'; | 207 | ) COMMENT '岗位信息表'; |
| 210 | 208 | ||
| 211 | - | ||
| 212 | CREATE TABLE IF NOT EXISTS "sys_notice" ( | 209 | CREATE TABLE IF NOT EXISTS "sys_notice" ( |
| 213 | "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | 210 | "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, |
| 214 | "title" varchar(50) NOT NULL COMMENT '公告标题', | 211 | "title" varchar(50) NOT NULL COMMENT '公告标题', |
| @@ -223,7 +220,6 @@ CREATE TABLE IF NOT EXISTS "sys_notice" ( | @@ -223,7 +220,6 @@ CREATE TABLE IF NOT EXISTS "sys_notice" ( | ||
| 223 | PRIMARY KEY("id") | 220 | PRIMARY KEY("id") |
| 224 | ) COMMENT '通知公告表'; | 221 | ) COMMENT '通知公告表'; |
| 225 | 222 | ||
| 226 | - | ||
| 227 | CREATE TABLE IF NOT EXISTS `sys_login_log` ( | 223 | CREATE TABLE IF NOT EXISTS `sys_login_log` ( |
| 228 | `id` bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, | 224 | `id` bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, |
| 229 | `log_type` bigint(4) NOT NULL, | 225 | `log_type` bigint(4) NOT NULL, |
| @@ -240,7 +236,6 @@ CREATE TABLE IF NOT EXISTS `sys_login_log` ( | @@ -240,7 +236,6 @@ CREATE TABLE IF NOT EXISTS `sys_login_log` ( | ||
| 240 | PRIMARY KEY (`id`) | 236 | PRIMARY KEY (`id`) |
| 241 | ) COMMENT ='系统访问记录'; | 237 | ) COMMENT ='系统访问记录'; |
| 242 | 238 | ||
| 243 | - | ||
| 244 | CREATE TABLE IF NOT EXISTS `sys_operate_log` ( | 239 | CREATE TABLE IF NOT EXISTS `sys_operate_log` ( |
| 245 | `id` bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, | 240 | `id` bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, |
| 246 | `trace_id` varchar(64) NOT NULL DEFAULT '', | 241 | `trace_id` varchar(64) NOT NULL DEFAULT '', |
| @@ -346,3 +341,73 @@ CREATE TABLE IF NOT EXISTS "inf_api_error_log" ( | @@ -346,3 +341,73 @@ CREATE TABLE IF NOT EXISTS "inf_api_error_log" ( | ||
| 346 | "deleted" bit not null default false, | 341 | "deleted" bit not null default false, |
| 347 | primary key ("id") | 342 | primary key ("id") |
| 348 | ) COMMENT '系统异常日志'; | 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 '短信日志'; |