mirror of
https://gitee.com/dromara/RuoYi-Vue-Plus.git
synced 2026-09-18 01:25:28 +08:00
Pre Merge pull request !866 from CyberShen/feat/sms-toggle
This commit is contained in:
commit
87135de6f6
@ -22,6 +22,7 @@ import org.dromara.common.ratelimiter.enums.LimitType;
|
|||||||
import org.dromara.common.redis.utils.RedisUtils;
|
import org.dromara.common.redis.utils.RedisUtils;
|
||||||
import org.dromara.common.web.core.WaveAndCircleCaptcha;
|
import org.dromara.common.web.core.WaveAndCircleCaptcha;
|
||||||
import org.dromara.common.web.config.properties.CaptchaProperties;
|
import org.dromara.common.web.config.properties.CaptchaProperties;
|
||||||
|
import org.dromara.common.sms.config.properties.SmsProperties;
|
||||||
import org.dromara.sms4j.api.SmsBlend;
|
import org.dromara.sms4j.api.SmsBlend;
|
||||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||||
import org.dromara.sms4j.core.factory.SmsFactory;
|
import org.dromara.sms4j.core.factory.SmsFactory;
|
||||||
@ -51,15 +52,28 @@ public class CaptchaController {
|
|||||||
|
|
||||||
private final CaptchaProperties captchaProperties;
|
private final CaptchaProperties captchaProperties;
|
||||||
private final MailProperties mailProperties;
|
private final MailProperties mailProperties;
|
||||||
|
private final SmsProperties smsProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 短信验证码
|
* 短信验证码
|
||||||
*
|
*
|
||||||
* @param phonenumber 用户手机号
|
* @param phonenumber 用户手机号
|
||||||
*/
|
*/
|
||||||
@RateLimiter(key = "#phonenumber", time = 60, count = 1)
|
|
||||||
@GetMapping("/resource/sms/code")
|
@GetMapping("/resource/sms/code")
|
||||||
public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
|
public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
|
||||||
|
if (!smsProperties.getEnabled()) {
|
||||||
|
return R.fail("当前系统没有开启短信功能!");
|
||||||
|
}
|
||||||
|
SpringUtils.getAopProxy(this).smsCodeImpl(phonenumber);
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短信验证码
|
||||||
|
* 独立方法避免短信功能关闭之后仍然走限流
|
||||||
|
*/
|
||||||
|
@RateLimiter(key = "#phonenumber", time = 60, count = 1)
|
||||||
|
public void smsCodeImpl(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
|
||||||
String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber;
|
String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber;
|
||||||
String code = RandomUtil.randomNumbers(4);
|
String code = RandomUtil.randomNumbers(4);
|
||||||
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
|
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
|
||||||
@ -71,9 +85,8 @@ public class CaptchaController {
|
|||||||
SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, templateId, map);
|
SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, templateId, map);
|
||||||
if (!smsResponse.isSuccess()) {
|
if (!smsResponse.isSuccess()) {
|
||||||
log.error("验证码短信发送异常 => {}", smsResponse);
|
log.error("验证码短信发送异常 => {}", smsResponse);
|
||||||
return R.fail(smsResponse.getData().toString());
|
throw new ServiceException(smsResponse.getData().toString());
|
||||||
}
|
}
|
||||||
return R.ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -156,6 +156,8 @@ mail:
|
|||||||
--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
|
--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
|
||||||
# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用
|
# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用
|
||||||
sms:
|
sms:
|
||||||
|
# 是否开启短信功能(默认关,与 mail 保持一致)
|
||||||
|
enabled: false
|
||||||
# 配置源类型用于标定配置来源(interface,yaml)
|
# 配置源类型用于标定配置来源(interface,yaml)
|
||||||
config-type: yaml
|
config-type: yaml
|
||||||
# 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制
|
# 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制
|
||||||
|
|||||||
@ -159,6 +159,8 @@ mail:
|
|||||||
--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
|
--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
|
||||||
# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用
|
# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用
|
||||||
sms:
|
sms:
|
||||||
|
# 是否开启短信功能(默认关,与 mail 保持一致)
|
||||||
|
enabled: false
|
||||||
# 配置源类型用于标定配置来源(interface,yaml)
|
# 配置源类型用于标定配置来源(interface,yaml)
|
||||||
config-type: yaml
|
config-type: yaml
|
||||||
# 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制
|
# 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制
|
||||||
|
|||||||
@ -0,0 +1,35 @@
|
|||||||
|
package org.dromara.test;
|
||||||
|
|
||||||
|
import org.dromara.common.core.domain.R;
|
||||||
|
import org.dromara.common.mail.config.properties.MailProperties;
|
||||||
|
import org.dromara.common.sms.config.properties.SmsProperties;
|
||||||
|
import org.dromara.common.web.config.properties.CaptchaProperties;
|
||||||
|
import org.dromara.web.controller.CaptchaController;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Tag;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CaptchaController.smsCode 关闭路径单元测试
|
||||||
|
* <p>直接构造 Controller 调用,不启动 Spring 上下文(无需 MySQL/Redis)
|
||||||
|
*/
|
||||||
|
@DisplayName("短信验证码开关单元测试")
|
||||||
|
public class CaptchaSmsCodeTest {
|
||||||
|
|
||||||
|
@Tag("dev")
|
||||||
|
@DisplayName("sms.enabled=false 时 smsCode 返回未开启提示且不发信")
|
||||||
|
@Test
|
||||||
|
public void shouldReturnFailWhenSmsDisabled() {
|
||||||
|
SmsProperties smsProperties = new SmsProperties();
|
||||||
|
smsProperties.setEnabled(false);
|
||||||
|
// 字段声明顺序决定构造参数顺序:captchaProperties, mailProperties, smsProperties
|
||||||
|
CaptchaController controller = new CaptchaController(
|
||||||
|
new CaptchaProperties(), new MailProperties(), smsProperties);
|
||||||
|
|
||||||
|
R<Void> result = controller.smsCode("13800000000");
|
||||||
|
|
||||||
|
Assertions.assertEquals(R.FAIL, result.getCode());
|
||||||
|
Assertions.assertEquals("当前系统没有开启短信功能!", result.getMsg());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
package org.dromara.test;
|
||||||
|
|
||||||
|
import org.dromara.common.sms.config.SmsAutoConfiguration;
|
||||||
|
import org.dromara.common.sms.config.properties.SmsProperties;
|
||||||
|
import org.dromara.common.sms.handler.SmsExceptionHandler;
|
||||||
|
import org.dromara.sms4j.api.dao.SmsDao;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Tag;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
|
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SmsProperties 绑定 + SmsAutoConfiguration 条件化测试
|
||||||
|
* <p>使用 ApplicationContextRunner,无需启动完整上下文(不需要 MySQL/Redis)
|
||||||
|
*/
|
||||||
|
@DisplayName("SMS 开关配置测试")
|
||||||
|
public class SmsPropertiesTest {
|
||||||
|
|
||||||
|
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||||
|
.withConfiguration(AutoConfigurations.of(SmsAutoConfiguration.class));
|
||||||
|
|
||||||
|
@Tag("dev")
|
||||||
|
@DisplayName("sms.enabled=false 时绑定到 SmsProperties.enabled=false")
|
||||||
|
@Test
|
||||||
|
public void shouldBindEnabledFalse() {
|
||||||
|
contextRunner
|
||||||
|
.withPropertyValues("sms.enabled=false")
|
||||||
|
.run(context -> {
|
||||||
|
assertThat(context).hasSingleBean(SmsProperties.class);
|
||||||
|
assertThat(context.getBean(SmsProperties.class).getEnabled()).isEqualTo(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Tag("dev")
|
||||||
|
@DisplayName("sms.enabled=true 时绑定到 SmsProperties.enabled=true")
|
||||||
|
@Test
|
||||||
|
public void shouldBindEnabledTrue() {
|
||||||
|
contextRunner
|
||||||
|
.withPropertyValues("sms.enabled=true")
|
||||||
|
.run(context -> {
|
||||||
|
assertThat(context).hasSingleBean(SmsProperties.class);
|
||||||
|
assertThat(context.getBean(SmsProperties.class).getEnabled()).isEqualTo(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Tag("dev")
|
||||||
|
@DisplayName("sms.enabled=false 时仍注册 SmsDao,但不创建 SmsExceptionHandler")
|
||||||
|
@Test
|
||||||
|
public void shouldAlwaysRegisterSmsDaoButOmitHandlerWhenDisabled() {
|
||||||
|
contextRunner
|
||||||
|
.withPropertyValues("sms.enabled=false")
|
||||||
|
.run(context -> {
|
||||||
|
assertThat(context).hasSingleBean(SmsDao.class);
|
||||||
|
assertThat(context).doesNotHaveBean(SmsExceptionHandler.class);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Tag("dev")
|
||||||
|
@DisplayName("sms.enabled=true 时创建 PlusSmsDao / SmsExceptionHandler")
|
||||||
|
@Test
|
||||||
|
public void shouldCreateSmsBeansWhenEnabled() {
|
||||||
|
contextRunner
|
||||||
|
.withPropertyValues("sms.enabled=true")
|
||||||
|
.run(context -> {
|
||||||
|
assertThat(context).hasSingleBean(SmsDao.class);
|
||||||
|
assertThat(context).hasSingleBean(SmsExceptionHandler.class);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,10 +1,13 @@
|
|||||||
package org.dromara.common.sms.config;
|
package org.dromara.common.sms.config;
|
||||||
|
|
||||||
|
import org.dromara.common.sms.config.properties.SmsProperties;
|
||||||
import org.dromara.common.sms.core.dao.PlusSmsDao;
|
import org.dromara.common.sms.core.dao.PlusSmsDao;
|
||||||
import org.dromara.common.sms.handler.SmsExceptionHandler;
|
import org.dromara.common.sms.handler.SmsExceptionHandler;
|
||||||
import org.dromara.sms4j.api.dao.SmsDao;
|
import org.dromara.sms4j.api.dao.SmsDao;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
|
|
||||||
@ -14,6 +17,7 @@ import org.springframework.context.annotation.Primary;
|
|||||||
* @author Feng
|
* @author Feng
|
||||||
*/
|
*/
|
||||||
@AutoConfiguration(after = {RedisAutoConfiguration.class})
|
@AutoConfiguration(after = {RedisAutoConfiguration.class})
|
||||||
|
@EnableConfigurationProperties(SmsProperties.class)
|
||||||
public class SmsAutoConfiguration {
|
public class SmsAutoConfiguration {
|
||||||
|
|
||||||
@Primary
|
@Primary
|
||||||
@ -26,6 +30,7 @@ public class SmsAutoConfiguration {
|
|||||||
* 异常处理器
|
* 异常处理器
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
|
@ConditionalOnProperty(prefix = "sms", name = "enabled", havingValue = "true")
|
||||||
public SmsExceptionHandler smsExceptionHandler() {
|
public SmsExceptionHandler smsExceptionHandler() {
|
||||||
return new SmsExceptionHandler();
|
return new SmsExceptionHandler();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,19 @@
|
|||||||
|
package org.dromara.common.sms.config.properties;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短信配置属性
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ConfigurationProperties(prefix = "sms")
|
||||||
|
public class SmsProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否开启短信功能(默认关闭,与 mail.enabled 保持一致)
|
||||||
|
*/
|
||||||
|
private Boolean enabled;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user