From e4828bf2a39f3bfd32dce018874ef4cba674c02e Mon Sep 17 00:00:00 2001 From: Top2Hub Date: Mon, 21 Apr 2025 16:11:25 +0800 Subject: [PATCH] =?UTF-8?q?feat(system/sms):=20=E7=9F=AD=E4=BF=A1=E6=B8=A0?= =?UTF-8?q?=E9=81=93=E6=94=AF=E6=8C=81=E6=95=B0=E6=8D=AE=E5=AD=97=E5=85=B8?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=20(#159)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/properties/CaptchaProperties.java | 17 ++++ .../system/config/sms/SmsConfigUtil.java | 88 +++++++++++++++++++ .../config/sms/SmsReadConfigDatabaseImpl.java | 6 +- .../admin/system/enums/SmsSupplierEnum.java | 3 + .../system/model/entity/SmsConfigDO.java | 3 +- .../system/model/query/SmsConfigQuery.java | 3 +- .../admin/system/model/req/SmsConfigReq.java | 3 +- .../system/model/resp/SmsConfigResp.java | 11 ++- .../service/impl/SmsConfigServiceImpl.java | 14 +-- .../controller/common/CaptchaController.java | 11 ++- .../db/changelog/db.changelog-master.yaml | 6 +- .../db/changelog/mysql/sms_dict_data.sql | 38 ++++++++ .../db/changelog/postgresql/sms_dict_data.sql | 38 ++++++++ 13 files changed, 220 insertions(+), 21 deletions(-) create mode 100644 continew-module-system/src/main/java/top/continew/admin/system/config/sms/SmsConfigUtil.java create mode 100644 continew-webapi/src/main/resources/db/changelog/mysql/sms_dict_data.sql create mode 100644 continew-webapi/src/main/resources/db/changelog/postgresql/sms_dict_data.sql diff --git a/continew-common/src/main/java/top/continew/admin/common/config/properties/CaptchaProperties.java b/continew-common/src/main/java/top/continew/admin/common/config/properties/CaptchaProperties.java index 51870c56..5e447e45 100644 --- a/continew-common/src/main/java/top/continew/admin/common/config/properties/CaptchaProperties.java +++ b/continew-common/src/main/java/top/continew/admin/common/config/properties/CaptchaProperties.java @@ -88,5 +88,22 @@ public class CaptchaProperties { * 模板 ID */ private String templateId; + + /** + * 供应商渠道 + * + * @see top.continew.admin.system.model.resp.SmsConfigResp#supplier + */ + private String supplier; + + /** + * 验证码字段模版键名 + */ + private String codeKey = "code"; + + /** + * 失效时间字段模版键名 + */ + private String timeKey = "expirationInMinutes"; } } diff --git a/continew-module-system/src/main/java/top/continew/admin/system/config/sms/SmsConfigUtil.java b/continew-module-system/src/main/java/top/continew/admin/system/config/sms/SmsConfigUtil.java new file mode 100644 index 00000000..f304be03 --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/system/config/sms/SmsConfigUtil.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.system.config.sms; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import org.dromara.sms4j.provider.config.BaseConfig; +import org.dromara.sms4j.provider.factory.BaseProviderFactory; +import org.dromara.sms4j.provider.factory.ProviderFactoryHolder; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import top.continew.admin.system.model.resp.SmsConfigResp; + +/** + * 短信配置工具类 + * + * @author Top2Hub + * @since 2025/04/21 14:00 + */ + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class SmsConfigUtil { + + private static final TypeReference> CONFIG_MAP_TYPE = new TypeReference>() {}; + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + /** + * 将本地配置对象转换为 BaseConfig + * + * @param smsConfig 本地配置对象 + * @return SMS4j 配置基类 + */ + public static BaseConfig from(SmsConfigResp smsConfig) { + if (Objects.isNull(smsConfig)) + return null; + + String supplierName = smsConfig.getSupplier(); + BaseProviderFactory providerFactory = ProviderFactoryHolder.requireForSupplier(supplierName); + if (Objects.isNull(providerFactory)) + return null; + + Map configInfo = new HashMap<>(); + configInfo.put("configId", smsConfig.getId().toString()); + configInfo.put("accessKeyId", smsConfig.getAccessKey()); + configInfo.put("accessKeySecret", smsConfig.getSecretKey()); + configInfo.put("signature", smsConfig.getSignature()); + configInfo.put("templateId", smsConfig.getTemplateId()); + if (Objects.nonNull(smsConfig.getWeight())) + configInfo.put("weight", smsConfig.getWeight()); + if (Objects.nonNull(smsConfig.getRetryInterval())) + configInfo.put("retryInterval", smsConfig.getRetryInterval()); + if (Objects.nonNull(smsConfig.getMaxRetries())) + configInfo.put("maxRetries", smsConfig.getMaxRetries()); + + if (Objects.nonNull(smsConfig.getSupplierConfig())) { + Map supplierInfo = OBJECT_MAPPER.convertValue(smsConfig + .getSupplierConfig(), CONFIG_MAP_TYPE); + configInfo.putAll(supplierInfo); + } + + BaseConfig config = (BaseConfig)OBJECT_MAPPER.convertValue(configInfo, providerFactory.getConfigClass()); + return config; + } + +} diff --git a/continew-module-system/src/main/java/top/continew/admin/system/config/sms/SmsReadConfigDatabaseImpl.java b/continew-module-system/src/main/java/top/continew/admin/system/config/sms/SmsReadConfigDatabaseImpl.java index 2ff9fab6..f0ed8613 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/config/sms/SmsReadConfigDatabaseImpl.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/config/sms/SmsReadConfigDatabaseImpl.java @@ -18,9 +18,11 @@ package top.continew.admin.system.config.sms; import cn.hutool.core.collection.CollUtil; import lombok.RequiredArgsConstructor; + import org.dromara.sms4j.core.datainterface.SmsReadConfig; import org.dromara.sms4j.provider.config.BaseConfig; import org.springframework.stereotype.Component; + import top.continew.admin.common.enums.DisEnableStatusEnum; import top.continew.admin.system.model.query.SmsConfigQuery; import top.continew.admin.system.model.resp.SmsConfigResp; @@ -48,7 +50,7 @@ public class SmsReadConfigDatabaseImpl implements SmsReadConfig { if (DisEnableStatusEnum.DISABLE.equals(smsConfig.getStatus())) { return null; } - return smsConfig.getSupplier().toBaseConfig(smsConfig); + return SmsConfigUtil.from(smsConfig); } @Override @@ -59,6 +61,6 @@ public class SmsReadConfigDatabaseImpl implements SmsReadConfig { if (CollUtil.isEmpty(list)) { return List.of(); } - return list.stream().map(smsConfig -> smsConfig.getSupplier().toBaseConfig(smsConfig)).toList(); + return list.stream().map(smsConfig -> SmsConfigUtil.from(smsConfig)).toList(); } } \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/enums/SmsSupplierEnum.java b/continew-module-system/src/main/java/top/continew/admin/system/enums/SmsSupplierEnum.java index 5abcd798..11943f30 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/enums/SmsSupplierEnum.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/enums/SmsSupplierEnum.java @@ -32,7 +32,10 @@ import top.continew.starter.core.enums.BaseEnum; * @author luoqiz * @author Charles7c * @since 2025/03/15 22:15 + * + * @deprecated 使用数据字典`sms_supplier_type`动态维护 */ +@Deprecated @Getter @RequiredArgsConstructor public enum SmsSupplierEnum implements BaseEnum { diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/SmsConfigDO.java b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/SmsConfigDO.java index 7b8a16a3..fc0bf91c 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/SmsConfigDO.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/SmsConfigDO.java @@ -20,7 +20,6 @@ import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import top.continew.admin.common.enums.DisEnableStatusEnum; import top.continew.admin.common.model.entity.BaseDO; -import top.continew.admin.system.enums.SmsSupplierEnum; import top.continew.starter.security.crypto.annotation.FieldEncrypt; import java.io.Serial; @@ -47,7 +46,7 @@ public class SmsConfigDO extends BaseDO { /** * 厂商 */ - private SmsSupplierEnum supplier; + private String supplier; /** * Access Key diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/query/SmsConfigQuery.java b/continew-module-system/src/main/java/top/continew/admin/system/model/query/SmsConfigQuery.java index 0c0dea7a..52b842b0 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/query/SmsConfigQuery.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/query/SmsConfigQuery.java @@ -19,7 +19,6 @@ package top.continew.admin.system.model.query; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import top.continew.admin.common.enums.DisEnableStatusEnum; -import top.continew.admin.system.enums.SmsSupplierEnum; import top.continew.starter.data.core.annotation.Query; import top.continew.starter.data.core.enums.QueryType; @@ -52,7 +51,7 @@ public class SmsConfigQuery implements Serializable { */ @Schema(description = "厂商", example = "cloopen") @Query - private SmsSupplierEnum supplier; + private String supplier; /** * Access Key diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/req/SmsConfigReq.java b/continew-module-system/src/main/java/top/continew/admin/system/model/req/SmsConfigReq.java index 9d44617a..b1db5446 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/req/SmsConfigReq.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/req/SmsConfigReq.java @@ -24,7 +24,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import org.hibernate.validator.constraints.Length; import top.continew.admin.common.enums.DisEnableStatusEnum; -import top.continew.admin.system.enums.SmsSupplierEnum; import java.io.Serial; import java.io.Serializable; @@ -56,7 +55,7 @@ public class SmsConfigReq implements Serializable { */ @Schema(description = "厂商", example = "cloopen") @NotNull(message = "厂商无效") - private SmsSupplierEnum supplier; + private String supplier; /** * Access Key diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/SmsConfigResp.java b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/SmsConfigResp.java index 3e32dab6..76cc1c3c 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/SmsConfigResp.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/SmsConfigResp.java @@ -20,9 +20,10 @@ import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import top.continew.admin.common.config.excel.DictExcelProperty; +import top.continew.admin.common.config.excel.ExcelDictConverter; import top.continew.admin.common.enums.DisEnableStatusEnum; import top.continew.admin.common.model.resp.BaseDetailResp; -import top.continew.admin.system.enums.SmsSupplierEnum; import top.continew.starter.file.excel.converter.ExcelBaseEnumConverter; import top.continew.starter.security.mask.annotation.JsonMask; @@ -52,10 +53,14 @@ public class SmsConfigResp extends BaseDetailResp { /** * 厂商 + * 对齐 sms4j 短信服务商常量类 + * + * @see org.dromara.sms4j.comm.constant.SupplierConstant */ @Schema(description = "厂商", example = "cloopen") - @ExcelProperty(value = "厂商") - private SmsSupplierEnum supplier; + @ExcelProperty(value = "厂商", converter = ExcelDictConverter.class) + @DictExcelProperty("sms_supplier_type") + private String supplier; /** * Access Key diff --git a/continew-module-system/src/main/java/top/continew/admin/system/service/impl/SmsConfigServiceImpl.java b/continew-module-system/src/main/java/top/continew/admin/system/service/impl/SmsConfigServiceImpl.java index aef416de..7ba89646 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/service/impl/SmsConfigServiceImpl.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/service/impl/SmsConfigServiceImpl.java @@ -16,12 +16,13 @@ package top.continew.admin.system.service.impl; -import cn.hutool.core.bean.BeanUtil; import lombok.RequiredArgsConstructor; + import org.dromara.sms4j.core.factory.SmsFactory; import org.dromara.sms4j.provider.config.BaseConfig; import org.springframework.stereotype.Service; -import top.continew.admin.system.enums.SmsSupplierEnum; + +import top.continew.admin.system.config.sms.SmsConfigUtil; import top.continew.admin.system.mapper.SmsConfigMapper; import top.continew.admin.system.model.entity.SmsConfigDO; import top.continew.admin.system.model.query.SmsConfigQuery; @@ -31,6 +32,7 @@ import top.continew.admin.system.service.SmsConfigService; import top.continew.starter.extension.crud.service.BaseServiceImpl; import java.util.List; +import java.util.Objects; /** * 短信配置业务实现 @@ -69,9 +71,11 @@ public class SmsConfigServiceImpl extends BaseServiceImpl messageMap = MapUtil.newHashMap(2, true); - messageMap.put("captcha", captcha); - messageMap.put("expirationInMinutes", String.valueOf(expirationInMinutes)); + messageMap.put(captchaSms.getCodeKey(), captcha); + messageMap.put(captchaSms.getTimeKey(), String.valueOf(expirationInMinutes)); + SmsResponse smsResponse = smsBlend.sendMessage(phone, captchaSms .getTemplateId(), (LinkedHashMap)messageMap); + CheckUtils.throwIf(!smsResponse.isSuccess(), "验证码发送失败"); // 保存验证码 String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + phone; diff --git a/continew-webapi/src/main/resources/db/changelog/db.changelog-master.yaml b/continew-webapi/src/main/resources/db/changelog/db.changelog-master.yaml index feefa21e..aaad8542 100644 --- a/continew-webapi/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/continew-webapi/src/main/resources/db/changelog/db.changelog-master.yaml @@ -11,6 +11,8 @@ databaseChangeLog: file: db/changelog/mysql/plugin/plugin_open.sql - include: file: db/changelog/mysql/plugin/plugin_generator.sql + - include: + file: db/changelog/mysql/sms_dict_data.sql # PostgreSQL # - include: # file: db/changelog/postgresql/main_table.sql @@ -23,4 +25,6 @@ databaseChangeLog: # - include: # file: db/changelog/postgresql/plugin/plugin_open.sql # - include: -# file: db/changelog/postgresql/plugin/plugin_generator.sql \ No newline at end of file +# file: db/changelog/postgresql/plugin/plugin_generator.sql +# - include: +# file: db/changelog/postgresql/sms_dict_data.sql \ No newline at end of file diff --git a/continew-webapi/src/main/resources/db/changelog/mysql/sms_dict_data.sql b/continew-webapi/src/main/resources/db/changelog/mysql/sms_dict_data.sql new file mode 100644 index 00000000..b1ed7687 --- /dev/null +++ b/continew-webapi/src/main/resources/db/changelog/mysql/sms_dict_data.sql @@ -0,0 +1,38 @@ +-- liquibase formatted sql + +-- changeset TopHub:2 +-- comment 新增 短信厂商 字典 + +-- 新增 短信厂商 字典数据 +INSERT INTO `sys_dict` +(`id`, `name`, `code`, `description`, `is_system`, `create_user`, `create_time`) +VALUES +(4, '短信厂商', 'sms_supplier_type', NULL, b'1', 1, NOW()); + +-- 新增 短信厂商 字典项 +INSERT INTO `sys_dict_item` +(`id`, `label`, `value`, `color`, `sort`, `description`, `status`, `dict_id`, `create_user`, `create_time`) +VALUES +(8, '阿里云', 'alibaba', 'primary', 1, NULL, 1, 4, 1, NOW()), +(9, '容联云', 'cloopen', 'success', 2, NULL, 1, 4, 1, NOW()), +(10, '天翼云', 'ctyun', 'warning', 3, NULL, 1, 4, 1, NOW()), +(11, '亿美软通', 'emay', 'primary', 4, NULL, 1, 4, 1, NOW()), +(12, '华为', 'huawei', 'success', 5, NULL, 1, 4, 1, NOW()), +(13, '京东', 'jdcloud', 'warning', 6, NULL, 1, 4, 1, NOW()), +(14, '网易', 'netease', 'primary', 7, NULL, 1, 4, 1, NOW()), +(15, '腾讯', 'tencent', 'success', 8, NULL, 1, 4, 1, NOW()), +(16, '合一', 'unisms', 'warning', 9, NULL, 1, 4, 1, NOW()), +(17, '云片', 'yunpian', 'primary', 10, NULL, 1, 4, 1, NOW()), +(18, '助通', 'zhutong', 'success', 11, NULL, 1, 4, 1, NOW()), +(19, '联麓', 'lianlu', 'warning', 12, NULL, 1, 4, 1, NOW()), +(20, '鼎众', 'dingzhong', 'primary', 13, NULL, 1, 4, 1, NOW()), +(21, '七牛云', 'qiniu', 'success', 14, NULL, 1, 4, 1, NOW()), +(22, '创蓝', 'chuanglan', 'warning', 15, NULL, 1, 4, 1, NOW()), +(23, '极光', 'jiguang', 'primary', 16, NULL, 1, 4, 1, NOW()), +(24, '布丁云V2', 'buding_v2', 'success', 17, NULL, 1, 4, 1, NOW()), +(25, '中国移动 云MAS', 'mas', 'warning', 18, NULL, 1, 4, 1, NOW()), +(26, '百度云', 'baidu', 'primary', 19, NULL, 1, 4, 1, NOW()), +(27, '螺丝帽', 'luosimao', 'success', 20, NULL, 1, 4, 1, NOW()), +(28, 'SUBMAIL短信', 'mysubmail', 'success', 21, NULL, 1, 4, 1, NOW()), +(29, '单米科技短信', 'danmi', 'success', 22, NULL, 1, 4, 1, NOW()), +(30, '联通一信通', 'yixintong', 'success', 23, NULL, 1, 4, 1, NOW()); \ No newline at end of file diff --git a/continew-webapi/src/main/resources/db/changelog/postgresql/sms_dict_data.sql b/continew-webapi/src/main/resources/db/changelog/postgresql/sms_dict_data.sql new file mode 100644 index 00000000..62f6e91b --- /dev/null +++ b/continew-webapi/src/main/resources/db/changelog/postgresql/sms_dict_data.sql @@ -0,0 +1,38 @@ +-- liquibase formatted sql + +-- changeset TopHub:2 +-- comment 新增 短信厂商 字典 + +-- 新增 短信厂商 字典数据 +INSERT INTO "sys_dict" +("id", "name", "code", "description", "is_system", "create_user", "create_time") +VALUES +(4, '短信厂商', 'sms_supplier_type', NULL, true, 1, NOW()); + +-- 新增 短信厂商 字典项 +INSERT INTO "sys_dict_item" +("id", "label", "value", "color", "sort", "description", "status", "dict_id", "create_user", "create_time") +VALUES +(8, '阿里云', 'alibaba', 'primary', 1, NULL, 1, 4, 1, NOW()), +(9, '容联云', 'cloopen', 'success', 2, NULL, 1, 4, 1, NOW()), +(10, '天翼云', 'ctyun', 'warning', 3, NULL, 1, 4, 1, NOW()), +(11, '亿美软通', 'emay', 'primary', 4, NULL, 1, 4, 1, NOW()), +(12, '华为', 'huawei', 'success', 5, NULL, 1, 4, 1, NOW()), +(13, '京东', 'jdcloud', 'warning', 6, NULL, 1, 4, 1, NOW()), +(14, '网易', 'netease', 'primary', 7, NULL, 1, 4, 1, NOW()), +(15, '腾讯', 'tencent', 'success', 8, NULL, 1, 4, 1, NOW()), +(16, '合一', 'unisms', 'warning', 9, NULL, 1, 4, 1, NOW()), +(17, '云片', 'yunpian', 'primary', 10, NULL, 1, 4, 1, NOW()), +(18, '助通', 'zhutong', 'success', 11, NULL, 1, 4, 1, NOW()), +(19, '联麓', 'lianlu', 'warning', 12, NULL, 1, 4, 1, NOW()), +(20, '鼎众', 'dingzhong', 'primary', 13, NULL, 1, 4, 1, NOW()), +(21, '七牛云', 'qiniu', 'success', 14, NULL, 1, 4, 1, NOW()), +(22, '创蓝', 'chuanglan', 'warning', 15, NULL, 1, 4, 1, NOW()), +(23, '极光', 'jiguang', 'primary', 16, NULL, 1, 4, 1, NOW()), +(24, '布丁云V2', 'buding_v2', 'success', 17, NULL, 1, 4, 1, NOW()), +(25, '中国移动 云MAS', 'mas', 'warning', 18, NULL, 1, 4, 1, NOW()), +(26, '百度云', 'baidu', 'primary', 19, NULL, 1, 4, 1, NOW()), +(27, '螺丝帽', 'luosimao', 'success', 20, NULL, 1, 4, 1, NOW()), +(28, 'SUBMAIL短信', 'mysubmail', 'success', 21, NULL, 1, 4, 1, NOW()), +(29, '单米科技短信', 'danmi', 'success', 22, NULL, 1, 4, 1, NOW()), +(30, '联通一信通', 'yixintong', 'success', 23, NULL, 1, 4, 1, NOW()); \ No newline at end of file