当前位置: 首页 > news >正文

阿里云短信发送(工厂模式+模板方法+策略模式实现)

文章目录

  • 一、业务场景
    • 1.1 验证码登录
    • 1.2 手机号注册
    • 1.3 忘记密码
    • 1.4 其他场景.....
  • 二. 准备工作
    • 1.注册阿里云账号
    • 2.阿里云申请签名
    • 3.阿里云短信模版
    • 4.阿里云短信控制台测试
  • 三、功能规划
    • 1. 设计模式
      • 1.1工厂模式:根据不同业务创建不同的工厂类
      • 1.1策略模式:每个类有不同的短信模版内容
      • 1.2 模版方法模式:每个类 发送时,都需要提前进行业务校验
    • 2.不同业务类型使用不同的缓存key
  • 四、业务实现
    • 1.引入jar
    • 2.yam配置信息及对应配置类
    • 3. 业务工厂: 获取对应业务模版
    • 4. 发送短信模版:用来发送短信
    • 5. 业务模版:用来校验
      • 5.1 登录
      • 5.2 注册
      • 5.3 忘记密码
    • 6.短信缓存redis
    • 7. 请求入口:
    • 8. 参数:SmsReqAo

提示:以下是本篇文章正文内容,下面案例可供参考

一、业务场景

每种业务场景对手机号校验方式可能都不相同

1.1 验证码登录

校验:手机号在系统是否存在,存在则发送短信

1.2 手机号注册

校验:手机号在系统是否已经注册,未注册则发送短信

1.3 忘记密码

校验:手机号在系统是否存在,存在则发送短信

1.4 其他场景…

二. 准备工作

1.注册阿里云账号

2.阿里云申请签名

3.阿里云短信模版

4.阿里云短信控制台测试

三、功能规划

1. 设计模式

1.1工厂模式:根据不同业务创建不同的工厂类

1.1策略模式:每个类有不同的短信模版内容

1.2 模版方法模式:每个类 发送时,都需要提前进行业务校验

作用:提高代码的可扩展性

2.不同业务类型使用不同的缓存key

四、业务实现

1.引入jar

<dependency><groupId>com.aliyun</groupId><artifactId>dysmsapi20170525</artifactId><version>3.1.0</version></dependency>

2.yam配置信息及对应配置类

@Configuration@ConfigurationProperties(prefix="aliyun.sms")@DatapublicclassAliyunSmsConfig{// 阿里云短信服务accessKeyIdprivateStringaccessKeyId;// 阿里云短信服务accessKeySecretprivateStringaccessKeySecret;// 短信签名privateStringsignName;// 短信发送地区privateStringregionId;// 短信模板privateMap<String,String>templates;publicStringgetTemplateCode(Stringkey){StringtemplateCode=templates.get(key);ArgumentAssert.notBlank(templateCode,"短信模板不存在");returntemplateCode;}}

3. 业务工厂: 获取对应业务模版

@Service@AllArgsConstructor@Slf4jpublicclassSmsTemplateFactory{privatefinalLoginSmsTemplateloginSmsTemplate;privatefinalRegisterSmsTemplateregisterSmsTemplate;privatefinalForgotPasswordSmsTemplateforgotPasswordSmsTemplate;/** * 根据场景类型获取对应的模板 * * @param templateKey 场景类型(login/register/forgotPassword) * @return 对应的模板实现类 */publicSmsTemplategetTemplate(StringtemplateKey){returnswitch(templateKey){case"login"->loginSmsTemplate;case"register"->registerSmsTemplate;case"forgotPassword"->forgotPasswordSmsTemplate;default->thrownewIllegalArgumentException("未知的场景类型:"+templateKey);};}}

4. 发送短信模版:用来发送短信

@Slf4jpublicabstractclassSmsTemplate{protectedabstractvoidcheckBiz(StringphoneNumber);/** * 发送短信验证码 * * @param templateKey 模板key * @param phoneNumber 手机号 * @return 是否发送成功 */publicbooleansendSms(StringtemplateKey,StringphoneNumber){checkBiz(phoneNumber);SmsCodeCachesmsCodeCache=SpringUtils.getBean(SmsCodeCache.class);// 检查发送频率(60秒内只能发送一次)if(!smsCodeCache.checkRateLimit(phoneNumber,60)){thrownewRuntimeException("发送频率过高,请稍后再试");}Stringcode=this.generateCode();// 生成验证码booleanisSuccess=this.sendSmsToAliyun(templateKey,phoneNumber,code);// 调用阿里云发送短信if(isSuccess){// 缓存验证码,有效期5分钟smsCodeCache.cacheCode(templateKey,phoneNumber,code,5);}returnisSuccess;}/** * 调用阿里云短信服务发送短信 * * @param templateKey 模板key * @param phoneNumber 手机号 * @param code 验证码 * @return 是否发送成功 */privatebooleansendSmsToAliyun(StringtemplateKey,StringphoneNumber,Stringcode){AliyunSmsConfigsmsConfig=SpringUtils.getBean(AliyunSmsConfig.class);StringtemplateCode=smsConfig.getTemplateCode(templateKey);try{// 初始化客户端Configconfig=newConfig().setAccessKeyId(smsConfig.getAccessKeyId()).setAccessKeySecret(smsConfig.getAccessKeySecret()).setRegionId(smsConfig.getRegionId());Clientclient=newClient(config);// 构建请求SendSmsRequestrequest=newSendSmsRequest().setPhoneNumbers(phoneNumber).setSignName(smsConfig.getSignName()).setTemplateCode(templateCode).setTemplateParam("{\"code\":\""+code+"\"}");// 发送短信SendSmsResponseresponse=client.sendSms(request);return"OK".equals(response.getBody().getCode());}catch(Exceptione){log.error("发送短信验证码失败,模版:{},手机号:{},验证码:{},错误:{}",templateKey,phoneNumber,code,e.getMessage());returnfalse;}}/** * 生成6位随机验证码 * * @return 验证码 */privateStringgenerateCode(){Randomrandom=newRandom();returnString.format("%06d",random.nextInt(1000000));}}

5. 业务模版:用来校验

5.1 登录

@Component@AllArgsConstructorpublicclassLoginSmsTemplateextendsSmsTemplate{privatefinalBaseStaffServicebaseStaffService;@OverridepublicvoidcheckBiz(StringphoneNumber){BaseStaffstaff=baseStaffService.getStaffByTelephone(phoneNumber);ArgumentAssert.notNull(staff,"该手机号不存在");}}

5.2 注册

@Component@AllArgsConstructorpublicclassRegisterSmsTemplateextendsSmsTemplate{privatefinalBaseStaffServicebaseStaffService;@OverridepublicvoidcheckBiz(StringphoneNumber){BaseStaffstaff=baseStaffService.getStaffByTelephone(phoneNumber);ArgumentAssert.isNull(staff,"该手机号已注册");}}

5.3 忘记密码

@Component@AllArgsConstructorpublicclassForgotPasswordSmsTemplateextendsSmsTemplate{privatefinalBaseStaffServicebaseStaffService;@OverridepublicvoidcheckBiz(StringphoneNumber){BaseStaffstaff=baseStaffService.getStaffByTelephone(phoneNumber);ArgumentAssert.notNull(staff,"该手机号不存在");}}

6.短信缓存redis

@Component@AllArgsConstructorpublicclassSmsCodeCache{privatefinalRedisTemplate<String,String>redisTemplate;// 验证码缓存前缀privatestaticfinalStringCODE_PREFIX="sms:code:";// 发送频率限制前缀privatestaticfinalStringRATE_LIMIT_PREFIX="sms:rate:";/** * 缓存验证码 * * @param templateKey 模板key * @param phoneNumber 手机号 * @param code 验证码 * @param expireTime 过期时间(分钟) */publicvoidcacheCode(StringtemplateKey,StringphoneNumber,Stringcode,longexpireTime){Stringkey=CODE_PREFIX+templateKey+StrPool.COLON+phoneNumber;redisTemplate.opsForValue().set(key,code,expireTime,TimeUnit.MINUTES);}/** * 获取缓存的验证码 * * @param templateKey 模板key * @param phoneNumber 手机号 * @return 验证码 */publicStringgetCachedCode(StringtemplateKey,StringphoneNumber){Stringkey=CODE_PREFIX+templateKey+StrPool.COLON+phoneNumber;returnredisTemplate.opsForValue().get(key);}/** * 删除缓存的验证码 * * @param templateKey 模板key * @param phoneNumber 手机号 */publicvoiddeleteCode(StringtemplateKey,StringphoneNumber){Stringkey=CODE_PREFIX+templateKey+StrPool.COLON+phoneNumber;redisTemplate.delete(key);}/** * 检查发送频率 * * @param phoneNumber 手机号 * @param interval 间隔时间(秒) * @return 是否可以发送 */publicbooleancheckRateLimit(StringphoneNumber,longinterval){Stringkey=RATE_LIMIT_PREFIX+phoneNumber;LonglastSentTime=redisTemplate.opsForValue().getOperations().getExpire(key);if(lastSentTime!=null&&lastSentTime>0){returnfalse;// 还在限制时间内}redisTemplate.opsForValue().set(key,"1",interval,TimeUnit.SECONDS);returntrue;}}

7. 请求入口:

@PostMapping("getSmsCode")@ApiOperation(value="获取短信验证码")publicDataResponse<Boolean>getSmsCode(@RequestBody@ValidatedSmsReqAoao){StringtemplateKey=ao.getTemplateKey();returnDataResponse.builderSuccess(smsTemplateFactory.getTemplate(templateKey).sendSms(templateKey,ao.getTelephone()));}

8. 参数:SmsReqAo

@Data@ApiModel("短信求入参")publicclassSmsReqAoimplementsSerializable{privatestaticfinallongserialVersionUID=1L;@NotBlank(message="模版KEY不能为空")@ApiModelProperty(value="模版KEY",required=true)StringtemplateKey;/** * 手机号 */@ApiModelProperty(value="手机号",required=true)@NotBlank(message="手机号不能为为空")@Pattern(regexp="^1[3-9]\\d{9}$",message="手机号格式不正确")privateStringtelephone;}
http://www.jsqmd.com/news/814937/

相关文章:

  • 护发精油十大品牌推荐:来自榜单的6款精选好物 - 速递信息
  • FinRL-Library回测框架:从历史数据到实盘交易的终极指南
  • 别让“随便买一个”耽误了您的聆听,从助听器购买看安湃声助听器怎么样? - 博客万
  • 内江除甲醛CMA甲醛检测治理公司公共卫生检测报告排行榜(2026版) - 张诗林资源库
  • 高效一键解锁12种加密音乐:Unlock Music免费开源工具完全指南
  • 2026兰州驾校测评推荐:5家正规驾校横向对比,新手选型不踩坑 - 深度智识库
  • React-Markdown完全指南:如何在React应用中安全高效地渲染Markdown内容
  • 【职场】为什么你在公司越老实,死得越快
  • Simulink Assignment模块实战:如何像写C代码一样更新数组元素?
  • 英雄联盟工具箱完整指南:5分钟掌握League Akari高效游戏辅助
  • AI治理步入深水区、终端国标落地、量子算力上线——国产AI生态迎来里程碑式“三重奏”
  • Validity90图像格式揭秘:从原始数据到PNG指纹图像
  • 3分钟掌握Navicat密码解密工具:轻松找回遗忘的数据库连接密码
  • 宁波除甲醛CMA甲醛检测治理公司公共卫生检测报告排行榜(2026版) - 张诗林资源库
  • BetterNCM安装器:一键解锁网易云音乐高级功能
  • 别再乱买了!空气泵选购建议+避坑指南,小白必看 - 品牌推荐大师
  • Go微服务开发利器:Kratos Blades工具链实战指南
  • 漳州除甲醛CMA甲醛检测治理公司公共卫生检测报告排行榜(2026版) - 张诗林资源库
  • 交变盐雾腐蚀试验箱什么牌子好?用户口碑与参数权重分析 - 品牌推荐大师1
  • SciDownl终极指南:让学术文献下载效率提升500%的Python工具
  • 英飞凌TC397开发板KIT_A2G_TC397_5V_TFT开箱与快速上手(附3.3V版选购建议)
  • 基于MCP协议的Telegram机器人开发:构建AI智能体与自动化流程的桥梁
  • QQ-Groups-Spider:一键获取海量QQ群数据的终极解决方案
  • TheCherno——Engine(十七)渲染开始之前
  • 给图像传感器做‘体检’:手把手教你用PQTool完成ISP三大基础校正(BLC/AWB/CCM)
  • 长春除甲醛CMA甲醛检测治理公司公共卫生检测报告排行榜(2026版) - 张诗林资源库
  • vscode-eslint的10个强大功能:从自动修复到多语言支持
  • RK3288系统镜像“瘦身”与“增肥”指南:如何精准控制Debian rootfs.img的大小
  • 阿坝除甲醛CMA甲醛检测治理公司公共卫生检测报告排行榜(2026版) - 张诗林资源库
  • 构建本地AI助手:离线优先架构、隐私保护与自动化实战