目录

Spring-Boot项目中集成阿里云短信服务

Spring Boot项目中集成阿里云短信服务

1. 导入阿里云依赖

首先,在你的 pom.xml 文件中添加阿里云短信服务的Maven依赖:

<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>dysmsapi20170525</artifactId>
    <version>2.0.24</version>
</dependency>

确保你已经包含了其他必要的依赖,如MySQL驱动和MyBatis:

<!-- MySQL Connector -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

<!-- MyBatis -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.0</version>
</dependency>

<!-- Lombok  -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

2. 配置客户端

2.1 在 application.propertiesapplication.yml 中配置阿里云凭证

application.properties 中添加以下配置:

# 阿里云短信服务配置
sms.api-key=your-access-key-id
sms.secret=your-access-key-secret

或者在 application.yml 中:

sms:
  api-key: your-access-key-id
  secret: your-access-key-secret
2.2 创建配置类来初始化阿里云SDK客户端实例

创建一个配置类来初始化阿里云SDK的客户端实例:

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.profile.DefaultProfile;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AliyunSmsConfig {

    @Value("${sms.api-key}")
    private String accessKeyId;

    @Value("${sms.secret}")
    private String accessKeySecret;

    @Bean
    public IAcsClient acsClient() {
        DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
        return new DefaultAcsClient(profile);
    }
}

3. 发送短信

接下来,我们编写具体的业务逻辑来发送短信。

3.1 创建控制器来处理HTTP请求

创建一个控制器类来处理发送短信的请求:

import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class SmsController {

    @Autowired
    private IAcsClient acsClient;

    @GetMapping("/sendSms/{phoneNumber}")
    public String sendSms(@PathVariable("phoneNumber") String phoneNumber) {
        CommonRequest request = new CommonRequest();
        request.setSysMethod(com.aliyuncs.http.MethodType.POST);
        request.setSysDomain("dysmsapi.aliyuncs.com");
        request.setSysVersion("2017-05-25");
        request.setSysAction("SendSms");

        // 设置参数
        request.putQueryParameter("PhoneNumbers", phoneNumber);
        request.putQueryParameter("SignName", "你的签名"); // 替换为已审核通过的签名名称
        request.putQueryParameter("TemplateCode", "SMS_XXXXXX"); // 替换为你申请的模板Code
        request.putQueryParameter("TemplateParam", "{\"code\":\"1234\"}"); // 根据模板内容设置参数

        try {
            CommonResponse response = acsClient.getCommonResponse(request);
            return response.getData(); // 返回响应数据
        } catch (ClientException e) {
            e.printStackTrace();
            return "Failed to send SMS.";
        }
    }
}

在这个示例中,我们定义了一个RESTful API端点 /sendSms/{phoneNumber} ,它接收手机号码作为路径变量,并尝试向该号码发送一条包含固定验证码(这里用 1234 代替)的短信。

3.2 使用异步方式发送短信(可选)

为了提高系统的并发处理能力,可以使用异步方式发送短信。首先启用异步支持:

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;

@Configuration
@EnableAsync
public class AsyncConfig {
}

然后,在服务层中使用异步方法:

import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class SmsService {

    @Autowired
    private IAcsClient acsClient;

    @Autowired
    private SmsRecordMapper smsRecordMapper;
    /**
     * 异步发送短信的方法
     *
     * @param phoneNumber 接收短信的手机号码
     * @param code        短信验证码
     */
    @Async  // 标记此方法为异步执行
    public void sendSmsAsync(String phoneNumber, String code) throws ClientException {
        CommonRequest request = new CommonRequest();
        request.setSysMethod(com.aliyuncs.http.MethodType.POST);
        request.setSysDomain("dysmsapi.aliyuncs.com");
        request.setSysVersion("2017-05-25");
        request.setSysAction("SendSms");

        // 设置参数
        request.putQueryParameter("PhoneNumbers", phoneNumber);
        request.putQueryParameter("SignName", "你的签名"); // 替换为已审核通过的签名名称
        request.putQueryParameter("TemplateCode", "SMS_XXXXXX"); // 替换为你申请的模板Code
        request.putQueryParameter("TemplateParam", "{\"code\":\"" + code + "\"}");

        try {
            CommonResponse response = acsClient.getCommonResponse(request);

            // 记录发送结果
            saveSmsRecord(phoneNumber, "验证码: " + code, response.getHttpStatus());
        } catch (ClientException e) {
            log.error("Failed to send SMS", e);
            saveSmsRecord(phoneNumber, "验证码: " + code, -1); // 假设-1表示发送失败
        }
    }
    
/**
     * 保存短信发送记录到数据库
     *
     * @param phoneNumber 手机号码
     * @param message     短信内容
     * @param status      发送状态(HTTP状态码或自定义错误码)
     */
    public void saveSmsRecord(String phoneNumber, String message, Integer status) {
        SmsRecord record = new SmsRecord();
        record.setPhoneNumber(phoneNumber);
        record.setMessage(message);
        record.setStatus(status);
        record.setSendTime(new Date());// 当前时间
        smsRecordMapper.insert(record);
    }
}

4. 签名和模板申请

4.1 签名申请

签名是短信内容前附加的个性化标识。你需要在阿里云控制台上申请签名,并等待审核通过。

示例代码:签名新增(Java)

创建服务接口和其实现类来处理签名申请:

import com.aliyuncs.dysmsapi.model.v20170525.AddSmsSignRequest;
import com.aliyuncs.dysmsapi.model.v20170525.AddSmsSignResponse;
import com.aliyuncs.exceptions.ClientException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

public interface ISmsSignService {
    AddSmsSignResponse createSmsSign(String signName, Integer signType, String remark, List<AddSmsSignRequest.SmsSignFileList> fileList) throws ClientException;
}

@Service
@Slf4j
public class SmsSignServiceImpl implements ISmsSignService {

    @Autowired
    private IAcsClient acsClient;

    @Override
    public AddSmsSignResponse createSmsSign(String signName, Integer signType, String remark, List<AddSmsSignRequest.SmsSignFileList> fileList) throws ClientException {
        AddSmsSignRequest request = new AddSmsSignRequest()
                .setSignName(signName)
                .setSignSource(signType)
                .setRemark(remark)
                .setSmsSignFileList(fileList);

        try {
            return acsClient.getAcsResponse(request);
        } catch (ClientException e) {
            log.error("Failed to add SMS sign", e);
            throw e;
        }
    }
}
4.2 模板申请

模板是具体要发送的短信内容格式。你需要在阿里云控制台上申请模板,并等待审核通过。

示例代码:模板新增(Java)

同样地,创建服务接口和其实现类来处理模板申请:

import com.aliyuncs.dysmsapi.model.v20170525.AddSmsTemplateRequest;
import com.aliyuncs.dysmsapi.model.v20170525.AddSmsTemplateResponse;
import com.aliyuncs.exceptions.ClientException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

public interface ISmsTemplateService {
    AddSmsTemplateResponse createSmsTemplate(String templateName, String templateContent, String remark) throws ClientException;
}

@Service
@Slf4j
public class SmsTemplateServiceImpl implements ISmsTemplateService {

    @Autowired
    private IAcsClient acsClient;

    @Override
    public AddSmsTemplateResponse createSmsTemplate(String templateName, String templateContent, String remark) throws ClientException {
        AddSmsTemplateRequest request = new AddSmsTemplateRequest()
                .setTemplateName(templateName)
                .setTemplateContent(templateContent)
                .setRemark(remark);

        try {
            return acsClient.getAcsResponse(request);
        } catch (ClientException e) {
            log.error("Failed to add SMS template", e);
            throw e;
        }
    }
}

5. 数据库记录短信发送情况

5.1 配置数据源

application.properties 文件中配置MySQL数据源:

# MySQL DataSource Configuration
spring.datasource.url=jdbc:mysql://localhost:3306/your_database_name?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=your_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# MyBatis Configuration
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.example.yourproject.model
5.2 创建实体类和Mapper接口

假设我们需要存储短信发送记录,可以创建一个 SmsRecord 实体类和对应的Mapper接口。

import lombok.Data;

@Data
public class SmsRecord {
    private Long id;
    private String phoneNumber;
    private String message;
    private Date sendTime;
    private Integer status;
}

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface SmsRecordMapper {
    @Insert("INSERT INTO sms_record(phone_number, message, send_time, status) VALUES(#{phoneNumber}, #{message}, NOW(), #{status})")
    int insert(SmsRecord record);
}

对应的XML映射文件 SmsRecordMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.yourproject.mapper.SmsRecordMapper">

    <resultMap id="BaseResultMap" type="com.example.yourproject.model.SmsRecord">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <result column="phone_number" property="phoneNumber" jdbcType="VARCHAR"/>
        <result column="message" property="message" jdbcType="VARCHAR"/>
        <result column="send_time" property="sendTime" jdbcType="TIMESTAMP"/>
        <result column="status" property="status" jdbcType="INTEGER"/>
    </resultMap>

    <insert id="insert" parameterType="com.example.yourproject.model.SmsRecord">
        INSERT INTO sms_record(phone_number, message, send_time, status)
        VALUES(#{phoneNumber}, #{message}, NOW(), #{status})
    </insert>

</mapper>

6. 查询发送记录

为了确保短信成功发送或排查问题,你可以随时查询短信的发送状态。这通常也是通过调用API来完成的。

6.1 查询接口

调用查询接口获取特定时间段内的短信发送记录及其状态:

import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse;
import com.aliyuncs.exceptions.ClientException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class SmsQueryController {

    @Autowired
    private IAcsClient acsClient;

    @GetMapping("/querySms/{phoneNumber}")
    public String querySendStatus(@PathVariable("phoneNumber") String phoneNumber) {
        QuerySendDetailsRequest request = new QuerySendDetailsRequest();
        request.setPhoneNumber(phoneNumber); // 设置目标手机号码
        request.setSendDate(new SimpleDateFormat("yyyyMMdd").format(new Date())); // 设置查询日期
        request.setPageSize(10L); // 设置每页大小
        request.setCurrentPage(1L); // 设置当前页码

        try {
            QuerySendDetailsResponse response = acsClient.getAcsResponse(request);
            return response.getBody().toString();
        } catch (ClientException e) {
            log.error("Failed to query SMS send status", e);
            return "Failed to query SMS send status.";
        }
    }
}

总结

以上是对如何在Spring Boot项目中集成阿里云短信服务的详细说明。包括了依赖导入、配置客户端、发送短信的具体实现、签名和模板申请、数据库记录短信发送情况以及查询发送记录等关键步骤。请根据实际需求调整上述代码中的参数和配置项,例如替换真实的Access Key ID和Secret、签名名称和模板Code等。同时,确保敏感信息的安全管理,不要在公开场合暴露这些信息。