官网流程文档:
https://help.aliyun.com/document_detail/55284.html?spm=a2c4g.11186623.6.567.77f914d1QWnwjX
登录阿里大于: https://dayu.aliyun.com/
去短信服务
https://dysms.console.aliyun.com/dysms.htm?spm=5176.12818093.0.ddysms.6eac16d0SKkGSC#/domestic/text/template
设置签名模板(需要审核)
拿到 AccessKey
总结需要的东西
短信签名,短信模板 id,AccessKey,AccessKey Secret,version(版本信息,有点坑), Action(SendSms)关键的还有一个 jar 包(依赖)
pom.xml(需要用到消息微服务的api----因为AliSmsModel类在消息api中)
<dependency>
<groupId>com.xiaoge</groupId>
<artifactId>message-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
RedisCacheConfig(有这个配置启动类上必须加@EnableCaching注解, 把缓存叫给redis, 用redis客户端查看也不会乱码)
package com.xiaoge.config;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
@Configuration
public class RedisCacheConfig {
//使用了cache的注解,需要手动设置序列化
private CacheProperties cacheProperties;
/**
* 该配置类被构造时,它里面的参数,将有Spring 的ioc 容器提供
*
* @param cacheProperties
*/
public RedisCacheConfig(CacheProperties cacheProperties) {
this.cacheProperties = cacheProperties;
}
/**
* 定义Redis缓存的序列化的形式
* 在这个RedisSerializer类里面提交了3 种常用的序列化形式
* 1 java jdk的序列化 (默认的形式)
* 2 json jackson (我们可以手动切换)
* 3 String String的序列化(主要用在字符串的存储,不是对象) 对key 一般使用的是String的序列化
*/
@Bean
public RedisCacheConfiguration redisCacheConfiguration() {
CacheProperties.Redis redisProperties = this.cacheProperties.getRedis();
RedisCacheConfiguration config = RedisCacheConfiguration
.defaultCacheConfig();
config = config.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()));
// 缓存的过期时间,一般我们在配置文件里面设置
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
// 是否给缓存添加前缀
if (redisProperties.getKeyPrefix() != null) {
config = config.prefixKeysWith(redisProperties.getKeyPrefix());
}
// 空值的缓存
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
//禁用key
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
return config;
}
}
启动类
@SpringBootApplication
@EnableEurekaClient
@EnableCaching
public class MemberServiceApplication {
public static void main(String[] args) {
SpringApplication.run(MemberServiceApplication.class, args);
}
}
UserController
package com.xiaoge.controller;
import com.xiaoge.domain.User;
import com.xiaoge.domain.UserCollection;
import com.xiaoge.service.UserCollectionService;
import com.xiaoge.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
* @Classname UserController
* @Date 2022/6/6 下午8:23
* @Created by xiaoge
* @Description TODO
*/
@Api(tags = "前台用户管理接口")
@RestController
@RequestMapping
public class UserController {
@Autowired
private UserService userService;
@ApiImplicitParams({
@ApiImplicitParam(name = "phoneNum", value = "用户手机号, 验证码")
})
@ApiOperation("修改用户信息")
@PostMapping("p/sms/send")
public ResponseEntity<Void> bindPhoneNum(@RequestBody Map<String, String> phoneNum) {
String openId = SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString();
userService.bindPhoneNum(openId, phoneNum);
return ResponseEntity.ok().build();
}
}
UserService
package com.xiaoge.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xiaoge.domain.User;
import java.util.Map;
/**
* @Classname UserService
* @Date 2022/6/6 下午7:00
* @Created by xiaoge
* @Description TODO
*/
public interface UserService extends IService<User>{
/**
* 用户绑定手机号
* @param openId
* @param phoneNum
*/
Boolean bindPhoneNum(String openId, Map<String, String> phoneNum);
}
UserServiceImpl
package com.xiaoge.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaoge.constant.QueueConstant;
import com.xiaoge.domain.User;
import com.xiaoge.mapper.UserMapper;
import com.xiaoge.model.AliSmsModel;
import com.xiaoge.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
/**
* @Classname UserServiceImpl
* @Date 2022/6/6 下午7:00
* @Created by xiaoge
* @Description TODO
*/
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService{
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private RedisTemplate redisTemplate;
/**
* 用户绑定手机号
* 1. 生成一个code
* 2. 放在redis
* 3. 设置一个过期时间 5min
* 4. 组装参数
* 5. 放入mq
* 6. 返回
* @param openId
* @param phoneNum
*/
@Override
public Boolean bindPhoneNum(String openId, Map<String, String> phoneNum) {
String phonenum = phoneNum.get("phonenum");
// 生成验证码
String code = createCode();
// 放入redis
redisTemplate.opsForValue().set(phonenum, code, Duration.ofMinutes(5));
// 组装参数 放mq 短信的签名 短信的模板,短信的code
AliSmsModel aliSmsModel = new AliSmsModel();
aliSmsModel.setPhoneNumbers(phonenum);
aliSmsModel.setSignName("ego商城");
aliSmsModel.setTemplateCode("SMS_203185255");
Map<String, String> map = new HashMap<>(2);
map.put("code", code);
aliSmsModel.setTemplateParam(JSON.toJSONString(map));
// 发送消息
rabbitTemplate.convertAndSend(QueueConstant.PHONE_CHANGE_EX, QueueConstant.PHONE_CHANGE_KEY, JSON.toJSONString(aliSmsModel));
return true;
}
/**
* 生成验证码
* @return
*/
private String createCode() {
// 位数 4 6 8多一点
// 随机生成验证码
return "88888888";
}
}
message-api中模型类 用户微服务用到了该模型类
package com.whsxt.model;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ApiModel("发送阿里大于短信的实体类")
public class AliSmsModel {
@ApiModelProperty("手机号")
private String phoneNumbers;
@ApiModelProperty("签名")
private String signName;
@ApiModelProperty("模板id")
private String templateCode;
@ApiModelProperty("参数")
private String templateParam;
@ApiModelProperty("短信扩展码")
private String smsUpExtendCode;
@ApiModelProperty("外部流水扩展字段")
private String outId;
}
pom.xml
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.0.6</version> <!-- 注:如提示报错,先升级基础包版,无法解决可联系技术支持 -->
</dependency>
RabbitMQConfig(发送短信mq配置 PHONE_CHANGE_QUEUE都是自己随便定义的常量值)
package com.xiaoge.config;
import com.xiaoge.constant.QueueConstant;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Classname RabbitMQConfig
* @Date 2022/6/25 下午2:56
* @Created by xiaoge
* @Description TODO
*/
@Configuration
public class RabbitMQConfig {
/* 短信mq配置 */
@Bean
public Queue phoneChangeQueue() {
return new Queue(QueueConstant.PHONE_CHANGE_QUEUE);
}
@Bean
public DirectExchange phoneChangeExchange() {
return new DirectExchange(QueueConstant.PHONE_CHANGE_EX);
}
@Bean
public Binding bind() {
return BindingBuilder
.bind(phoneChangeQueue())
.to(phoneChangeExchange())
.with(QueueConstant.PHONE_CHANGE_KEY);
}
}
application.yml
# 阿里大于短信模版
sms:
region-id: cn-hangzhou
access-key-id: LTAIkRdZIaLMBGKw
access-secret: ZPuDXJKxZ0ZNndDXlWkO8WAcZK26n7
sys-domain: dysmsapi.aliyuncs.com
version: 2017-05-25
SmsProperties
package com.xiaoge.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @Classname Sms
* @Date 2022/6/25 下午3:56
* @Created by xiaoge
* @Description TODO
*/
@Data
@ConfigurationProperties(prefix = "sms")
public class SmsProperties {
private String regionId;
private String accessKeyId;
private String accessSecret;
private String version;
}
SmsAutoConfiguration
package com.xiaoge.config;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.profile.DefaultProfile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Classname SmsAutoConfiguration
* @Date 2022/6/25 下午4:00
* @Created by xiaoge
* @Description TODO
*/
@Configuration
@EnableConfigurationProperties(value = SmsProperties.class)
public class SmsAutoConfiguration {
@Autowired
private SmsProperties smsProperties;
/**
* 初始化阿里的客户端
* 阿里的不是直接给一个url 你直接请求就可以
* 他是需要你自己写一个客户端的配置
* 你自己调用客户客户端的配置来发请求
*
* @return
*/
@Bean
public IAcsClient iAcsClient() {
DefaultProfile profile = DefaultProfile.getProfile(smsProperties.getRegionId(),
smsProperties.getAccessKeyId(),
smsProperties.getAccessSecret()
);
IAcsClient acsClient = new DefaultAcsClient(profile);
return acsClient;
}
}
SmsListener(监听器, 并且发送短信)
package com.xiaoge.listener;
import com.alibaba.fastjson.JSON;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.rabbitmq.client.Channel;
import com.xiaoge.config.SmsProperties;
import com.xiaoge.constant.QueueConstant;
import com.xiaoge.model.AliSmsModel;
import com.xiaoge.service.SmsLogService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @Classname SmsListener
* @Date 2022/6/25 下午3:52
* @Created by xiaoge
* @Description TODO
*/
@Component
@Slf4j
public class SmsListener {
@Autowired
private SmsProperties smsProperties;
@Autowired
private IAcsClient iAcsClient;
@Autowired
private SmsLogService smsLogService;
/**
* 短信监听 concurrency 线程数量3-5个
*/
@RabbitListener(queues = QueueConstant.PHONE_CHANGE_QUEUE, concurrency = "3-5")
public void smsHandler(Message message, Channel channel){
// 拿到消息
String msg = new String(message.getBody());
// 需要发送的内容
AliSmsModel aliSmsModel = JSON.parseObject(msg, AliSmsModel.class);
// 发短信了
try {
CommonResponse commonResponse = realSendMsg(aliSmsModel);
// 成功了
log.info("短信发送成功");
// 记录数据库
smsLogService.saveMsg(aliSmsModel, commonResponse);
// 签收
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} catch (Exception e) {
log.error("发送短信失败");
}
}
/**
* 发送短信
* @param aliSmsModel
* @return
*/
private CommonResponse realSendMsg(AliSmsModel aliSmsModel) throws ClientException {
CommonRequest commonRequest = new CommonRequest();
// 基本参数
commonRequest.setVersion(smsProperties.getVersion());
commonRequest.setMethod(MethodType.POST);
commonRequest.setRegionId(smsProperties.getRegionId());
commonRequest.setAction("SendSms");
commonRequest.setDomain("dysmsapi.aliyuncs.com");
// 给哪个手机号发
commonRequest.putQueryParameter("PhoneNumbers", aliSmsModel.getPhoneNumbers());
commonRequest.putQueryParameter("SignName", aliSmsModel.getSignName());
commonRequest.putQueryParameter("TemplateCode", aliSmsModel.getTemplateCode());
commonRequest.putQueryParameter("TemplateParam", aliSmsModel.getTemplateParam());
// 发请求
CommonResponse commonResponse = iAcsClient.getCommonResponse(commonRequest);
System.out.println(commonResponse.getData());
System.out.println(commonResponse.getHttpStatus());
return commonResponse;
}
}
SmsLogService
package com.xiaoge.service;
import com.aliyuncs.CommonResponse;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xiaoge.domain.SmsLog;
import com.xiaoge.model.AliSmsModel;
/**
* @Classname SmsLogService
* @Date 2022/6/25 下午3:19
* @Created by xiaoge
* @Description TODO
*/
public interface SmsLogService extends IService<SmsLog>{
/**
* 把短信信息保存进数据库
* @param aliSmsModel
* @param commonResponse
*/
void saveMsg(AliSmsModel aliSmsModel, CommonResponse commonResponse);
}
SmsLogServiceImpl(保存发送过的短信到数据库)
package com.xiaoge.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.CommonResponse;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaoge.domain.SmsLog;
import com.xiaoge.mapper.SmsLogMapper;
import com.xiaoge.model.AliSmsModel;
import com.xiaoge.service.SmsLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
/**
* @Classname SmsLogServiceImpl
* @Date 2022/6/25 下午3:19
* @Created by xiaoge
* @Description TODO
*/
@Service
public class SmsLogServiceImpl extends ServiceImpl<SmsLogMapper, SmsLog> implements SmsLogService{
@Autowired
private SmsLogMapper smsLogMapper;
/**
* 把短信信息保存进数据库
*
* @param aliSmsModel
* @param commonResponse
*/
@Override
public void saveMsg(AliSmsModel aliSmsModel, CommonResponse commonResponse) {
String phoneNumbers = aliSmsModel.getPhoneNumbers();
String templateParam = aliSmsModel.getTemplateParam();
JSONObject jsonObject = JSON.parseObject(templateParam);
String code = jsonObject.getString("code");
SmsLog smsLog = new SmsLog();
smsLog.setUserPhone(phoneNumbers);
smsLog.setContent("尊敬的用户,您的注册会员动态密码为:" + code + ",请勿泄漏于他人!");
smsLog.setMobileCode(code);
smsLog.setRecDate(new Date());
String data = commonResponse.getData();
JSONObject jsonObject1 = JSON.parseObject(data);
String code1 = jsonObject1.getString("Code");
smsLog.setResponseCode(String.valueOf(commonResponse.getHttpStatus()));
if (code1.equals("OK")) {
smsLog.setStatus(1);
} else {
smsLog.setStatus(0);
}
smsLog.setType(2);
// 插入数据库
smsLogMapper.insert(smsLog);
}
}