package com.mydreamjob.auth.sms; import com.aliyun.dysmsapi20170525.Client; import com.aliyun.dysmsapi20170525.models.SendSmsRequest; import com.aliyun.dysmsapi20170525.models.SendSmsResponse; import com.aliyun.teautil.models.RuntimeOptions; import com.mydreamjob.common.exception.BusinessException; import com.mydreamjob.common.exception.ErrorCode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Set; /** SDK 返回 OK 只代表网关受理,不代表手机已收到;禁止自动重试非幂等发送。 */ public final class AliyunSmsCodeSender implements SmsCodeSender { private static final Logger LOG = LoggerFactory.getLogger(AliyunSmsCodeSender.class); private static final Set PURPOSES = Set.of("login", "password-set", "password-reset", "company-register"); private final Client client; private final String signName; private final String templateCode; private final int connectTimeoutMs; private final int readTimeoutMs; public AliyunSmsCodeSender(Client client, AliyunSmsProperties properties) { properties.validateForSending(); this.client = client; this.signName = properties.getSignName(); this.templateCode = properties.getTemplateCode(); this.connectTimeoutMs = properties.getConnectTimeoutMs(); this.readTimeoutMs = properties.getReadTimeoutMs(); } @Override public void sendVerificationCode(String phone, String purpose, String code) { if (phone == null || !phone.matches("[0-9]{11}") || purpose == null || !PURPOSES.contains(purpose) || code == null || !code.matches("[0-9]{6}")) { throw new BusinessException(ErrorCode.BAD_REQUEST, "invalid SMS request"); } SendSmsRequest request = new SendSmsRequest().setPhoneNumbers(phone).setSignName(signName) .setTemplateCode(templateCode).setTemplateParam("{\"code\":\"" + code + "\"}"); RuntimeOptions runtime = new RuntimeOptions().setAutoretry(false) .setConnectTimeout(connectTimeoutMs).setReadTimeout(readTimeoutMs); SendSmsResponse response; try { response = client.sendSmsWithOptions(request, runtime); } catch (Exception ignored) { if (ignored instanceof InterruptedException) Thread.currentThread().interrupt(); // SDK 异常可能包含请求绑定值,既不透传原因也不输出堆栈。 LOG.warn("Aliyun SMS gateway call failed"); throw new BusinessException(ErrorCode.SMS_SEND_FAILED); } if (response == null || response.getBody() == null || response.getStatusCode() == null || response.getStatusCode() != 200 || !"OK".equals(response.getBody().getCode())) { LOG.warn("Aliyun SMS gateway did not accept request"); throw new BusinessException(ErrorCode.SMS_SEND_FAILED); } } }