package com.tcm.training.admin.utils; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.tcm.training.admin.app.auth.config.AppAliyunSmsProperties; import org.springframework.beans.factory.annotation.Autowired; import com.aliyun.dysmsapi20170525.models.SendSmsRequest; import com.aliyun.dysmsapi20170525.models.SendSmsResponse; import com.aliyun.tea.TeaException; import com.aliyun.teaopenapi.models.Config; import com.aliyun.teautil.models.RuntimeOptions; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.util.Map; @Component @Slf4j public class MsgUtils { private final AppAliyunSmsProperties properties; private final ObjectMapper objectMapper; /** * 注入阿里云短信配置与 JSON 序列化工具。 */ @Autowired public MsgUtils(AppAliyunSmsProperties properties, ObjectMapper objectMapper) { this.properties = properties; this.objectMapper = objectMapper; } /** * 使用AK&SK初始化账号Client * @param accessKeyId * @param accessKeySecret * @return Client * @throws Exception */ public static com.aliyun.dysmsapi20170525.Client createClient(String accessKeyId, String accessKeySecret) throws Exception { Config config = new Config() // 您的 AccessKey ID .setAccessKeyId(accessKeyId) // 您的 AccessKey Secret .setAccessKeySecret(accessKeySecret); // 访问的域名 config.endpoint = "dysmsapi.aliyuncs.com"; return new com.aliyun.dysmsapi20170525.Client(config); } /** * 创建阿里云短信客户端,便于测试覆盖异常分支。 */ protected com.aliyun.dysmsapi20170525.Client createSmsClient() throws Exception { return MsgUtils.createClient( properties.getAccessKeyId(), properties.getAccessKeySecret() ); } /** * 发送阿里云短信验证码。 */ public void sendMsg(String phone, Map params, String templateCode, String signName) throws Exception { com.aliyun.dysmsapi20170525.Client client = createSmsClient(); SendSmsRequest sendSmsRequest = new SendSmsRequest() .setSignName(signName) .setTemplateCode(templateCode) .setPhoneNumbers(phone); if (params != null && !params.isEmpty()) { sendSmsRequest.setTemplateParam(toTemplateParam(params)); } RuntimeOptions runtime = new RuntimeOptions(); try { SendSmsResponse sendSmsResponse = client.sendSmsWithOptions(sendSmsRequest, runtime); String responseCode = sendSmsResponse.getBody() == null ? null : sendSmsResponse.getBody().getCode(); if (!"OK".equalsIgnoreCase(responseCode)) { String message = sendSmsResponse.getBody() == null ? "短信发送失败" : sendSmsResponse.getBody().getMessage(); throw new IllegalStateException(message == null || message.trim().isEmpty() ? "短信发送失败,请稍后重试" : message); } log.info("阿里云短信发送成功,templateCode={}, statusCode={}", templateCode, sendSmsResponse.getStatusCode()); } catch (TeaException error) { log.error("阿里云短信发送失败,templateCode={}, code={}, message={}", templateCode, error.getCode(), resolveExceptionMessage(error, "阿里云短信发送失败"), error); throw new IllegalStateException("短信发送失败,请稍后重试"); } catch (Exception error) { log.error("阿里云短信发送异常,templateCode={}, message={}", templateCode, resolveExceptionMessage(error, "阿里云短信发送异常"), error); throw new IllegalStateException("短信发送失败,请稍后重试"); } } /** * 安全读取异常文案,避免阿里云 SDK 异常 message 为空时二次抛错。 */ private String resolveExceptionMessage(Throwable error, String fallback) { if (error == null) { return fallback; } String message = error.getMessage(); if (message == null || message.trim().isEmpty()) { return fallback; } return message; } /** * 将模板变量转换为阿里云要求的 JSON 字符串。 */ private String toTemplateParam(Map params) throws JsonProcessingException { return objectMapper.writeValueAsString(params); } }