RuoYi-Vue-Plus/docs/superpowers/specs/2026-06-30-sms-toggle-design.md
Shenlijun 18f51687b0 docs 新增 sms 短信功能 enabled 开关设计文档
对齐 mail.enabled 的软开关方案:新增 SmsProperties、条件化 SmsAutoConfiguration、
CaptchaController.smsCode 调用层判断。详见 docs/superpowers/specs/2026-06-30-sms-toggle-design.md

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-30 07:35:14 +08:00

7.4 KiB

设计文档:为 SMS 增加 enabled 开关(对齐 mail)

  • 日期:2026-06-30
  • 分支:5.X
  • 状态:已批准(待实现)

1. 背景与目标

RuoYi-Vue-Plus 中,部分服务在配置文件里带开关,例如 mail.enabled 可以关闭邮件功能;但 sms 没有开关,短信功能始终启用。

目标:为 sms 增加一个 enabled 开关,使其与 mail.enabled 在语义与代码结构上完全对齐(软开关)。

2. 现状

2.1 Mail(有开关)——两层防护

  • 配置层:application-dev.ymlmail.enabled: false
  • Bean 层:MailConfig(ruoyi-common-mail/.../config/MailConfig.java)用 @ConditionalOnProperty(value="mail.enabled", havingValue="true") 创建 MailAccount;关掉时不创建该 Bean。
  • 调用层:CaptchaController.emailCode() 显式判断 mailProperties.getEnabled(),关掉时返回 R.fail("当前系统没有开启邮箱功能!"),并拆出独立的 emailCodeImpl() 承载 @RateLimiter,避免功能关闭后仍消耗限流配额。

2.2 SMS(无开关)

  • 配置层:application-dev.ymlsms: 只有 config-type / restricted / minute-max / account-max / blends,无 enabled
  • Bean 层:SmsAutoConfiguration(ruoyi-common-sms/.../config/SmsAutoConfiguration.java)无条件注册 PlusSmsDao(@Primary SmsDao,Redis 实现)与 SmsExceptionHandler;模块内没有 properties 类、也没有 SmsUtils
  • 第三方层:sms4j-spring-boot-starter 自身的自动配置读取 sms.blends.*,始终初始化 SmsFactory / SmsBlend
  • 调用层:直接调用 SmsFactory.getSmsBlend("config1")
    • 活跃调用方:CaptchaController.smsCode()(真实场景,短信验证码)、SmsController(demo)。
    • FlwCommonServiceImpl 中对 SmsFactory 的引用是注释代码,不计入范围。

2.3 关键差异

MailAccount项目自有 Bean,可用 @ConditionalOnProperty 精确掐断;SmsFactory第三方 sms4j 自动初始化的,无法用同一注解直接掐掉。本设计采用软开关:真正"关掉发送"由调用层判断完成;Bean 层只做与 MailConfig 对齐的清理(关掉时不创建项目自有的 SMS Bean)。

3. 决策记录

  • 开关语义:软开关(已确认)。sms.enabled=false 时:调用层返回友好"未开启"提示;项目自有的 PlusSmsDao / SmsExceptionHandler 不创建;sms4j starter 仍会在后台初始化(其默认 in-memory SmsDao 兜底,不影响应用启动,且不会真实发信)。
  • 默认值:false(与 mail 一致)。
  • 命名:sms.enabled(与 mail.enabled 完全一致)。
  • 范围:仅覆盖真实调用方 CaptchaController.smsCode();demo SmsController 不动。

4. 详细设计

4.1 配置文件

文件:ruoyi-admin/src/main/resources/application-dev.ymlapplication-prod.yml

sms: 节点顶部新增 enabled:

--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
sms:
  # 是否开启短信功能(默认关,与 mail 保持一致)
  enabled: false
  # 配置源类型用于标定配置来源(interface,yaml)
  config-type: yaml
  # ...其余原配置不变

4.2 新增 SmsProperties

文件:ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/properties/SmsProperties.java

package org.dromara.common.sms.config.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * 短信配置属性
 */
@Data
@ConfigurationProperties(prefix = "sms")
public class SmsProperties {

    /**
     * 是否开启短信功能
     */
    private Boolean enabled;
}

sms 前缀与 sms4j 的 blends 等配置共存。Spring Boot 允许同一前缀绑定多个 @ConfigurationProperties 类,只要字段不冲突;enabled 不属于 sms4j 使用的字段。该假设须在实现阶段通过启动验证。

4.3 SmsAutoConfiguration 条件化

文件:ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/SmsAutoConfiguration.java

  • 类上新增 @EnableConfigurationProperties(SmsProperties.class)
  • smsDao()smsExceptionHandler() 两个 @Bean 方法各加 @ConditionalOnProperty(prefix = "sms", name = "enabled", havingValue = "true")

效果:sms.enabled=false 时不创建 PlusSmsDaoSmsExceptionHandler(对齐 MailConfig 关掉后不创建 MailAccount)。

4.4 CaptchaController.smsCode 调用层判断

文件:ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java

照搬 emailCode / emailCodeImpl 的拆分手法:

  1. 类字段注入 SmsProperties(与已有的 MailProperties 并列)。
  2. smsCode(String phonenumber):
    • 保留 @GetMapping("/resource/sms/code")
    • 移除方法上的 @RateLimiter
    • 开头判断 if (!smsProperties.getEnabled()) return R.fail("当前系统没有开启短信功能!");
    • 否则调用 SpringUtils.getAopProxy(this).smsCodeImpl(phonenumber);return R.ok();
  3. 新增 smsCodeImpl(String phonenumber):
    • 标注 @RateLimiter(key = "#phonenumber", time = 60, count = 1)
    • 承载原 smsCode 内部的验证码生成、Redis 缓存、SmsFactory.getSmsBlend("config1").sendMessage(...) 逻辑。

这样功能关闭时不再消耗限流配额,与 emailCode 的实现一致。

5. 不在范围内(YAGNI)

  • 不新建 SmsUtils 包装类:现有调用方直接使用 SmsFactory,开关不需要它。
  • 不修改 demo SmsController:仅测试用途;如需一致可后续补充。
  • 不改动 sms4j 自身的自动配置:属于"硬开关"范畴,本次明确排除。

6. 风险与验证(TDD)

风险 验证方式
sms 前缀与 sms4j 共存绑定时启动报错 启动 Spring 上下文,断言 SmsProperties.enabled 能被正确读取
关掉 PlusSmsDao 后 sms4j 找不到 SmsDao 而崩溃 sms.enabled=false 下上下文正常启动,sms4j 使用默认 in-memory SmsDao 兜底

PlusSmsDao 条件化的回退决策:实现阶段先验证 sms4j 是否自带默认 SmsDao

  • 自带默认 SmsDao:按设计对 PlusSmsDao 加条件化(关掉时用默认兜底)。
  • 不自带且启动强依赖 SmsDao Bean:则 PlusSmsDao 保持无条件注册(始终提供 Redis 实现),仅对 SmsExceptionHandler 加条件化,真正"关掉发送"完全由 4.4 的调用层判断承担。届时回退方案不影响本设计的开关语义(仍是软开关)。 | 开关行为不符合预期 | enabled=false:无 PlusSmsDao Bean + 调用 /resource/sms/code 返回"未开启"提示;enabled=true:逻辑与现状一致 |

测试要点:

  • sms.enabled=false:(1) 不存在 PlusSmsDao Bean;(2) 调用 smsCode 返回失败提示且进入 smsCodeImpl;(3) 上下文正常启动。
  • sms.enabled=true:PlusSmsDao 存在且为 @Primary;smsCode 正常进入发送流程(与改动前行为一致)。

7. 影响面

  • 配置:application-dev.ymlapplication-prod.yml(sms: 节点新增 enabled)。
  • 代码:
    • 新增 SmsProperties.java
    • 修改 SmsAutoConfiguration.java
    • 修改 CaptchaController.java(smsCode 拆分 + 注入 SmsProperties)。
  • 注册:SmsAutoConfiguration 已在 ruoyi-common-smsAutoConfiguration.imports 中,无需改动;新增的 SmsProperties 通过 @EnableConfigurationProperties 注册,无需改动 imports。
  • 兼容性:默认 false 与 mail 一致;现有未配置 sms.enabled 的环境需显式设置才能开启短信功能。