前提:在阿里云或腾讯云购买短信,填写相关信息,准备好模板参数等等。
思路:将需要发送的短信保存到数据库,短信发送项目从数据库获取需要发送的短信,用线程的形式,循环读取数据库内容并发送,读取一次睡眠3秒,也就是每3秒执行一次。
可能用到的mysql的表:
待发送短信列表 message
CREATE TABLE `message` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`message_type` tinyint unsigned NOT NULL COMMENT '类型 1验证码 2通知',
`send_platform` tinyint unsigned NOT NULL COMMENT '发送平台 1阿里云 2 阿里云批量 3腾讯云',
`sign_name` varchar(256) NOT NULL COMMENT '短信签名',
`template_code` varchar(225) NOT NULL COMMENT '短信模板编码',
`template_params` varchar(256) NOT NULL COMMENT '短信模板参数',
`tels` varchar(1100) NOT NULL COMMENT '手机号码,多个用,隔开,腾讯的前面加+86',
`template_content` varchar(256) NOT NULL COMMENT '短信模板内容',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
`status` tinyint unsigned NOT NULL COMMENT '发送状态 0待发 1发送中 2已发',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=93 DEFAULT CHARSET=utf8 COMMENT='待发送短信列表';
示例
已发送短信列表 message_sent
CREATE TABLE `message_sent` (
`id` bigint unsigned NOT NULL COMMENT 'id',
`message_type` tinyint unsigned NOT NULL COMMENT '类型 1验证码 2通知',
`send_platform` tinyint unsigned NOT NULL COMMENT '发送平台 1阿里云 2 阿里云批量 3腾讯云',
`sign_name` varchar(256) NOT NULL COMMENT '短信签名',
`template_code` varchar(225) NOT NULL COMMENT '短信模板编码',
`template_params` varchar(256) NOT NULL COMMENT '短信模板参数',
`tels` varchar(1100) NOT NULL COMMENT '手机号码,多个用,隔开,腾讯的前面加+86',
`template_content` varchar(256) NOT NULL COMMENT '短信模板内容',
`request_id` varchar(225) DEFAULT NULL COMMENT '返回msgId',
`status` tinyint unsigned NOT NULL COMMENT '发送结果 0待定 1成功 2失败',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='已发送短信列表';
短信模板 message_template
CREATE TABLE `message_template` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`message_key` varchar(256) DEFAULT NULL COMMENT '短信key',
`message_name` varchar(256) DEFAULT NULL COMMENT '短信名称',
`platform` tinyint unsigned NOT NULL COMMENT '短信平台 1阿里云 3腾讯云',
`is_enable` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '启用状态,0停用 1启用',
`sign_name` varchar(256) NOT NULL COMMENT '短信签名',
`template_code` varchar(225) NOT NULL COMMENT '短信模板编码',
`template_content` varchar(256) NOT NULL COMMENT '短信模板内容',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
`creator` varchar(16) DEFAULT NULL COMMENT '创建人',
`gmt_modified` datetime DEFAULT NULL COMMENT '更新时间',
`modifier` varchar(16) DEFAULT NULL COMMENT '更新人',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='短信模板';
示例:
搭建项目
idea新建maven项目,去除其他目录结构。如下图:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.uwa.srm</groupId>
<artifactId>srm-sms-task</artifactId>
<description>这里集成短信发送</description>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.49</version>
</dependency>
<!-- 阿里云短信 -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.5.3</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
<version>1.1.0</version>
</dependency>
<!-- 腾讯云短信 -->
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>3.1.62</version><!-- 注:这里只是示例版本号,请获取并替换为 最新的版本号 -->
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.uwa.srm.task.SrmSmsTask</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
核心代码
AliSmsConfig.java
/**
* 阿里云短信发送--配置
*/
public class AliSmsConfig {
public static String ACCESS_KEY_ID = "xxxx";
public static String ACCESS_KEY_SECRET = "xxxxx";
}
AliSmsUtil.java
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendBatchSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendBatchSmsResponse;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.uwa.srm.task.message.Message;
/**
* 阿里云短信发送
*/
public class AliSmsUtil {
public static final String CODE_SUCCESS = "OK";
//产品名称:云通信短信API产品,开发者无需替换
static final String product = "Dysmsapi";
//产品域名,开发者无需替换
static final String domain = "dysmsapi.aliyuncs.com";
/*
*--返回代码
{
"Message":"OK",
"RequestId":"2184201F-BFB3-446B-B1F2-C746B7BF0657",
"BizId":"197703245997295588^0",
"Code":"OK"
}*/
public static SendSmsResponse sendSms(Message msg) throws ClientException {
//可自助调整超时时间
System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
System.setProperty("sun.net.client.defaultReadTimeout", "10000");
//初始化acsClient,暂不支持region化
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", AliSmsConfig.ACCESS_KEY_ID, AliSmsConfig.ACCESS_KEY_SECRET);
DefaultProfile.addEndpoint("cn-hangzhou", product, domain);
IAcsClient acsClient = new DefaultAcsClient(profile);
//组装请求对象-具体描述见控制台-文档部分内容
SendSmsRequest request = new SendSmsRequest();
//必填:待发送手机号
request.setPhoneNumbers(msg.getTels());
//必填:短信签名-可在短信控制台中找到
request.setSignName(msg.getSignName());
//必填:短信模板-可在短信控制台中找到
request.setTemplateCode(msg.getTemplateCode());
//模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为"{\"name\":\"Tom\", \"code\":\"123\"}"
request.setTemplateParam(msg.getTemplateParams());
//hint 此处可能会抛出异常,注意catch
SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
return sendSmsResponse;
}
public static SendBatchSmsResponse SendBatchSms(Message msg) throws ClientException {
//可自助调整超时时间
System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
System.setProperty("sun.net.client.defaultReadTimeout", "10000");
//初始化acsClient,暂不支持region化
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", AliSmsConfig.ACCESS_KEY_ID, AliSmsConfig.ACCESS_KEY_SECRET);
DefaultProfile.addEndpoint("cn-hangzhou", product, domain);
IAcsClient acsClient = new DefaultAcsClient(profile);
//组装请求对象
SendBatchSmsRequest request = new SendBatchSmsRequest();
//使用post提交
//必填:待发送手机号。支持JSON格式的批量调用,批量上限为100个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式
//"[\"1500000000\",\"1500000001\"]"
request.setPhoneNumberJson(JSONObject.toJSONString(msg.getTels().split(",")));
//必填:短信签名-支持不同的号码发送不同的短信签名
//"[\"云通信\",\"云通信\"]"
request.setSignNameJson(JSONObject.toJSONString(msg.getSignName().split(",")));
//必填:短信模板-可在短信控制台中找到
request.setTemplateCode(msg.getTemplateCode());
//必填:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
//友情提示:如果JSON中需要带换行符,请参照标准的JSON协议对换行符的要求,比如短信内容中包含\r\n的情况在JSON中需要表示成\\r\\n,否则会导致JSON在服务端解析失败
//"[{\"name\":\"Tom\", \"code\":\"123\"},{\"name\":\"Jack\", \"code\":\"456\"}]"
request.setTemplateParamJson(msg.getTemplateParams());
//可选-上行短信扩展码(扩展码字段控制在7位或以下,无特殊需求用户请忽略此字段)
//request.setSmsUpExtendCodeJson("[\"90997\",\"90998\"]");
//请求失败这里会抛ClientException异常
SendBatchSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
return sendSmsResponse;
}
}
MessageSent.java
import java.sql.Timestamp;
import java.io.Serializable;
public class MessageSent implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
private Long id;
/**
* 类型 1验证码 2通知
*/
private Integer messageType;
/**
* 发送平台 1阿里云 2腾讯云
*/
private Integer sendPlatform;
/**
* 短信签名
*/
private String signName;
/**
* 短信模板编码
*/
private String templateCode;
/**
* 短信模板参数
*/
private String templateParams;
/**
* 手机号码,多个用,隔开
*/
private String tels;
/**
* 短信模板内容
*/
private String templateContent;
/**
* 返回msgId
*/
private String requestId;
/**
* 发送结果 0待定 1成功 2失败
*/
private Integer status;
/**
* 创建时间
*/
private Timestamp gmtCreate;
//......
}
SendScheduler.java
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.dysmsapi.model.v20170525.SendBatchSmsResponse;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.tencentcloudapi.sms.v20190711.models.SendStatus;
import com.uwa.srm.task.config.Conn;
import com.uwa.srm.task.dysmsapi.AliSmsUtil;
import com.uwa.srm.task.tencentapi.TencentSmsUtil;
import jdk.nashorn.internal.runtime.logging.Logger;
import java.sql.Connection;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Logger
public class SendScheduler extends Thread {
protected final ThreadPoolExecutor executor;
protected transient boolean alive = true;
private MessageRepository msgRep;
private Connection conn;
public SendScheduler() {
executor = new ThreadPoolExecutor(10, 50, 30, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>());
msgRep = new MessageRepository();
conn = Conn.getConn();
}
public void halt() {
alive = false;
try {
executor.shutdownNow();
} finally {
this.interrupt();
}
}
@Override
public void run() {
while (alive) {
List<Message> list = msgRep.getUnredMessage(conn);
if (list != null && list.size() > 0) {
Runner runner = new Runner(list);
executor.submit(runner);
}
try {
sleep(3000);
} catch (InterruptedException e) {
alive = false;
break;
}
}
}
protected class Runner implements Runnable {
protected List<Message> list;
public Runner(List<Message> list) {
this.list = list;
}
public void send(Message msg) {
int status = 2;
String requestId = "";
try {
//判断平台
if (msg.getSendPlatform() == 1) {
SendSmsResponse response = AliSmsUtil.sendSms(msg);
System.out.println("阿里云-发送短信返回值:" + JSONObject.toJSONString(response));
if (response != null) {
requestId = response.getRequestId();
if (AliSmsUtil.CODE_SUCCESS.equals(response.getCode())) {
//成功
status = 1;
} else {
//失败
status = 2;
}
} else {
//异常其它
status = 3;
}
} else if (msg.getSendPlatform() == 2) {
SendBatchSmsResponse response = AliSmsUtil.SendBatchSms(msg);
System.out.println("阿里云-发送短信返回值:" + JSONObject.toJSONString(response));
if (response != null) {
requestId = response.getRequestId();
if (AliSmsUtil.CODE_SUCCESS.equals(response.getCode())) {
//成功
status = 1;
} else {
//失败
status = 2;
}
} else {
//异常其它
status = 3;
}
} else if (msg.getSendPlatform() == 3) {
com.tencentcloudapi.sms.v20190711.models.SendSmsResponse response = TencentSmsUtil.sendSms(msg);
System.out.println("腾讯云-发送短信返回值:" + JSONObject.toJSONString(response));
if (response != null) {
boolean isAllFail = true;
requestId = response.getRequestId();
for (SendStatus sendStatus : response.getSendStatusSet()) {
if (sendStatus != null && TencentSmsUtil.CODE_SUCCESS.equals(sendStatus.getCode())) {
isAllFail = false;
break;
}
}
if (!isAllFail) {
status = 1;
} else {
status = 2;
}
} else {
//异常其它
status = 3;
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
msg.setStatus(status);
msg.setRequestId(requestId);
try {
msgRep.copyMessage(conn, msg);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void run() {
if (list == null || list.size() == 0) {
return;
}
int len = list.size();
for (int i = 0; i < len; i++) {
send(list.get(i));
}
}
}
}
TencentSmsConfig.java
import com.aliyuncs.exceptions.ClientException;
import com.tencentcloudapi.sms.v20190711.models.SendSmsResponse;
import com.uwa.srm.task.message.Message;
/**
* 腾讯云短信发送--配置
*/
public class TencentSmsConfig {
public static String ACCESS_KEY_ID = "AKIDqSpyAp5gfWCdgXHeBIXVXJcAUYLsPJSM";
public static String ACCESS_KEY_SECRET = "6zqYI8nv0rX8k9MKRAhaEAuQ8aJmyylI";
/* 填充请求参数,这里 request 对象的成员变量即对应接口的入参
* 您可以通过官网接口文档或跳转到 request 对象的定义处查看请求参数的定义
* 基本类型的设置:
* 帮助链接:
* 短信控制台:https://console.cloud.tencent.com/smsv2
* sms helper:https://cloud.tencent.com/document/product/382/3773 */
/* 短信应用 ID: 在 [短信控制台] 添加应用后生成的实际 SDKAppID,例如1400006666 */
public static String APP_ID = "1400475158";
}
TencentSmsUtil.java
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.sms.v20190711.SmsClient;
import com.tencentcloudapi.sms.v20190711.models.SendSmsRequest;
import com.tencentcloudapi.sms.v20190711.models.SendSmsResponse;
import com.uwa.srm.task.message.Message;
/**
* 腾讯云短信发送
*/
public class TencentSmsUtil {
public static final String CODE_SUCCESS = "Ok";
/*
*--返回代码
{
"Response": {
"SendStatusSet": [
{
"SerialNo": "5000:1045710669157053657849499619",
"PhoneNumber": "+8618511122233",
"Fee": 1,
"SessionContext": "test",
"Code": "Ok",
"Message": "send success",
"IsoCode": "CN"
},
{
"SerialNo": "5000:104571066915705365784949619",
"PhoneNumber": "+8618511122266",
"Fee": 1,
"SessionContext": "test",
"Code": "Ok",
"Message": "send success",
"IsoCode": "CN"
}
],
"RequestId": "a0aabda6-cf91-4f3e-a81f-9198114a2279"
}
}*/
public static SendSmsResponse sendSms(Message msg) throws TencentCloudSDKException {
Credential cred = new Credential(TencentSmsConfig.ACCESS_KEY_ID, TencentSmsConfig.ACCESS_KEY_SECRET);
// 实例化一个 http 选项,可选,无特殊需求时可以跳过
HttpProfile httpProfile = new HttpProfile();
/* SDK 会自动指定域名,通常无需指定域名,但访问金融区的服务时必须手动指定域名 */
httpProfile.setEndpoint("sms.tencentcloudapi.com");
/* 非必要步骤:
* 实例化一个客户端配置对象,可以指定超时时间等配置 */
ClientProfile clientProfile = new ClientProfile();
clientProfile.setSignMethod("HmacSHA256");
clientProfile.setHttpProfile(httpProfile);
//地域信息,可以直接填写字符串 ap-guangzhou,或者引用预设的常量
String region = "ap-guangzhou";
SmsClient client = new SmsClient(cred, region, clientProfile);
SendSmsRequest req = new SendSmsRequest();
req.setSmsSdkAppid(TencentSmsConfig.APP_ID);
req.setSign(msg.getSignName());
req.setTemplateID(msg.getTemplateCode());
/* 下发手机号码,"+8621212313123", "+8612345678902", "+8612345678903"*/
req.setPhoneNumberSet(msg.getTels().split(","));
if (msg.getTemplateParams() != null) {
req.setTemplateParamSet(msg.getTemplateParams().split(","));
}
SendSmsResponse sendSmsResponse = client.SendSms(req);
return sendSmsResponse;
}
}
SrmSmsTask .java
import com.uwa.srm.task.message.SendScheduler;
public class SrmSmsTask {
public static void main(String[] args){
new SendScheduler().start();
}
}
数据库操作部分自己清楚,项目完成。