改pom
com.aliyun dysmsapi20170525
写yml
aliyun:
sms:
access-key-id:
access-key-secret:
endpoint:
sign-name-json:
template-code-map:
#这儿涉及到多个模板,正常只需要一个
mainLand:
international:
traditional:
配置类
@Data
@ConfigurationProperties(prefix = "aliyun.sms")
@Configuration
public class SmsProperties {
private String accessKeyId;
private String accessKeySecret;
private String endpoint;
private String signNameJson;
private String regionId;
private Map templateCodeMap;
}
Bean注入
@Configuration
@AllArgsConstructor
@Data
@Slf4j
public class SmsConfig {
private final SmsProperties smsProperties;
@Bean("smsClient")
public Client smsClient() {
Config config = new Config()
.setAccessKeyId(smsProperties.getAccessKeyId())
.setAccessKeySecret(smsProperties.getAccessKeySecret())
.setEndpoint(smsProperties.getEndpoint());
Client client = null;
try {
client = new Client(config);
} catch (Exception e) {
log.error("阿里云sms加载失败");
}
return client;
}
@Bean("smsRequest")
SendSmsRequest sendSmsRequest() {
return new SendSmsRequest()
.setSignName(smsProperties.getSignNameJson());
}
}
业务类
public ResultEnum sendSms(HttpServletRequest request,String mobile, String code) {
// 设置手机号
sendSmsRequest.setPhoneNumbers(mobile);
// 验证是否为大陆手机号
Integer languageIndex = LanguageUtil.getLanguageIndex();
// 根据当前语言设置短信模板Code(业务需要,涉及到了多语言)
if (Objects.equals(languageIndex, LanguageType.CN.getIndex())) {
sendSmsRequest.setTemplateCode(smsProperties.getTemplateCodeMap().get("mainLand"));
} else if (Objects.equals(languageIndex, LanguageType.EN.getIndex())) {
sendSmsRequest.setTemplateCode(smsProperties.getTemplateCodeMap().get("international"));
} else {
sendSmsRequest.setTemplateCode(smsProperties.getTemplateCodeMap().get("traditional"));
}
// 设置验证码
sendSmsRequest.setTemplateParam("{"code": "" + code + ""}");
SendSmsResponse sendSmsResponse;
try {
sendSmsResponse = client.sendSms(sendSmsRequest);
} catch (Exception e) {
throw new BadRequestException(ResultEnum.SMS_NOT_SEND);
}
CodeLog codeLog = new CodeLog();
codeLog.setSendTo(mobile);
codeLog.setMessageContent(code);
codeLog.setIp(StringUtils.getIp(request));
codeLogService.save(codeLog);
String bizId = sendSmsResponse.getBody().bizId;
if (StringUtils.isBlank(bizId)) {
// 阿里云中可以配置发送频率,取不到bizId就默认发送频繁
// 配置的全局拦截,错误直接抛出枚举
throw new BadRequestException(ResultEnum.REQUEST_FREQUENT);
}
// 查询短信发送状态
com.aliyun.teautil.Common.sleep(10000);
QuerySendDetailsRequest queryReq = new QuerySendDetailsRequest()
.setPhoneNumber(mobile)
.setBizId(bizId)
.setSendDate(DateUtil.today().replaceAll("-",""))
.setPageSize(10L)
.setCurrentPage(1L);
try {
QuerySendDetailsResponse queryResp = client.querySendDetails(queryReq);
List smsSendDetailDTO = queryResp.getBody().getSmsSendDetailDTOs().getSmsSendDetailDTO();
for (QuerySendDetailsResponseBody.QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO sendDetailDTO : smsSendDetailDTO) {
if (sendDetailDTO.getSendStatus().equals(2L)) {
throw new BadRequestException(ResultEnum.SMS_NOT_SEND);
}
}
} catch (Exception e) {
log.error("手机号发送状态查询失败");
}
return "OK".equals(sendSmsResponse.getBody().code) ? ResultEnum.SUCCESS : ResultEnum.SMS_NOT_SEND;
}
结语
注:这儿的查询策略是:发送完成之后延迟十秒钟查询该短信的发送状态。若是前端一直需要等待才能响应的话,效果也不太好。
可以发送短信返回bizId,然后再提供一个根据bizId查询发送状态的接口供前端调用。
参考:
SendSms - 发送短信
https://next.api.aliyun.com/document/Dysmsapi/2017-05-25/SendSms
QuerySendDetails - 查看短信发送记录和发送状态
https://next.api.aliyun.com/document/Dysmsapi/2017-05-25/QuerySendDetails



