# 短信服务架构说明 ## 设计理念 短信服务采用**通用化、可扩展**的设计: - ✅ 配置保持通用,不绑定特定服务商 - ✅ 支持多种短信服务商切换 - ✅ 易于扩展新的服务商 - ✅ 统一的对外接口 ## 架构设计 ### 1. 配置层(通用化) ```yaml sms: enabled: false # 是否启用 provider: aliyun # 服务商选择 access-key-id: xxx # 通用配置 access-key-secret: xxx sign-name: xxx template-code: xxx region-id: xxx ``` **设计优势**: - 配置不带服务商前缀,保持通用性 - 通过 `provider` 灵活切换服务商 - 未来增加服务商无需修改配置结构 ### 2. 工具类层(SmsUtils) ```java @Component public class SmsUtils { // 通用配置 @Value("${sms.provider:aliyun}") private String provider; // 对外统一接口 public boolean sendVerificationCode(String phone, String code) { switch (provider) { case "aliyun": return sendByAliyun(phone, code); case "tencent": return sendByTencent(phone, code); default: return false; } } // 各服务商的私有实现 private boolean sendByAliyun(String phone, String code) { ... } private boolean sendByTencent(String phone, String code) { ... } } ``` **设计优势**: - 对外统一接口:`sendVerificationCode()` - 各服务商只是内部的私有方法 - 调用方无需关心使用哪个服务商 - 新增服务商只需添加一个私有方法 ### 3. 调用层(AuthController) ```java @Autowired private SmsUtils smsUtils; public ResultDomain sendSmsCode(...) { // 直接调用统一接口,无需关心服务商 boolean success = smsUtils.sendVerificationCode(phone, code); ... } ``` ## 支持的服务商 ### 当前已实现 - ✅ **阿里云**(dysmsapi20170525) ### 待实现 - ⏳ **腾讯云**(预留接口) - ⏳ **华为云**(预留接口) ## 如何添加新服务商 ### 步骤1:添加Maven依赖 在 `common-util/pom.xml` 中添加对应SDK: ```xml com.tencentcloudapi tencentcloud-sdk-java 3.x.x ``` ### 步骤2:实现私有方法 在 `SmsUtils.java` 中添加私有实现方法: ```java /** * 使用腾讯云发送短信验证码 */ private boolean sendByTencent(String phone, String code) { try { // 腾讯云SDK调用逻辑 ... return true; } catch (Exception e) { logger.error("腾讯云短信发送失败", e); return false; } } ``` ### 步骤3:添加到分发逻辑 在 `sendVerificationCode()` 方法中添加case: ```java switch (provider.toLowerCase()) { case "aliyun": return sendByAliyun(phone, code); case "tencent": return sendByTencent(phone, code); // 新增 default: logger.error("未知的短信服务商: {}", provider); return false; } ``` ### 步骤4:配置切换 修改 `application.yml`: ```yaml sms: provider: tencent # 切换到腾讯云 ``` 完成!无需修改调用方代码。 ## 开发模式 ### 模拟模式(推荐用于开发) ```yaml sms: enabled: false # 关闭真实发送 ``` **特点**: - 不会实际发送短信 - 验证码输出到日志 - 不产生任何费用 - 便于本地调试 ### 真实发送模式 ```yaml sms: enabled: true provider: aliyun access-key-id: xxx access-key-secret: xxx ``` ## 最佳实践 ### 1. 环境隔离 **开发环境**: ```yaml sms: enabled: false # 模拟模式 ``` **生产环境**: ```yaml sms: enabled: true provider: aliyun access-key-id: ${SMS_ACCESS_KEY_ID} # 从环境变量读取 access-key-secret: ${SMS_ACCESS_KEY_SECRET} ``` ### 2. 安全性 - ❌ 不要将 AccessKey 写在代码中 - ❌ 不要将 AccessKey 提交到 Git - ✅ 使用环境变量或配置中心 - ✅ 使用 RAM 子账号而非主账号 - ✅ 定期更换密钥 ### 3. 可靠性 ```java // 系统已实现: - 60秒发送频率限制(防刷) - 10分钟验证码有效期 - Redis存储验证码 - 手机号格式验证 // 建议增加: - 图形验证码前置 - IP限流 - 黑名单机制 - 发送失败重试 ``` ### 4. 监控告警 建议监控指标: - 短信发送成功率 - 短信发送量(防异常消耗) - 验证码验证成功率 - 单个手机号发送频率 ## 优势总结 ### 对比旧方案 **旧方案**(绑定服务商): ```yaml aliyun: sms: enabled: true ``` - ❌ 配置绑定服务商 - ❌ 切换服务商需要大量修改 - ❌ 扩展性差 **新方案**(通用化): ```yaml sms: provider: aliyun enabled: true ``` - ✅ 配置保持通用 - ✅ 切换服务商只需修改 `provider` - ✅ 扩展性强,易于维护 ### 符合设计原则 - **开闭原则**:对扩展开放,对修改关闭 - **单一职责**:每个服务商实现独立 - **依赖倒置**:调用方依赖抽象接口,不依赖具体实现 - **里氏替换**:各服务商实现可以互相替换 ## 总结 这个架构设计的核心理念是: 1. **配置通用化**:不绑定特定服务商 2. **实现私有化**:服务商只是一个方法 3. **接口统一化**:对外提供统一接口 4. **扩展简单化**:新增服务商仅需3步 这样的设计既满足当前需求,又具备良好的扩展性,是一个**优雅、实用、易维护**的架构方案。