目前系统集成短信似乎是必不可少的部分,由于各种云平台都提供了不同的短信通道,这里我们增加多租户多通道的短信验证码,并增加配置项,使系统可以支持多家云平台提供的短信服务。这里以阿里云和腾讯云为例,集成短信通知服务。
1、在gitegg-platform中新建gitegg-platform-sms基础工程,定义抽象方法和配置类
smsndrvice发送短信抽象接口:
/** * 短信发送接口 */public interface smsndrvice { /** * 发送单个短信 * @param smsdata * @param phonenumber * @return */ default smsrespon ndsms(smsdata smsdata, string phonenumber){ if (strutil.impty(phonenumber)) { return new smsrespon(); } return this.ndsms(smsdata, collections.singletonlist(phonenumber)); } /** * 群发发送短信 * @param smsdata * @param phonenumbers * @return */ smsrespon ndsms(smsdata smsdata, collection<string> phonenumbers); }
smsresultcodeenum定义短信发送结果
/** * @classname: resultcodeenum * @description: 自定义返回码枚举 * @author gitegg * @date 2020年09月19日 下午11:49:45 */@getter@allargsconstructorpublic enum smsresultcodeenum { /** * 成功 */ success(200, "操作成功"), /** * 系统繁忙,请稍后重试 */ error(429, "短信发送失败,请稍后重试"), /** * 系统错误 */ phone_number_error(500, "手机号错误"); public int code; public string msg;}
2、新建gitegg-platform-sms-aliyun工程,实现阿里云短信发送接口
aliyunsmsproperties配置类
@data@component@configurationproperties(prefix = "sms.aliyun")public class aliyunsmsproperties { /** * product */ private string product = "dysmsapi"; /** * domain */ private string domain = "dysmsapi.aliyuncs.com"; /** * regionid */ private string regionid = "cn-hangzhou"; /** * accesskeyid */ private string accesskeyid; /** * accesskeycret */ private string accesskeycret; /** * 短信签名 */ private string signname;}
aliyunsmsndrviceimpl阿里云短信发送接口实现类
/** * 阿里云短信发送 */@slf4j@allargsconstructorpublic class aliyunsmsndrviceimpl implements smsndrvice { private static final string successcode = "ok"; private final aliyunsmsproperties properties; private final iacsclient acsclient; @override public smsrespon ndsms(smsdata smsdata, collection<string> phonenumbers) { smsrespon smsrespon = new smsrespon(); ndsmsrequest request = new ndsmsrequest(); request.tsysmethod(methodtype.post); request.tphonenumbers(strutil.join(",", phonenumbers)); request.tsignname(properties.getsignname()); request.ttemplatecode(smsdata.gettemplateid()); request.ttemplateparam(jsonutils.maptojson(smsdata.getparams())); try { ndsmsrespon ndsmsrespon = acsclient.getacsrespon(request); if (null != ndsmsrespon && !stringutils.impty(ndsmsrespon.getcode())) { if (this.successcode.equals(ndsmsrespon.getcode())) { smsrespon.tsuccess(true); } el { log.error("nd aliyun sms fail: [code={}, message={}]", ndsmsrespon.getcode(), ndsmsrespon.getmessage()); } smsrespon.tcode(ndsmsrespon.getcode()); smsrespon.tmessage(ndsmsrespon.getmessage()); } } catch (exception e) { e.printstacktrace(); log.error("nd aliyun sms fail: {}", e); smsrespon.tmessage("nd aliyun sms fail!"); } return smsrespon; }}
3、新建gitegg-platform-sms-tencent工程,实现腾讯云短信发送接口
tencentsmsproperties配置类
@data@component@configurationproperties(prefix = "sms.tencent")public class tencentsmsproperties { /* 填充请求参数,这里 request 对象的成员变量即对应接口的入参 * 您可以通过官网接口文档或跳转到 request 对象的定义处查看请求参数的定义 * 基本类型的设置: * 帮助链接: * 短信控制台:/d/file/titlepic/login * sms helper:https清朝的名人://cloud.tencent.com/document/product/382/3773 */ /* 短信应用 id: 在 [短信控制台] 添加应用后生成的实际 sdkappid,例如1400006666 */ private string smssdkappid; /* 国际/港澳台短信 nderid: 国内短信填空,默认未开通,如需开通请联系 [sms helper] */ private string nderid; /* 短信码号扩展号: 默认未开通,如需开通请联系 [sms helper] */ private string extendcode; /** * 短信签名 */ private string signname;}
tencentsmsndrviceimpl腾讯云短信发送接口实现类
/** * 腾讯云短信发送 */@slf4j@allargsconstructorpublic class tencentsmsndrviceimpl implements smsndrvice { private static final string successcode = "ok"; private final tencentsmsproperties properties; private final smsclient client; @override public smsrespon ndsms(smsdata smsdata, collection<string> phonenumbers) { smsrespon smsrespon = new smsrespon(); ndsmsrequest request = new ndsmsrequest(); request.tsmssdkappid(properties.getsmssdkappid()); /* 短信签名内容: 使用 utf-8 编码,必须填写已审核通过的签名,可登录 [短信控制台] 查看签名信息 */ request.tsign(properties.getsignname()); /* 国际/港澳台短信 nderid: 国内短信填空,默认未开通,如需开通请联系 [sms helper] */ if (!stringutils.impty(properties.getnderid())) { request.tnderid(properties.getnderid()); } request.ttemplateid(smsdata.gettemplateid()); /* 下发手机号码,采用 e.164 标准,+[国家或地区码][手机号] * 例如+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号*/ string[] phonenumbersarray = (string[]) phonenumbers.toarray(); request.tphonenumbert(phonenumbersarray); /* 模板参数: 若无模板参数,则设置为空*/ string[] templateparams = new string[]{}; if (!collectionutils.impty(smsdata.getparams())) { templateparams = (string[]) smsdata.getparams().values().toarray(); } request.ttemplateparamt(templateparams); try { /* 通过 client 对象调用 ndsms 方法发起请求。注意请求方法名与请求对象是对应的 * 返回的 res 是一个 ndsmsrespon 类的实例,与请求对象对应 */ ndsmsrespon ndsmsrespon = client.ndsms(request); //如果是批量发送,那么腾讯云短信会返回每条短信的发送状态,这里默认返回第一条短信的状态 if (null != ndsmsrespon && null != ndsmsrespon.getndstatust()) { ndstatus ndstatus = ndsmsrespon.getndstatust()[0]; if (this.successcode.equals(ndstatus.getcode())) { smsrespon.tsuccess(true); } el { smsrespon.tcode(ndstatus.getcode()); smsrespon.tmessage(ndstatus.getmessage()); } } } catch (exception e) { e.printstacktrace(); log.error("nd aliyun sms fail: {}", e); smsrespon.tmessage("nd aliyun sms fail!"); } return smsrespon; }}
4、在gitegg-cloud中新建业务调用方法,这里要考虑到不同租户调用不同的短信配置进行短信发送,所以新建smsfactory短信接口实例化工厂,根据不同的租户实例化不同的短信发送接口,这里以实例化com.gitegg.rvice.extens狐假虎威近义词ion.sms.factory.smsaliyunfactory类为例,进行实例化操作,实际使用中,这里需要配置和租户的对应关系,从租户的短信配置中获取。
@componentpublic class smsfactory { private final ismstemplatervice smstemplatervice; /** * smsndrvice 缓存 */ private final map<long, smsndrvice=""> smsndrvicemap = new concurrenthashmap<>(); public smsfactory(ismstemplatervice smstemplatervice) { this.smstemplatervice = smstemplatervice; } /** * 获取 smsndrvice * * @param smstemplatedto 短信模板 * @return smsndrvice */ public smsndrvice getsmsndrvice(smstemplatedto smstemplatedto) { //根据channelid获取对应的发送短信服务接口,channelid是唯一的,每个租户有其自有的channelid long channelid = smstemplatedto.getchannelid(); smsndrvice smsndrvice = smsndrvicemap.get(channelid); if (null == smsndrvice) { class cls = null; try { cls = class.forname("com.gitegg.rvice.extension.sms.factory.smsaliyunfactory"); method staticmethod = cls.getdeclaredmethod("getsmsndrvice", smstemplatedto.class); smsndrvice = (smsndrvice) staticmethod.invoke(cls,smstemplatedto); smsndrvicemap.put(channelid, smsndrvice); } catch (classnotfoundexception | nosuchmethodexception e) { e.printstacktrace(); } catch (illegalaccesxception e) { e.printstacktrace(); } catch (invocationtargetexception e) { e.printstacktrace(); } } return smsndrvice; }}
/** * 阿里云短信服务接口工厂类 */public class smsaliyunfactory { public static smsndrvice getsmsndrvice(smstemplatedto sms) { aliyunsmsproperties aliyunsmsproperties = new aliyunsmsproperties(); aliyunsmsproperties.taccesskeyid(sms.getcretid()); aliyunsmsproperties.taccesskeycret(sms.getcretkey()); aliyunsmsproperties.tregionid(sms.getregionid()); aliyunsmsproperties.tsignname(sms.getsignname()); iclientprofile profile = defaultprofile.getprofile(aliyunsmsproperties.getregionid(), aliyunsmsproperties.getaccesskeyid(), aliyunsmsproperties.getaccesskeycret()); iacsclient acsclient = new defaultacsclient(profile); return new aliyunsmsndrviceimpl(aliyunsmsproperties, acsclient); }}
/** * 腾讯云短信服务接口工厂类 */public class smstencentfactory { public static smsndrvice getsmsndrvice(smstemplatedto sms) { tencentsmsproperties tencentsmsproperties = new tencentsmsproperties(); tencentsmsproperties.tsmssdkappid(sms.getcretid()); tencentsmsproperties.textendcode(sms.getcretkey()); tencentsmsproperties.tnderid(sms.getregionid()); tencentsmsproperties.tsignname(sms.getsignname()); /* 必要步骤: * 实例化一个认证对象,入参需要传入腾讯云账户密钥对 cretid 和 cretkey * 本示例采用从环境变量读取的方式,需要预先在环境变量中设置这两个值 * 您也可以直接在代码中写入密钥对,但需谨防泄露,不要将代码复制、上传或者分享给他人 * cam 密钥查询:/d/file/titlepic/login */ credential cred = new credential(sms.getcretid(), sms.getcretkey()); // 实例化一个 http 选项,可选,无特殊需求时可以跳过 httpprofile httpprofile = new httpprofile(); // 设置代理// httpprofile.tproxyhost("host");// httpprofile.tp亚热带地区roxyport(port); /* sdk 默认使用 post 方法。 * 如需使用 get 方法,可以在此处设置,但 get 方法无法处理较大的请求 */ httpprofile.treqmethod("post"); /* sdk 有默认的超时时间,非必要请不要进行调整 * 如有需要请在代码中查阅以获取最新的默认值 */ httpprofile.tconntimeout(60); /* sdk 会自动指定域名,通常无需指定域名,但访问金融区的服务时必须手动指定域名 * 例如 sms 的上海金融区域名为 sms.ap-shanghai-fsi.tencentcloudapi.com */ if (!stringutils.impty(sms.getregionid())) { httpprofile.tendpoint(sms.getregionid()); } /* 非必要步骤: * 实例化一个客户端配置对象,可以指定超时时间等配置 */ clientprofile clientprofile = new clientprofile(); /* sdk 默认用 tc3-hmac-sha256 进行签名 * 非必要请不要修改该字段 */ clientprofile.tsignmethod("hmacsha256"); clientprofile.thttpprofile(httpprofile); /* 实例化 sms 的 client 对象 * 第二个参数是地域信息,可以直接填写字符串 ap-guangzhou,或者引用预设的常量 */ smsclient client = new smsclient(cred, "",clientprofile); return new tencentsmsndrviceimpl(tencentsmsproperties, client); }}
5、定义短信发送接口及实现类
ismsrvice业务短信发送接口定义
/** * <p> * 短信发送接口定义 * </p> * * @author gitegg * @since 2021-01-25 */public interface ismsrvice { /** * 发送短信 * * @param smscode * @param smsdata * @param phonenumbers * @return */ smsrespon ndsmsnormal(string smscode, string smsdata, string phonenumbers); /** * 发送短信验证码 * * @param smscode * @param phonenumber * @return */ smsrespon ndsmsverificationcode( string smscode, string phonenumber); /** * 校验短信验证码 * * @param smscode * @param phonenumber * @return */ boolean checksmsverificationcode(string smscode, string phonenumber, string verificationcode);}
smsrviceimpl 短信发送接口实现类
/** * <p> * 短信发送接口实现类 * </p> * * @author gitegg * @since 2021-01-25 */@slf4j@rvice@requiredargsconstructor(onconstructor_ = @autowired)public class smsrviceimpl implements ismsrvice { private final smsfactory smsfactory; private final ismstemplatervice smstemplatervice; private final redistemplate rediste元旦快乐英文mplate; @override public smsrespon ndsmsnormal(string smscode, string smsdata, string phonenumbers) { smsrespon smsrespon = new smsrespon(); try { querysmstemplatedto querysmstemplatedto = new querysmstemplatedto(); querysmstemplatedto.tsmscode(smscode); //获取短信code的相关信息,租户信息会根据mybatis plus插件获取 smstemplatedto smstemplatedto = smstemplatervice.querysmstemplate(querysmstemplatedto); objectmapper mapper = new objectmapper(); map smsdatamap = mapper.readvalue(smsdata, map.class); list<string> phonenumberlist = jsonutils.jsontolist(phonenumbers, string.class); smsdata smsdataparam = new smsdata(); smsdataparam.ttemplateid(smstemplatedto.gettemplateid()); smsdataparam.tparams(smsdatamap); smsndrvice smsndrvice = smsfactory.getsmsndrvice(smstemplatedto); smsrespon = smsndrvice.ndsms(smsdataparam, phonenumberlist); } catch (exception e) { smsrespon.tmessage("短信发送失败"); e.printstacktrace(); } return smsrespon; } @override public smsrespon ndsmsverificationcode(string smscode, string phonenumber) { string verificationcode = randomutil.randomnumbers(6); map<string, string=""> smsdatamap = new hashmap<>(); smsdatamap.put(smsconstant.sms_captcha_template_code, verificationcode); list<string> phonenumbers = arrays.aslist(phonenumbe糖尿病人食疗方r); smsrespon smsrespon = this.ndsmsnormal(smscode, jsonutils.maptojson(smsdatamap), jsonutils.listtojson(phonenumbers)); if (null != smsrespon && smsrespon.issuccess()) { // 将短信验证码存入redis并设置过期时间为5分钟 redistemplate.opsforvalue().t(smsconstant.sms_captcha_key + smscode + phonenumber, verificationcode, 30, timeunit.minutes); } return smsrespon; } @override public boolean checksmsverificationcode(string smscode, string phonenumber, string verificationcode) { string verificationcoderedis = (string) redistemplate.opsforvalue().get(smsconstant.sms_captcha_key + smscode + phonenumber); if (!strutil.isallempty(verificationcoderedis, verificationcode) && verificationcode.equalsignoreca(verificationcoderedis)) { return true; } return fal; }}
6、新建smsfeign类,供其他微服务调用发送短信
/** * @classname: smsfeign * @description: smsfeign前端控制器 * @author gitegg * @date 2019年5月18日 下午4:03:58 */@restcontroller@requestmapping(value = "/feign/sms")@requiredargsconstructor(onconstructor_ = @autowired)@api(value = "smsfeign|提供微服务调用接口")@refreshscopepublic class smsfeign { private final ismsrvice smsrvice; @getmapping(value = "/nd/normal") @apioperation(value = "发送普通短信", notes = "发送普通短信") result<object> ndsmsnormal(@requestparam("smscode") string smscode, @requestparam("smsdata") string smsdata, @requestparam("phonenumbers") string phonenumbers) { smsrespon smsrespon = smsrvice.ndsmsnormal(smscode, smsdata, phonenumbers); return result.data(smsrespon); } @getmapping(value = "/nd/verification/code") @apioperation(value = "发送短信验证码", notes = "发送短信验证码") result<object> ndsmsverificationcode(@requestparam("smscode") string smscode, @requestparam("phonenumber") string phonenumber) { smsrespon smsrespon = smsrvice.ndsmsverificationcode(smscode, phonenumber); return result.data(smsrespon); } @getmapping(value = "/check/verification/code") @apioperation(value = "校验短信验证码", notes = "校验短信验证码") result<boolean> checksmsverificationcode(@requestparam("smscode") string smscode, @requestparam("phonenumber") string phonenumber, @requestparam("verificationcode") string verificationcode) { boolean checkresult = smsrvice.checksmsverificationcode(smscode, phonenumber, verificationcode); return result.data(checkresult); }}
项目源码:
gitee: https://gitee.com/wmz1930/gitegg
github: https://github.com/wmz1930/gitegg
到此这篇关于springcloud 搭建企业级开发框架之实现多租户多平台短信通知服务的文章就介绍到这了,更多相关springcloud 短信通知服务内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!
本文发布于:2023-04-03 23:22:48,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/645887d1f878467b004c958bb0913de6.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:SpringCloud 搭建企业级开发框架之实现多租户多平台短信通知服务(微服务实战).doc
本文 PDF 下载地址:SpringCloud 搭建企业级开发框架之实现多租户多平台短信通知服务(微服务实战).pdf
留言与评论(共有 0 条评论) |