你是否经常接到一些垃圾短信,这些短信是否使你厌烦?现在教给大家如何发送垃圾​短信。​

本文用例是采用了阿里云的短信服务

添加 Maven 依赖:

<dependency>
  <groupId>com.aliyun</groupId>
  <artifactId>aliyun-java-sdk-core</artifactId>
  <version>4.1.0</version>
</dependency>

编写阿里云短信工具类 SMSUtil.java

@Slf4j
@Component
public class SMSUtil {
    @Autowired
    private SMSPutLogMapper smsPutLogMapper;

    private static SMSUtil smsUtil;

    /**
     * 静态调 Mapper
     */
    @PostConstruct
    public void init() {
        smsUtil = this;
        smsUtil.smsPutLogMapper = this.smsPutLogMapper;
    }

    private static final String accessKeyId = "accessKeyId";
    private static final String secret = "secret";

    /**
     * 短信发送接口
     *
     * @param dto
     * @return 发送回执ID
     */
    public static String sendSMS(SMSDto dto) {
        CommonRequest request = new CommonRequest();
        //request.setProtocol(ProtocolType.HTTPS);
        request.setMethod(MethodType.POST);
        request.setDomain("dysmsapi.aliyuncs.com");
        request.setVersion("2017-05-25");
        request.setAction("SendSms");
        request.putQueryParameter("PhoneNumbers", dto.getPhoneNumbers());
        request.putQueryParameter("SignName", "SignName");
        request.putQueryParameter("TemplateCode", dto.getTemplateCode());

        if (!Strings.isNullOrEmpty(dto.getTemplateParam())) {
            request.putQueryParameter("TemplateParam", dto.getTemplateParam());
        }

        String resData = baseSMS(request);

        int saveRes = saveSMSLog(dto, resData);

        String message = JSONObject.parseObject(resData).get("Message").toString();
        String code = JSONObject.parseObject(resData).get("Code").toString();

        if ("OK".equals(message) && "OK".equals(code)) {
            return JSONObject.parseObject(resData).get("BizId").toString();
        } else {
            return code + message + "saveRes:" + saveRes;
        }
    }

    /**
     * 保存发送日志
     *
     * @param dto
     * @param str
     * @return
     */
    private static int saveSMSLog(SMSDto dto, String str) {
        String message = JSONObject.parseObject(str).get("Message").toString();
        String code = JSONObject.parseObject(str).get("Code").toString();
        String bizId = "error!", requestId = "error!";
        if ("OK".equals(message) && "OK".equals(code)) {
            bizId = JSONObject.parseObject(str).get("BizId").toString();
            requestId = JSONObject.parseObject(str).get("RequestId").toString();
        }

        SMSPutLog log = new SMSPutLog();
        log.setPutTime(LocalDateTime.now())
                .setTemplateCode(dto.getTemplateCode())
                .setSucNum(dto.getPhoneNumbers().split(",").length)
                .setBizId(bizId)
                .setRequestId(requestId);

        return smsUtil.smsPutLogMapper.insert(log);
    }

    /**
     * 查看短信发送记录和发送状态
     *
     * @param bizId       发送回执ID
     * @param phoneNumber 接收短信的手机号码
     * @param sendDate    短信发送日期,支持查询最近30天的记录, 格式为yyyyMMdd,默认当前时间
     * @return SmsSendDetailDTOs
     */
    public static String querySendDetails(String bizId, String phoneNumber, String sendDate) {
        if (Strings.isNullOrEmpty(sendDate)) {
            try {
                sendDate = DateUtil.dateFormat(null, "yyyyMMdd");
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }

        CommonRequest request = new CommonRequest();
        //request.setProtocol(ProtocolType.HTTPS);
        request.setMethod(MethodType.POST);
        request.setDomain("dysmsapi.aliyuncs.com");
        request.setVersion("2017-05-25");
        request.setAction("QuerySendDetails");
        request.putQueryParameter("PhoneNumber", phoneNumber);
        request.putQueryParameter("SendDate", sendDate);
        request.putQueryParameter("PageSize", "10");
        request.putQueryParameter("CurrentPage", "1");
        request.putQueryParameter("BizId", bizId);

        String resData = baseSMS(request);

        String message = JSONObject.parseObject(resData).get("Message").toString();
        String code = JSONObject.parseObject(resData).get("Code").toString();

        if ("OK".equals(message) && "OK".equals(code)) {
            return JSONObject.parseObject(resData).get("SmsSendDetailDTOs").toString();
        } else {
            return code + message;
        }
    }

    private static String baseSMS(CommonRequest request) {
        DefaultProfile profile = DefaultProfile.getProfile("default", accessKeyId, secret);
        IAcsClient client = new DefaultAcsClient(profile);

        try {
            CommonResponse response = client.getCommonResponse(request);
            log.info(response.getData());
            return response.getData();
        } catch (ServerException e) {
            e.printStackTrace();
        } catch (ClientException e) {
            e.printStackTrace();
        }
        return "{\"Message\":\"ERROR\",\"Code\":\"ERROR\"}";
    }
}

服务实现类代码如下:

public String sendSmsTask(SendSMSDto dto) {
    try {
        if (!Strings.isNullOrEmpty(dto.getPhoneNumbers())) {
            String[] strs = dto.getPhoneNumbers().split(",");
            // 填会员ID
            if (strs[0].length() < 11) {
                dto.setPhoneNumbers(getMemberPhonsById(strs));
            }
            // 填手机号(不变)
        }
    } catch (Exception e) {
        return "输入异常";
    }

    String bizId = "";

    SMSDto smsDto = new SMSDto();
    smsDto.setPhoneNumbers(dto.getPhoneNumbers())
            .setTemplateCode(dto.getTemplateCode())
            .setTemplateParam(dto.getTemplateParam());
    if (dto.getSendDateTime() == null) {
        bizId = SMSUtil.sendSMS(smsDto);
    } else {
        Timer timer = new Timer();
        timer.schedule(new SendSMSTask(smsDto), DateUtil.toDate(dto.getSendDateTime()));
        bizId = "定时任务开启:发送时间" + dto.getSendDateTime().toString();
        log.info("定时任务开启:发送时间" + dto.getSendDateTime().toString());
    }
    return bizId;
}

其中,SendSMSDto.java 模型如下:

@Data
@Accessors(chain = true)
public class SendSMSDto extends BaseSign {
    @NotBlank(message = "手机号/会员ID 不能为空")
    @ApiModelProperty("手机号/会员ID,多个逗号分隔")
    private String phoneNumbers;
    @NotBlank(message = "短信模板不能为空")
    @ApiModelProperty("短信模板ID")
    private String templateCode;
    @ApiModelProperty("发送时间(yyyy-MM-dd HH:mm:ss)默认当前时间")
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime sendDateTime;
    @ApiModelProperty("短信模板参数(例:{\"code\":\"1111\"})")
    private String templateParam;
}

SendSMSDto 中,继承的 BaseSign 为校验字段,防止其他人恶意调用接口。其中的规则可以自定义,且有时效性。

由于业务需求,发送短信接口可接受的参数包含手机号或会员的ID,如果输入的 phoneNumbers 为用户 ID 则会自动查表取出用户的手机号。

短信的模板和模板参数要对应,详见阿里云短信服务的接口简介

除此之外,SendSMSDto 还接收一个 sendDateTime 参数,可以指定发送时间,这就用到了定时任务。由于之前的系列文章内没有单独讲过定时任务,在本文中会简单介绍下,将来会单独写一篇文章介绍定时任务。


如果发送时间不填写,则会直接执行 SMSUtil 里面的 sendSMS 方法。

返回参数示例:

名称类型示例值描述
BizIdString900619746936498440^0

发送回执ID,可根据该ID在接口QuerySendDetails中查询具体的发送状态。

CodeStringOK

请求状态码。

MessageStringOK

状态码的描述。

RequestIdStringF655A8D5-B967-440B-8683-DAD6FF8DE990

请求ID。

如果填写了发送时间,则开启一个定时任务 Timmer ,开辟一个新线程。
new Timer().schedule(new SendSMSTask(smsDto), DateUtil.toDate(dto.getSendDateTime()));

SendSMSTask.java:

@Slf4j
public class SendSMSTask extends TimerTask {
    private SMSDto dto;

    @Override
    public void run() {
        String bizId = SMSUtil.sendSMS(dto);
        log.info("定时发送短信执行,回执:" + bizId);
    }

    public SendSMSTask(SMSDto dto) {
        this.dto = dto;
    }
}

到了指定的时间会自动执行 sendSMS 函数,并保存短信回执。