package com.iailab.module.shasteel.framework.sms.core.client.impl; import cn.hutool.core.lang.Assert; import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.IAcsClient; import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateRequest; import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateResponse; import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse; import com.aliyuncs.profile.DefaultProfile; import com.aliyuncs.profile.IClientProfile; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.annotations.VisibleForTesting; import com.iailab.framework.common.core.KeyValue; import com.iailab.framework.common.util.collection.MapUtils; import com.iailab.framework.common.util.json.JsonUtils; import com.iailab.module.shasteel.framework.sms.core.client.dto.SmsReceiveRespDTO; import com.iailab.module.shasteel.framework.sms.core.client.dto.SmsSendRespDTO; import com.iailab.module.shasteel.framework.sms.core.client.dto.SmsTemplateRespDTO; import com.iailab.module.shasteel.framework.sms.core.enums.SmsTemplateAuditStatusEnum; import com.iailab.module.shasteel.framework.sms.core.property.SmsChannelProperties; import lombok.Data; import lombok.extern.slf4j.Slf4j; import java.time.LocalDateTime; import java.util.List; import java.util.Objects; import static com.iailab.framework.common.util.collection.CollectionUtils.convertList; import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; import static com.iailab.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT; /** * 阿里短信客户端的实现类 * * @author zzf * @since 2021/1/25 14:17 */ @Slf4j public class AliyunSmsClient extends AbstractSmsClient { /** * 调用成功 code */ public static final String API_CODE_SUCCESS = "OK"; /** * REGION, 使用杭州 */ private static final String ENDPOINT = "cn-hangzhou"; /** * 阿里云客户端 */ private volatile IAcsClient client; public AliyunSmsClient(SmsChannelProperties properties) { super(properties); Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); } @Override protected void doInit() { IClientProfile profile = DefaultProfile.getProfile(ENDPOINT, properties.getApiKey(), properties.getApiSecret()); client = new DefaultAcsClient(profile); } @Override public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId, List> templateParams) throws Throwable { // 构建请求 SendSmsRequest request = new SendSmsRequest(); request.setPhoneNumbers(mobile); request.setSignName(properties.getSignature()); request.setTemplateCode(apiTemplateId); request.setTemplateParam(JsonUtils.toJsonString(MapUtils.convertMap(templateParams))); request.setOutId(String.valueOf(sendLogId)); // 执行请求 SendSmsResponse response = client.getAcsResponse(request); return new SmsSendRespDTO().setSuccess(Objects.equals(response.getCode(), API_CODE_SUCCESS)).setSerialNo(response.getBizId()) .setApiRequestId(response.getRequestId()).setApiCode(response.getCode()).setApiMsg(response.getMessage()); } @Override public List parseSmsReceiveStatus(String text) { List statuses = JsonUtils.parseArray(text, SmsReceiveStatus.class); return convertList(statuses, status -> new SmsReceiveRespDTO().setSuccess(status.getSuccess()) .setErrorCode(status.getErrCode()).setErrorMsg(status.getErrMsg()) .setMobile(status.getPhoneNumber()).setReceiveTime(status.getReportTime()) .setSerialNo(status.getBizId()).setLogId(Long.valueOf(status.getOutId()))); } @Override public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable { // 构建请求 QuerySmsTemplateRequest request = new QuerySmsTemplateRequest(); request.setTemplateCode(apiTemplateId); // 执行请求 QuerySmsTemplateResponse response = client.getAcsResponse(request); if (response.getTemplateStatus() == null) { return null; } return new SmsTemplateRespDTO().setId(response.getTemplateCode()).setContent(response.getTemplateContent()) .setAuditStatus(convertSmsTemplateAuditStatus(response.getTemplateStatus())).setAuditReason(response.getReason()); } @VisibleForTesting Integer convertSmsTemplateAuditStatus(Integer templateStatus) { switch (templateStatus) { case 0: return SmsTemplateAuditStatusEnum.CHECKING.getStatus(); case 1: return SmsTemplateAuditStatusEnum.SUCCESS.getStatus(); case 2: return SmsTemplateAuditStatusEnum.FAIL.getStatus(); default: throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus)); } } /** * 短信接收状态 * * 参见 文档 * * @author iailab */ @Data public static class SmsReceiveStatus { /** * 手机号 */ @JsonProperty("phone_number") private String phoneNumber; /** * 发送时间 */ @JsonProperty("send_time") @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) private LocalDateTime sendTime; /** * 状态报告时间 */ @JsonProperty("report_time") @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) private LocalDateTime reportTime; /** * 是否接收成功 */ private Boolean success; /** * 状态报告说明 */ @JsonProperty("err_msg") private String errMsg; /** * 状态报告编码 */ @JsonProperty("err_code") private String errCode; /** * 发送序列号 */ @JsonProperty("biz_id") private String bizId; /** * 用户序列号 * * 这里我们传递的是 SysSmsLogDO 的日志编号 */ @JsonProperty("out_id") private String outId; /** * 短信长度,例如说 1、2、3 * * 140 字节算一条短信,短信长度超过 140 字节时会拆分成多条短信发送 */ @JsonProperty("sms_size") private Integer smsSize; } }