mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-11-04 20:28:44 +08:00 
			
		
		
		
	1,腾讯云短信实现优化,去除不必要VO;
2,增加腾讯云短信功能单测,和集成单测;
This commit is contained in:
		@@ -2,36 +2,29 @@ package cn.iocoder.yudao.module.system.framework.sms.core.client.impl;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.lang.Assert;
 | 
			
		||||
import cn.hutool.core.util.StrUtil;
 | 
			
		||||
import cn.hutool.http.HttpRequest;
 | 
			
		||||
import cn.hutool.http.HttpResponse;
 | 
			
		||||
import cn.hutool.json.JSONArray;
 | 
			
		||||
import cn.hutool.json.JSONObject;
 | 
			
		||||
import cn.hutool.json.JSONUtil;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
 | 
			
		||||
import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
 | 
			
		||||
import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties;
 | 
			
		||||
import com.fasterxml.jackson.annotation.JsonFormat;
 | 
			
		||||
import com.fasterxml.jackson.annotation.JsonProperty;
 | 
			
		||||
import com.google.common.annotations.VisibleForTesting;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import jakarta.xml.bind.DatatypeConverter;
 | 
			
		||||
 | 
			
		||||
import javax.crypto.Mac;
 | 
			
		||||
import javax.crypto.spec.SecretKeySpec;
 | 
			
		||||
import javax.xml.bind.DatatypeConverter;
 | 
			
		||||
import java.nio.charset.StandardCharsets;
 | 
			
		||||
import java.text.SimpleDateFormat;
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
import static cn.hutool.crypto.digest.DigestUtil.sha256Hex;
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 腾讯云短信功能实现
 | 
			
		||||
@@ -103,14 +96,15 @@ public class TencentSmsClient extends AbstractSmsClient {
 | 
			
		||||
        body.put("TemplateId",apiTemplateId);
 | 
			
		||||
        body.put("TemplateParamSet",ArrayUtils.toArray(templateParams, e -> String.valueOf(e.getValue())));
 | 
			
		||||
 | 
			
		||||
        JSONObject JsonResponse = sendSmsRequest(body,"SendSms","2021-01-11","ap-guangzhou");
 | 
			
		||||
        SmsResponse smsResponse = getSmsSendResponse(JsonResponse);
 | 
			
		||||
 | 
			
		||||
        return new SmsSendRespDTO().setSuccess(smsResponse.success).setApiMsg(smsResponse.data.toString());
 | 
			
		||||
        JSONObject JsonResponse = request(body,"SendSms","2021-01-11","ap-guangzhou");
 | 
			
		||||
 | 
			
		||||
        return new SmsSendRespDTO().setSuccess(API_CODE_SUCCESS.equals(JsonResponse.getJSONObject("Response").getJSONArray("SendStatusSet").getJSONObject(0).getStr("Code")))
 | 
			
		||||
                .setApiRequestId(JsonResponse.getJSONObject("Response").getStr("RequestId"))
 | 
			
		||||
                .setSerialNo(JsonResponse.getJSONObject("Response").getJSONArray("SendStatusSet").getJSONObject(0).getStr("SerialNo"))
 | 
			
		||||
                .setApiMsg(JsonResponse.getJSONObject("Response").getJSONArray("SendStatusSet").getJSONObject(0).getStr("Message"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JSONObject sendSmsRequest(TreeMap<String, Object> body,String action,String version,String region) throws Exception {
 | 
			
		||||
    JSONObject request(TreeMap<String, Object> body,String action,String version,String region) throws Exception {
 | 
			
		||||
 | 
			
		||||
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
 | 
			
		||||
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
 | 
			
		||||
@@ -155,12 +149,9 @@ public class TencentSmsClient extends AbstractSmsClient {
 | 
			
		||||
        headers.put("X-TC-Version", version);
 | 
			
		||||
        headers.put("X-TC-Region", region);
 | 
			
		||||
 | 
			
		||||
        HttpResponse response = HttpRequest.post("https://"+host)
 | 
			
		||||
                .addHeaders(headers)
 | 
			
		||||
                .body(JSONUtil.toJsonStr(body))
 | 
			
		||||
                .execute();
 | 
			
		||||
        String responseBody = HttpUtils.post("https://"+host, headers, JSONUtil.toJsonStr(body));
 | 
			
		||||
 | 
			
		||||
        return JSONUtil.parseObj(response.body());
 | 
			
		||||
        return JSONUtil.parseObj(responseBody);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static byte[] hmac256(byte[] key, String msg) throws Exception {
 | 
			
		||||
@@ -170,22 +161,20 @@ public class TencentSmsClient extends AbstractSmsClient {
 | 
			
		||||
        return mac.doFinal(msg.getBytes(StandardCharsets.UTF_8));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private SmsResponse getSmsSendResponse(JSONObject resJson) {
 | 
			
		||||
        SmsResponse smsResponse = new SmsResponse();
 | 
			
		||||
        JSONArray statusJson =resJson.getJSONObject("Response").getJSONArray("SendStatusSet");
 | 
			
		||||
        smsResponse.setSuccess("Ok".equals(statusJson.getJSONObject(0).getStr("Code")));
 | 
			
		||||
        smsResponse.setData(resJson);
 | 
			
		||||
        return smsResponse;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) {
 | 
			
		||||
        List<SmsReceiveStatus> callback = JsonUtils.parseArray(text, SmsReceiveStatus.class);
 | 
			
		||||
        return convertList(callback, status -> new SmsReceiveRespDTO()
 | 
			
		||||
                .setSuccess(SmsReceiveStatus.SUCCESS_CODE.equalsIgnoreCase(status.getStatus()))
 | 
			
		||||
                .setErrorCode(status.getErrCode()).setErrorMsg(status.getDescription())
 | 
			
		||||
                .setMobile(status.getMobile()).setReceiveTime(status.getReceiveTime())
 | 
			
		||||
                .setSerialNo(status.getSerialNo()).setLogId(status.getSessionContext().getLogId()));
 | 
			
		||||
 | 
			
		||||
        JSONArray statuses = JSONUtil.parseArray(text);
 | 
			
		||||
        // 字段参考
 | 
			
		||||
        return convertList(statuses, status -> {
 | 
			
		||||
            JSONObject statusObj = (JSONObject) status;
 | 
			
		||||
            return new SmsReceiveRespDTO()
 | 
			
		||||
                    .setSuccess("SUCCESS".equals(statusObj.getStr("report_status"))) // 是否接收成功
 | 
			
		||||
                    .setErrorCode(statusObj.getStr("errmsg")) // 状态报告编码
 | 
			
		||||
                    .setMobile(statusObj.getStr("mobile")) // 手机号
 | 
			
		||||
                    .setReceiveTime(statusObj.getLocalDateTime("user_receive_time", null)) // 状态报告时间
 | 
			
		||||
                    .setSerialNo(statusObj.getStr("sid")); // 发送序列号
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -193,48 +182,22 @@ public class TencentSmsClient extends AbstractSmsClient {
 | 
			
		||||
 | 
			
		||||
        // 构建请求
 | 
			
		||||
        TreeMap<String, Object> body = new TreeMap<>();
 | 
			
		||||
        body.put("International",0);
 | 
			
		||||
        body.put("International",INTERNATIONAL_CHINA);
 | 
			
		||||
        Integer[] templateIds = {Integer.valueOf(apiTemplateId)};
 | 
			
		||||
        body.put("TemplateIdSet",templateIds);
 | 
			
		||||
 | 
			
		||||
        JSONObject JsonResponse = sendSmsRequest(body,"DescribeSmsTemplateList","2021-01-11","ap-guangzhou");
 | 
			
		||||
        QuerySmsTemplateResponse smsTemplateResponse = getSmsTemplateResponse(JsonResponse);
 | 
			
		||||
        String templateId = Integer.toString(smsTemplateResponse.getDescribeTemplateStatusSet().get(0).getTemplateId());
 | 
			
		||||
        String content = smsTemplateResponse.getDescribeTemplateStatusSet().get(0).getTemplateContent();
 | 
			
		||||
        Integer templateStatus = smsTemplateResponse.getDescribeTemplateStatusSet().get(0).getStatusCode();
 | 
			
		||||
        String auditReason = smsTemplateResponse.getDescribeTemplateStatusSet().get(0).getReviewReply();
 | 
			
		||||
        JSONObject JsonResponse = request(body,"DescribeSmsTemplateList","2021-01-11","ap-guangzhou");
 | 
			
		||||
        System.out.println("JsonResponse======"+JsonResponse);
 | 
			
		||||
 | 
			
		||||
        return new SmsTemplateRespDTO().setId(templateId).setContent(content)
 | 
			
		||||
        JSONObject TemplateStatusSet = JsonResponse.getJSONObject("Response").getJSONArray("DescribeTemplateStatusSet").getJSONObject(0);
 | 
			
		||||
        String content = TemplateStatusSet.get("TemplateContent").toString();
 | 
			
		||||
        int templateStatus = Integer.parseInt(TemplateStatusSet.get("StatusCode").toString());
 | 
			
		||||
        String auditReason = TemplateStatusSet.get("ReviewReply").toString();
 | 
			
		||||
 | 
			
		||||
        return new SmsTemplateRespDTO().setId(apiTemplateId).setContent(content)
 | 
			
		||||
                .setAuditStatus(convertSmsTemplateAuditStatus(templateStatus)).setAuditReason(auditReason);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private QuerySmsTemplateResponse getSmsTemplateResponse(JSONObject resJson) {
 | 
			
		||||
 | 
			
		||||
        QuerySmsTemplateResponse smsTemplateResponse = new QuerySmsTemplateResponse();
 | 
			
		||||
 | 
			
		||||
        smsTemplateResponse.setRequestId(resJson.getJSONObject("Response").getStr("RequestId"));
 | 
			
		||||
 | 
			
		||||
        smsTemplateResponse.setDescribeTemplateStatusSet(new ArrayList<>());
 | 
			
		||||
 | 
			
		||||
        QuerySmsTemplateResponse.TemplateInfo templateInfo = new QuerySmsTemplateResponse.TemplateInfo();
 | 
			
		||||
 | 
			
		||||
        Object statusObject = resJson.getJSONObject("Response").getJSONArray("DescribeTemplateStatusSet").getFirst();
 | 
			
		||||
 | 
			
		||||
        JSONObject statusJSON = new JSONObject(statusObject);
 | 
			
		||||
 | 
			
		||||
        templateInfo.setTemplateContent(statusJSON.get("TemplateContent").toString());
 | 
			
		||||
 | 
			
		||||
        templateInfo.setStatusCode(Integer.parseInt(statusJSON.get("StatusCode").toString()));
 | 
			
		||||
 | 
			
		||||
        templateInfo.setReviewReply(statusJSON.get("ReviewReply").toString());
 | 
			
		||||
 | 
			
		||||
        templateInfo.setTemplateId(Integer.parseInt(statusJSON.get("TemplateId").toString()));
 | 
			
		||||
 | 
			
		||||
        smsTemplateResponse.getDescribeTemplateStatusSet().add(templateInfo);
 | 
			
		||||
 | 
			
		||||
        return smsTemplateResponse;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @VisibleForTesting
 | 
			
		||||
    Integer convertSmsTemplateAuditStatus(int templateStatus) {
 | 
			
		||||
        switch (templateStatus) {
 | 
			
		||||
@@ -244,113 +207,4 @@ public class TencentSmsClient extends AbstractSmsClient {
 | 
			
		||||
            default: throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Data
 | 
			
		||||
    public static class SmsResponse {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 是否成功
 | 
			
		||||
         */
 | 
			
		||||
        private boolean success;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 厂商原返回体
 | 
			
		||||
         */
 | 
			
		||||
        private Object data;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * <p>类名: QuerySmsTemplateResponse
 | 
			
		||||
     * <p>说明:  sms模板查询返回信息
 | 
			
		||||
     *
 | 
			
		||||
     * @author :scholar
 | 
			
		||||
     * 2024/07/17  0:25
 | 
			
		||||
     **/
 | 
			
		||||
    @Data
 | 
			
		||||
    public static class QuerySmsTemplateResponse {
 | 
			
		||||
        private List<TemplateInfo> DescribeTemplateStatusSet;
 | 
			
		||||
        private String RequestId;
 | 
			
		||||
        @Data
 | 
			
		||||
        static class TemplateInfo {
 | 
			
		||||
            private String TemplateName;
 | 
			
		||||
            private Integer TemplateId;
 | 
			
		||||
            private Integer International;
 | 
			
		||||
            private String ReviewReply;
 | 
			
		||||
            private long CreateTime;
 | 
			
		||||
            private String TemplateContent;
 | 
			
		||||
            private Integer StatusCode;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Data
 | 
			
		||||
    private static class SmsReceiveStatus {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 短信接受成功 code
 | 
			
		||||
         */
 | 
			
		||||
        public static final String SUCCESS_CODE = "SUCCESS";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 用户实际接收到短信的时间
 | 
			
		||||
         */
 | 
			
		||||
        @JsonProperty("user_receive_time")
 | 
			
		||||
        @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
 | 
			
		||||
        private LocalDateTime receiveTime;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 国家(或地区)码
 | 
			
		||||
         */
 | 
			
		||||
        @JsonProperty("nationcode")
 | 
			
		||||
        private String nationCode;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 手机号码
 | 
			
		||||
         */
 | 
			
		||||
        private String mobile;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 实际是否收到短信接收状态,SUCCESS(成功)、FAIL(失败)
 | 
			
		||||
         */
 | 
			
		||||
        @JsonProperty("report_status")
 | 
			
		||||
        private String status;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 用户接收短信状态码错误信息
 | 
			
		||||
         */
 | 
			
		||||
        @JsonProperty("errmsg")
 | 
			
		||||
        private String errCode;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 用户接收短信状态描述
 | 
			
		||||
         */
 | 
			
		||||
        @JsonProperty("description")
 | 
			
		||||
        private String description;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 本次发送标识 ID(与发送接口返回的SerialNo对应)
 | 
			
		||||
         */
 | 
			
		||||
        @JsonProperty("sid")
 | 
			
		||||
        private String serialNo;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 用户的 session 内容(与发送接口的请求参数 SessionContext 一致)
 | 
			
		||||
         */
 | 
			
		||||
        @JsonProperty("ext")
 | 
			
		||||
        private SessionContext sessionContext;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @VisibleForTesting
 | 
			
		||||
    @Data
 | 
			
		||||
    static class SessionContext {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 发送短信记录id
 | 
			
		||||
         */
 | 
			
		||||
        private Long logId;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,139 @@
 | 
			
		||||
package cn.iocoder.yudao.module.system.framework.sms.core.client.impl;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
 | 
			
		||||
import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties;
 | 
			
		||||
import org.junit.jupiter.api.Disabled;
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 各种 {@link SmsClientTests  集成测试
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
public class SmsClientTests {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @Disabled
 | 
			
		||||
    public void testHuaweiSmsClient_sendSms() throws Throwable {
 | 
			
		||||
        SmsChannelProperties properties = new SmsChannelProperties()
 | 
			
		||||
                .setApiKey("123")
 | 
			
		||||
                .setApiSecret("456")
 | 
			
		||||
                .setSignature("runpu");
 | 
			
		||||
        HuaweiSmsClient client = new HuaweiSmsClient(properties);
 | 
			
		||||
        // 准备参数
 | 
			
		||||
        Long sendLogId = System.currentTimeMillis();
 | 
			
		||||
        String mobile = "15601691323";
 | 
			
		||||
        String apiTemplateId = "xx test01";
 | 
			
		||||
        List<KeyValue<String, Object>> templateParams = List.of(new KeyValue<>("code", "1024"));
 | 
			
		||||
        // 调用
 | 
			
		||||
        SmsSendRespDTO smsSendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, templateParams);
 | 
			
		||||
        // 打印结果
 | 
			
		||||
        System.out.println(smsSendRespDTO);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ========== 阿里云 ==========
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @Disabled
 | 
			
		||||
    public void testAliyunSmsClient_getSmsTemplate() throws Throwable {
 | 
			
		||||
        SmsChannelProperties properties = new SmsChannelProperties()
 | 
			
		||||
                .setApiKey("LTAI5tAicJAxaSFiZuGGeXHR")
 | 
			
		||||
                .setApiSecret("Fdr9vadxnDvS6GJU0W1tijQ0VmLhYz");
 | 
			
		||||
        AliyunSmsClient client = new AliyunSmsClient(properties);
 | 
			
		||||
        // 准备参数
 | 
			
		||||
        String apiTemplateId = "SMS_207945135";
 | 
			
		||||
        // 调用
 | 
			
		||||
        SmsTemplateRespDTO template = client.getSmsTemplate(apiTemplateId);
 | 
			
		||||
        // 打印结果
 | 
			
		||||
        System.out.println(template);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @Disabled
 | 
			
		||||
    public void testAliyunSmsClient_sendSms() throws Throwable {
 | 
			
		||||
        SmsChannelProperties properties = new SmsChannelProperties()
 | 
			
		||||
                .setApiKey("LTAI5tAicJAxaSFiZuGGeXHR")
 | 
			
		||||
                .setApiSecret("Fdr9vadxnDvS6GJU0W1tijQ0VmLhYz")
 | 
			
		||||
                .setSignature("runpu");
 | 
			
		||||
        AliyunSmsClient client = new AliyunSmsClient(properties);
 | 
			
		||||
        // 准备参数
 | 
			
		||||
        Long sendLogId = System.currentTimeMillis();
 | 
			
		||||
        String mobile = "15601691323";
 | 
			
		||||
        String apiTemplateId = "SMS_207945135";
 | 
			
		||||
        // 调用
 | 
			
		||||
        SmsSendRespDTO sendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, List.of(new KeyValue<>("code", "1024")));
 | 
			
		||||
        // 打印结果
 | 
			
		||||
        System.out.println(sendRespDTO);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @Disabled
 | 
			
		||||
    public void testAliyunSmsClient_parseSmsReceiveStatus() {
 | 
			
		||||
        SmsChannelProperties properties = new SmsChannelProperties()
 | 
			
		||||
                .setApiKey("LTAI5tAicJAxaSFiZuGGeXHR")
 | 
			
		||||
                .setApiSecret("Fdr9vadxnDvS6GJU0W1tijQ0VmLhYz");
 | 
			
		||||
        AliyunSmsClient client = new AliyunSmsClient(properties);
 | 
			
		||||
        // 准备参数
 | 
			
		||||
        String text = "[\n" +
 | 
			
		||||
                "  {\n" +
 | 
			
		||||
                "    \"phone_number\" : \"13900000001\",\n" +
 | 
			
		||||
                "    \"send_time\" : \"2017-01-01 11:12:13\",\n" +
 | 
			
		||||
                "    \"report_time\" : \"2017-02-02 22:23:24\",\n" +
 | 
			
		||||
                "    \"success\" : true,\n" +
 | 
			
		||||
                "    \"err_code\" : \"DELIVERED\",\n" +
 | 
			
		||||
                "    \"err_msg\" : \"用户接收成功\",\n" +
 | 
			
		||||
                "    \"sms_size\" : \"1\",\n" +
 | 
			
		||||
                "    \"biz_id\" : \"12345\",\n" +
 | 
			
		||||
                "    \"out_id\" : \"67890\"\n" +
 | 
			
		||||
                "  }\n" +
 | 
			
		||||
                "]";
 | 
			
		||||
        // mock 方法
 | 
			
		||||
 | 
			
		||||
        // 调用
 | 
			
		||||
        List<SmsReceiveRespDTO> statuses = client.parseSmsReceiveStatus(text);
 | 
			
		||||
        // 打印结果
 | 
			
		||||
        System.out.println(statuses);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ========== 腾讯云 ==========
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @Disabled
 | 
			
		||||
    public void testTencentSmsClient_sendSms() throws Throwable {
 | 
			
		||||
        SmsChannelProperties properties = new SmsChannelProperties()
 | 
			
		||||
                .setApiKey("LTAI5tAicJAxaSFiZuGGeXHR 1428926523")
 | 
			
		||||
                .setApiSecret("Fdr9vadxnDvS6GJU0W1tijQ0VmLhYz")
 | 
			
		||||
                .setSignature("芋道源码");
 | 
			
		||||
        TencentSmsClient client = new TencentSmsClient(properties);
 | 
			
		||||
        // 准备参数
 | 
			
		||||
        Long sendLogId = System.currentTimeMillis();
 | 
			
		||||
        String mobile = "15601691323";
 | 
			
		||||
        String apiTemplateId = "2136358";
 | 
			
		||||
        // 调用
 | 
			
		||||
        SmsSendRespDTO sendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, List.of(new KeyValue<>("code", "1024")));
 | 
			
		||||
        // 打印结果
 | 
			
		||||
        System.out.println(sendRespDTO);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @Disabled
 | 
			
		||||
    public void testTencentSmsClient_getSmsTemplate() throws Throwable {
 | 
			
		||||
        SmsChannelProperties properties = new SmsChannelProperties()
 | 
			
		||||
                .setApiKey("LTAI5tAicJAxaSFiZuGGeXHR 1428926523")
 | 
			
		||||
                .setApiSecret("Fdr9vadxnDvS6GJU0W1tijQ0VmLhYz")
 | 
			
		||||
                .setSignature("芋道源码");
 | 
			
		||||
        TencentSmsClient client = new TencentSmsClient(properties);
 | 
			
		||||
        // 准备参数
 | 
			
		||||
        String apiTemplateId = "2136358";
 | 
			
		||||
        // 调用
 | 
			
		||||
        SmsTemplateRespDTO template = client.getSmsTemplate(apiTemplateId);
 | 
			
		||||
        // 打印结果
 | 
			
		||||
        System.out.println(template);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1,10 +1,7 @@
 | 
			
		||||
package cn.iocoder.yudao.module.system.framework.sms.core.client.impl;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.util.ReflectUtil;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
 | 
			
		||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
 | 
			
		||||
import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO;
 | 
			
		||||
@@ -12,24 +9,19 @@ import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateR
 | 
			
		||||
import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
 | 
			
		||||
import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties;
 | 
			
		||||
import com.google.common.collect.Lists;
 | 
			
		||||
import com.tencentcloudapi.sms.v20210111.SmsClient;
 | 
			
		||||
import com.tencentcloudapi.sms.v20210111.models.DescribeSmsTemplateListResponse;
 | 
			
		||||
import com.tencentcloudapi.sms.v20210111.models.DescribeTemplateListStatus;
 | 
			
		||||
import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
 | 
			
		||||
import com.tencentcloudapi.sms.v20210111.models.SendStatus;
 | 
			
		||||
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
import org.mockito.InjectMocks;
 | 
			
		||||
import org.mockito.Mock;
 | 
			
		||||
import org.mockito.MockedStatic;
 | 
			
		||||
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
 | 
			
		||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
 | 
			
		||||
import static org.junit.jupiter.api.Assertions.*;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.argThat;
 | 
			
		||||
import static org.mockito.Mockito.when;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.*;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.anyString;
 | 
			
		||||
import static org.mockito.Mockito.mockStatic;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * {@link TencentSmsClient} 的单元测试
 | 
			
		||||
@@ -46,9 +38,6 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
 | 
			
		||||
    @InjectMocks
 | 
			
		||||
    private TencentSmsClient smsClient = new TencentSmsClient(properties);
 | 
			
		||||
 | 
			
		||||
    @Mock
 | 
			
		||||
    private SmsClient client;
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testDoInit() {
 | 
			
		||||
        // 准备参数
 | 
			
		||||
@@ -56,103 +45,92 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
 | 
			
		||||
 | 
			
		||||
        // 调用
 | 
			
		||||
        smsClient.doInit();
 | 
			
		||||
        // 断言
 | 
			
		||||
        assertNotSame(client, ReflectUtil.getFieldValue(smsClient, "client"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testRefresh() {
 | 
			
		||||
        // 准备参数
 | 
			
		||||
        SmsChannelProperties p = new SmsChannelProperties()
 | 
			
		||||
                .setApiKey(randomString() + " " + randomString()) // 随机一个 apiKey,避免构建报错
 | 
			
		||||
                .setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错
 | 
			
		||||
                .setSignature("芋道源码");
 | 
			
		||||
        // 调用
 | 
			
		||||
        smsClient.refresh(p);
 | 
			
		||||
        // 断言
 | 
			
		||||
        assertNotSame(client, ReflectUtil.getFieldValue(smsClient, "client"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testDoSendSms_success() throws Throwable {
 | 
			
		||||
 | 
			
		||||
        try (MockedStatic<HttpUtils> httpUtilsMockedStatic = mockStatic(HttpUtils.class)) {
 | 
			
		||||
            // 准备参数
 | 
			
		||||
            Long sendLogId = randomLongId();
 | 
			
		||||
            String mobile = randomString();
 | 
			
		||||
            String apiTemplateId = randomString();
 | 
			
		||||
            List<KeyValue<String, Object>> templateParams = Lists.newArrayList(
 | 
			
		||||
                    new KeyValue<>("1", 1234), new KeyValue<>("2", "login"));
 | 
			
		||||
        String requestId = randomString();
 | 
			
		||||
        String serialNo = randomString();
 | 
			
		||||
 | 
			
		||||
            // mock 方法
 | 
			
		||||
        SendSmsResponse response = randomPojo(SendSmsResponse.class, o -> {
 | 
			
		||||
            o.setRequestId(requestId);
 | 
			
		||||
            SendStatus[] sendStatuses = new SendStatus[1];
 | 
			
		||||
            o.setSendStatusSet(sendStatuses);
 | 
			
		||||
            SendStatus sendStatus = new SendStatus();
 | 
			
		||||
            sendStatuses[0] = sendStatus;
 | 
			
		||||
            sendStatus.setCode(TencentSmsClient.API_CODE_SUCCESS);
 | 
			
		||||
            sendStatus.setMessage("send success");
 | 
			
		||||
            sendStatus.setSerialNo(serialNo);
 | 
			
		||||
        });
 | 
			
		||||
        when(client.SendSms(argThat(request -> {
 | 
			
		||||
            assertEquals(mobile, request.getPhoneNumberSet()[0]);
 | 
			
		||||
            assertEquals(properties.getSignature(), request.getSignName());
 | 
			
		||||
            assertEquals(apiTemplateId, request.getTemplateId());
 | 
			
		||||
            assertEquals(toJsonString(ArrayUtils.toArray(new ArrayList<>(MapUtils.convertMap(templateParams).values()), String::valueOf)),
 | 
			
		||||
                    toJsonString(request.getTemplateParamSet()));
 | 
			
		||||
            assertEquals(sendLogId, ReflectUtil.getFieldValue(JsonUtils.parseObject(request.getSessionContext(), TencentSmsClient.SessionContext.class), "logId"));
 | 
			
		||||
            return true;
 | 
			
		||||
        }))).thenReturn(response);
 | 
			
		||||
            httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString()))
 | 
			
		||||
                    .thenReturn(
 | 
			
		||||
                            "{\n" +
 | 
			
		||||
                                    "    \"Response\": {\n" +
 | 
			
		||||
                                    "        \"SendStatusSet\": [\n" +
 | 
			
		||||
                                    "            {\n" +
 | 
			
		||||
                                    "                \"SerialNo\": \"5000:1045710669157053657849499619\",\n" +
 | 
			
		||||
                                    "                \"PhoneNumber\": \"+8618511122233\",\n" +
 | 
			
		||||
                                    "                \"Fee\": 1,\n" +
 | 
			
		||||
                                    "                \"SessionContext\": \"test\",\n" +
 | 
			
		||||
                                    "                \"Code\": \"Ok\",\n" +
 | 
			
		||||
                                    "                \"Message\": \"send success\",\n" +
 | 
			
		||||
                                    "                \"IsoCode\": \"CN\"\n" +
 | 
			
		||||
                                    "            },\n" +
 | 
			
		||||
                                    "        ],\n" +
 | 
			
		||||
                                    "        \"RequestId\": \"a0aabda6-cf91-4f3e-a81f-9198114a2279\"\n" +
 | 
			
		||||
                                    "    }\n" +
 | 
			
		||||
                                    "}"
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
            // 调用
 | 
			
		||||
        SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, apiTemplateId, templateParams);
 | 
			
		||||
            SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile,
 | 
			
		||||
                    apiTemplateId, templateParams);
 | 
			
		||||
            // 断言
 | 
			
		||||
            assertTrue(result.getSuccess());
 | 
			
		||||
        assertEquals(response.getRequestId(), result.getApiRequestId());
 | 
			
		||||
        assertEquals(response.getSendStatusSet()[0].getCode(), result.getApiCode());
 | 
			
		||||
        assertEquals(response.getSendStatusSet()[0].getMessage(), result.getApiMsg());
 | 
			
		||||
        assertEquals(response.getSendStatusSet()[0].getSerialNo(), result.getSerialNo());
 | 
			
		||||
            assertEquals("5000:1045710669157053657849499619", result.getSerialNo());
 | 
			
		||||
            assertEquals("a0aabda6-cf91-4f3e-a81f-9198114a2279", result.getApiRequestId());
 | 
			
		||||
            assertEquals("send success", result.getApiMsg());
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testDoSendSms_fail() throws Throwable {
 | 
			
		||||
        try (MockedStatic<HttpUtils> httpUtilsMockedStatic = mockStatic(HttpUtils.class)) {
 | 
			
		||||
            // 准备参数
 | 
			
		||||
            Long sendLogId = randomLongId();
 | 
			
		||||
            String mobile = randomString();
 | 
			
		||||
            String apiTemplateId = randomString();
 | 
			
		||||
            List<KeyValue<String, Object>> templateParams = Lists.newArrayList(
 | 
			
		||||
                    new KeyValue<>("1", 1234), new KeyValue<>("2", "login"));
 | 
			
		||||
        String requestId = randomString();
 | 
			
		||||
        String serialNo = randomString();
 | 
			
		||||
 | 
			
		||||
            // mock 方法
 | 
			
		||||
        SendSmsResponse response = randomPojo(SendSmsResponse.class, o -> {
 | 
			
		||||
            o.setRequestId(requestId);
 | 
			
		||||
            SendStatus[] sendStatuses = new SendStatus[1];
 | 
			
		||||
            o.setSendStatusSet(sendStatuses);
 | 
			
		||||
            SendStatus sendStatus = new SendStatus();
 | 
			
		||||
            sendStatuses[0] = sendStatus;
 | 
			
		||||
            sendStatus.setCode("ERROR");
 | 
			
		||||
            sendStatus.setMessage("send success");
 | 
			
		||||
            sendStatus.setSerialNo(serialNo);
 | 
			
		||||
        });
 | 
			
		||||
        when(client.SendSms(argThat(request -> {
 | 
			
		||||
            assertEquals(mobile, request.getPhoneNumberSet()[0]);
 | 
			
		||||
            assertEquals(properties.getSignature(), request.getSignName());
 | 
			
		||||
            assertEquals(apiTemplateId, request.getTemplateId());
 | 
			
		||||
            assertEquals(toJsonString(ArrayUtils.toArray(new ArrayList<>(MapUtils.convertMap(templateParams).values()), String::valueOf)),
 | 
			
		||||
                    toJsonString(request.getTemplateParamSet()));
 | 
			
		||||
            assertEquals(sendLogId, ReflectUtil.getFieldValue(JsonUtils.parseObject(request.getSessionContext(), TencentSmsClient.SessionContext.class), "logId"));
 | 
			
		||||
            return true;
 | 
			
		||||
        }))).thenReturn(response);
 | 
			
		||||
            httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString()))
 | 
			
		||||
                    .thenReturn(
 | 
			
		||||
                            "{\n" +
 | 
			
		||||
                                    "    \"Response\": {\n" +
 | 
			
		||||
                                    "        \"SendStatusSet\": [\n" +
 | 
			
		||||
                                    "            {\n" +
 | 
			
		||||
                                    "                \"SerialNo\": \"5000:1045710669157053657849499619\",\n" +
 | 
			
		||||
                                    "                \"PhoneNumber\": \"+8618511122233\",\n" +
 | 
			
		||||
                                    "                \"Fee\": 1,\n" +
 | 
			
		||||
                                    "                \"SessionContext\": \"test\",\n" +
 | 
			
		||||
                                    "                \"Code\": \"ERROR\",\n" +
 | 
			
		||||
                                    "                \"Message\": \"send success\",\n" +
 | 
			
		||||
                                    "                \"IsoCode\": \"CN\"\n" +
 | 
			
		||||
                                    "            },\n" +
 | 
			
		||||
                                    "        ],\n" +
 | 
			
		||||
                                    "        \"RequestId\": \"a0aabda6-cf91-4f3e-a81f-9198114a2279\"\n" +
 | 
			
		||||
                                    "    }\n" +
 | 
			
		||||
                                    "}"
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
            // 调用
 | 
			
		||||
        SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, apiTemplateId, templateParams);
 | 
			
		||||
            SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile,
 | 
			
		||||
                    apiTemplateId, templateParams);
 | 
			
		||||
            // 断言
 | 
			
		||||
            assertFalse(result.getSuccess());
 | 
			
		||||
        assertEquals(response.getRequestId(), result.getApiRequestId());
 | 
			
		||||
        assertEquals(response.getSendStatusSet()[0].getCode(), result.getApiCode());
 | 
			
		||||
        assertEquals(response.getSendStatusSet()[0].getMessage(), result.getApiMsg());
 | 
			
		||||
        assertEquals(response.getSendStatusSet()[0].getSerialNo(), result.getSerialNo());
 | 
			
		||||
            assertEquals("5000:1045710669157053657849499619", result.getSerialNo());
 | 
			
		||||
            assertEquals("a0aabda6-cf91-4f3e-a81f-9198114a2279", result.getApiRequestId());
 | 
			
		||||
            assertEquals("send success", result.getApiMsg());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
@@ -170,7 +148,6 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
 | 
			
		||||
                "        \"ext\": {\"logId\":\"67890\"}\n" +
 | 
			
		||||
                "    }\n" +
 | 
			
		||||
                "]";
 | 
			
		||||
        // mock 方法
 | 
			
		||||
 | 
			
		||||
        // 调用
 | 
			
		||||
        List<SmsReceiveRespDTO> statuses = smsClient.parseSmsReceiveStatus(text);
 | 
			
		||||
@@ -178,41 +155,45 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
 | 
			
		||||
        assertEquals(1, statuses.size());
 | 
			
		||||
        assertTrue(statuses.get(0).getSuccess());
 | 
			
		||||
        assertEquals("DELIVRD", statuses.get(0).getErrorCode());
 | 
			
		||||
        assertEquals("用户短信送达成功", statuses.get(0).getErrorMsg());
 | 
			
		||||
        assertEquals("13900000001", statuses.get(0).getMobile());
 | 
			
		||||
        assertEquals(LocalDateTime.of(2015, 10, 17, 8, 3, 4), statuses.get(0).getReceiveTime());
 | 
			
		||||
        assertEquals("12345", statuses.get(0).getSerialNo());
 | 
			
		||||
        assertEquals(67890L, statuses.get(0).getLogId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testGetSmsTemplate() throws Throwable {
 | 
			
		||||
 | 
			
		||||
        try (MockedStatic<HttpUtils> httpUtilsMockedStatic = mockStatic(HttpUtils.class)) {
 | 
			
		||||
 | 
			
		||||
            // 准备参数
 | 
			
		||||
        Long apiTemplateId = randomLongId();
 | 
			
		||||
        String requestId = randomString();
 | 
			
		||||
            String apiTemplateId = "1122";
 | 
			
		||||
 | 
			
		||||
            // mock 方法
 | 
			
		||||
        DescribeSmsTemplateListResponse response = randomPojo(DescribeSmsTemplateListResponse.class, o -> {
 | 
			
		||||
            DescribeTemplateListStatus[] describeTemplateListStatuses = new DescribeTemplateListStatus[1];
 | 
			
		||||
            DescribeTemplateListStatus templateStatus = new DescribeTemplateListStatus();
 | 
			
		||||
            templateStatus.setTemplateId(apiTemplateId);
 | 
			
		||||
            templateStatus.setStatusCode(0L);// 设置模板通过
 | 
			
		||||
            describeTemplateListStatuses[0] = templateStatus;
 | 
			
		||||
            o.setDescribeTemplateStatusSet(describeTemplateListStatuses);
 | 
			
		||||
            o.setRequestId(requestId);
 | 
			
		||||
        });
 | 
			
		||||
        when(client.DescribeSmsTemplateList(argThat(request -> {
 | 
			
		||||
            assertEquals(apiTemplateId, request.getTemplateIdSet()[0]);
 | 
			
		||||
            return true;
 | 
			
		||||
        }))).thenReturn(response);
 | 
			
		||||
            httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString()))
 | 
			
		||||
                    .thenReturn("{     \"Response\": {\n" +
 | 
			
		||||
                            "        \"DescribeTemplateStatusSet\": [\n" +
 | 
			
		||||
                            "            {\n" +
 | 
			
		||||
                            "                \"TemplateName\": \"验证码\",\n" +
 | 
			
		||||
                            "                \"TemplateId\": 1122,\n" +
 | 
			
		||||
                            "                \"International\": 0,\n" +
 | 
			
		||||
                            "                \"ReviewReply\": \"审批备注\",\n" +
 | 
			
		||||
                            "                \"CreateTime\": 1617379200,\n" +
 | 
			
		||||
                            "                \"TemplateContent\": \"您的验证码是{1}\",\n" +
 | 
			
		||||
                            "                \"StatusCode\": 0\n" +
 | 
			
		||||
                            "            },\n" +
 | 
			
		||||
                            "            \n" +
 | 
			
		||||
                            "        ],\n" +
 | 
			
		||||
                            "        \"RequestId\": \"f36e4f00-605e-49b1-ad0d-bfaba81c7325\"\n" +
 | 
			
		||||
                            "    }}");
 | 
			
		||||
 | 
			
		||||
            // 调用
 | 
			
		||||
        SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId.toString());
 | 
			
		||||
            SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId);
 | 
			
		||||
            // 断言
 | 
			
		||||
        assertEquals(response.getDescribeTemplateStatusSet()[0].getTemplateId().toString(), result.getId());
 | 
			
		||||
        assertEquals(response.getDescribeTemplateStatusSet()[0].getTemplateContent(), result.getContent());
 | 
			
		||||
            assertEquals("1122", result.getId());
 | 
			
		||||
            assertEquals("您的验证码是{1}", result.getContent());
 | 
			
		||||
            assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getAuditStatus());
 | 
			
		||||
        assertEquals(response.getDescribeTemplateStatusSet()[0].getReviewReply(), result.getAuditReason());
 | 
			
		||||
            assertEquals("审批备注", result.getAuditReason());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user