init
This commit is contained in:
3
sl-express-ms-sms-service/.gitignore
vendored
Normal file
3
sl-express-ms-sms-service/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.idea
|
||||
target/
|
||||
*.iml
|
||||
13
sl-express-ms-sms-service/Dockerfile
Normal file
13
sl-express-ms-sms-service/Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
||||
FROM openjdk:11-jdk
|
||||
LABEL maintainer="研究院研发组 <research@itcast.cn>"
|
||||
|
||||
# 时区修改为东八区
|
||||
ENV TZ=Asia/Shanghai
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
WORKDIR /app
|
||||
ARG JAR_FILE=target/*.jar
|
||||
ADD ${JAR_FILE} app.jar
|
||||
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["sh","-c","java -Djava.security.egd=file:/dev/./urandom -jar $JAVA_OPTS app.jar"]
|
||||
149
sl-express-ms-sms-service/README.md
Normal file
149
sl-express-ms-sms-service/README.md
Normal file
@@ -0,0 +1,149 @@
|
||||
# sl-express-ms-sms-service使用手册
|
||||
|
||||
## 1、说明
|
||||
|
||||
为了将短信相关的代码进行统一,所以将发送短信的代码抽取到sl-express-ms-sms-service
|
||||
|
||||
主要功能:
|
||||
|
||||
- 发送短信包含文字短信、语音短信,现在第三方短信通道还没有对接
|
||||
- 可以配置多个第三方短信通过实现动态切换
|
||||
- 一条短信同时可以多条发送
|
||||
|
||||
## 2、SQL脚本
|
||||
|
||||
~~~sql
|
||||
CREATE TABLE `sl_sms_record` (
|
||||
`id` bigint NOT NULL COMMENT '短信发送记录id',
|
||||
`send_channel_id` bigint NOT NULL COMMENT '发送通道id,对应sl_sms_third_channel的主键',
|
||||
`batch_id` bigint NOT NULL COMMENT '发送批次id,用于判断这些数据是同一批次发送的',
|
||||
`app_name` varchar(100) COLLATE utf8mb4_general_ci NOT NULL COMMENT '发起发送请求的微服务名称,如:sl-express-ms-work',
|
||||
`mobile` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '手机号',
|
||||
`sms_content` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '短信内容,一般为json格式的参数数据,用于填充短信模板中的占位符参数',
|
||||
`status` int NOT NULL COMMENT '发送状态,1:成功,2:失败',
|
||||
`created` datetime NOT NULL COMMENT '创建时间',
|
||||
`updated` datetime NOT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `created` (`created`) USING BTREE,
|
||||
KEY `batch_id` (`batch_id`) USING BTREE,
|
||||
KEY `mobile` (`mobile`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='短信发送记录';
|
||||
|
||||
|
||||
|
||||
CREATE TABLE `sl_sms_third_channel` (
|
||||
`id` bigint NOT NULL COMMENT '主键id',
|
||||
`sms_type` int NOT NULL COMMENT '短信类型,1:验证类型短信,2:通知类型短信',
|
||||
`content_type` int NOT NULL COMMENT '内容类型,1:文字短信,2:语音短信',
|
||||
`sms_code` int NOT NULL COMMENT '短信code,短信微服务发放的code,与sms_code是一对多的关系',
|
||||
`template_code` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '第三方平台模板code',
|
||||
`send_channel` int NOT NULL COMMENT '第三方短信平台码',
|
||||
`sign_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '签名',
|
||||
`sms_priority` int NOT NULL COMMENT '数字越大优先级越高',
|
||||
`account` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '三方平台对应的账户信息,如:accessKeyId、accessKeySecret等,以json格式存储,使用时自行解析',
|
||||
`status` int NOT NULL COMMENT '通道状态1:使用 中,2:已经停用',
|
||||
`created` datetime NOT NULL COMMENT '创建时间',
|
||||
`updated` datetime NOT NULL COMMENT '更新时间',
|
||||
`is_delete` bit(1) NOT NULL COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `created` (`created`) USING BTREE,
|
||||
KEY `sms_priority` (`sms_priority`),
|
||||
KEY `index_type` (`sms_type`,`content_type`,`sms_code`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='短信发送通道';
|
||||
~~~
|
||||
|
||||
## 3、使用
|
||||
|
||||
> 短信中心使用方法见 sl-express-ms-sms-api项目中的README.md
|
||||
|
||||
|
||||
## 4.配置
|
||||
### 4.1、通道配置(表字段介绍)
|
||||
~~~
|
||||
表sl_sms_third_channel
|
||||
通道信息:
|
||||
sms_type:短信类型,1:验证类型短信,2:通知类型短信
|
||||
content_type:内容类型,1:文字短信,2:语音短信
|
||||
sms_code:短信code,短信微服务发放的code,与sms_code是一对多的关系
|
||||
template_code 第三方平台模板code
|
||||
send_channel 第三方短信平台码
|
||||
sign_name 签名,短信中的签名
|
||||
sms_priority 数字越大优先级越高
|
||||
account 三方平台对应的账户信息,如:accessKeyId、accessKeySecret等,以json格式存储,使用时自行解析
|
||||
|
||||
~~~~
|
||||
|
||||
### 4.2、路由规则
|
||||
路由规则现在只有一个:
|
||||
根据短信类型、内容类型、短信code、状态使用中作为查询条件,通过权重排序,取优先级前五的通道,然后从这些通道中随机选择一条通道。
|
||||
路由规则后续可以进行拓展。代码展示
|
||||
|
||||
~~~java
|
||||
/**
|
||||
* 目前只根据优先级进行路由选出前五,然后随机选择渠道
|
||||
*
|
||||
* @param smsTypeEnum 短信类型
|
||||
* @param smsContentTypeEnum 内容类型
|
||||
* @param smsCode 短信code,短信微服务发放的code,与sms_code是一对多的关系
|
||||
* @return 选中的发送通道信息
|
||||
*/
|
||||
@Override
|
||||
public SmsThirdChannelEntity route(SmsTypeEnum smsTypeEnum, SmsContentTypeEnum smsContentTypeEnum, String smsCode) {
|
||||
|
||||
//取前五条记录
|
||||
LambdaQueryWrapper<SmsThirdChannelEntity> queryWrapper = Wrappers.<SmsThirdChannelEntity>lambdaQuery()
|
||||
.eq(SmsThirdChannelEntity::getSmsType, smsTypeEnum)
|
||||
.eq(SmsThirdChannelEntity::getContentType, smsContentTypeEnum)
|
||||
.eq(SmsThirdChannelEntity::getSmsCode, smsCode)
|
||||
.eq(SmsThirdChannelEntity::getStatus, 1) //使用中的状态
|
||||
.orderByDesc(SmsThirdChannelEntity::getSmsPriority).last("LIMIT 5");
|
||||
|
||||
//查询数据
|
||||
List<SmsThirdChannelEntity> smsThirdChannelEntities = super.list(queryWrapper);
|
||||
if (CollUtil.isEmpty(smsThirdChannelEntities)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//随机选择
|
||||
int index = RandomUtil.randomInt(0, CollUtil.size(smsThirdChannelEntities));
|
||||
return CollUtil.get(smsThirdChannelEntities, index);
|
||||
}
|
||||
~~~
|
||||
### 4.3、第三方通道对接
|
||||
|
||||
例如
|
||||
~~~java
|
||||
package com.sl.ms.sms.handler;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.sl.ms.sms.annotation.SendChannel;
|
||||
import com.sl.ms.sms.entity.SmsRecordEntity;
|
||||
import com.sl.ms.sms.entity.SmsThirdChannelEntity;
|
||||
import com.sl.ms.sms.enums.SendChannelEnum;
|
||||
import com.sl.ms.sms.enums.SendStatusEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@SendChannel(type = SendChannelEnum.ALI_YUN)
|
||||
public class AliYunSmsSendHandler implements SmsSendHandler {
|
||||
|
||||
@Override
|
||||
public void send(SmsThirdChannelEntity smsThirdChannelEntity, List<SmsRecordEntity> smsRecordEntities) {
|
||||
//第三方发送短信验证码
|
||||
log.info("smsRecordEntities:{}", smsRecordEntities);
|
||||
log.info("smsThirdChannelEntity:{}", smsThirdChannelEntity);
|
||||
CollUtil.forEach(smsRecordEntities, (value, index) -> {
|
||||
log.info("短信发送成功 ...");
|
||||
//设置发送成功状态
|
||||
value.setStatus(SendStatusEnum.SUCCESS);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
~~~
|
||||
|
||||
> **对接短信通道时一定要加上@SendChannel注解,并且指定type,枚举中的code与sl_sms_third_channel表中的send_channel字段值一致**
|
||||
99
sl-express-ms-sms-service/pom.xml
Normal file
99
sl-express-ms-sms-service/pom.xml
Normal file
@@ -0,0 +1,99 @@
|
||||
<?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>
|
||||
|
||||
<parent>
|
||||
<groupId>com.sl-express</groupId>
|
||||
<artifactId>sl-express-parent</artifactId>
|
||||
<version>1.4</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.sl-express.ms.sms</groupId>
|
||||
<artifactId>sl-express-ms-sms-service</artifactId>
|
||||
<version>1.1-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<sl-express-common.version>1.2-SNAPSHOT</sl-express-common.version>
|
||||
<sl-express-mq.version>1.1-SNAPSHOT</sl-express-mq.version>
|
||||
<sl-express-ms-base-domain.version>1.1-SNAPSHOT</sl-express-ms-base-domain.version>
|
||||
<sl-express-ms-sms-domain.version>1.1-SNAPSHOT</sl-express-ms-sms-domain.version>
|
||||
<sl-express-ms-carriage-api.version>1.1-SNAPSHOT</sl-express-ms-carriage-api.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.sl-express.common</groupId>
|
||||
<artifactId>sl-express-common</artifactId>
|
||||
<version>${sl-express-common.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sl-express.ms.sms</groupId>
|
||||
<artifactId>sl-express-ms-sms-domain</artifactId>
|
||||
<version>${sl-express-ms-sms-domain.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
<goal>build-info</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<!--指定主类-->
|
||||
<mainClass>com.sl.SmsApplication</mainClass>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.sl;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@Slf4j
|
||||
@SpringBootApplication
|
||||
public class SmsApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SmsApplication.class, args).getEnvironment();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.sl.ms.sms.annotation;
|
||||
|
||||
import com.sl.ms.sms.enums.SendChannelEnum;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented //标记注解
|
||||
public @interface SendChannel {
|
||||
|
||||
SendChannelEnum type();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.sl.ms.sms.controller;
|
||||
|
||||
import com.sl.ms.sms.dto.SendResultDTO;
|
||||
import com.sl.ms.sms.dto.SmsInfoDTO;
|
||||
import com.sl.ms.sms.service.SmsService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("sms")
|
||||
@Api(tags = "短信发送相关接口")
|
||||
public class SmsController {
|
||||
|
||||
@Resource
|
||||
private SmsService smsService;
|
||||
|
||||
@PostMapping("send")
|
||||
@ApiOperation("发送短信")
|
||||
public List<SendResultDTO> send(@RequestBody SmsInfoDTO smsInfoDTO) {
|
||||
return this.smsService.sendSms(smsInfoDTO);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.sl.ms.sms.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.sl.ms.sms.enums.SendStatusEnum;
|
||||
import com.sl.transport.common.entity.BaseEntity;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@TableName("sl_sms_record")
|
||||
public class SmsRecordEntity extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 发送通道id,对应sl_sms_third_channel的主键
|
||||
*/
|
||||
private Long sendChannelId;
|
||||
|
||||
/**
|
||||
* 发送批次id,用于判断这些数据是同一批次发送的
|
||||
*/
|
||||
private Long batchId;
|
||||
|
||||
/**
|
||||
* 发起发送请求的微服务名称,如:sl-express-ms-work
|
||||
*/
|
||||
private String appName;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
private String mobile;
|
||||
|
||||
/**
|
||||
* 短信内容,一般为json格式的参数数据,用于填充短信模板中的占位符参数
|
||||
*/
|
||||
private String smsContent;
|
||||
|
||||
/**
|
||||
* 发送状态,1:成功,2:失败
|
||||
*/
|
||||
private SendStatusEnum status;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.sl.ms.sms.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.sl.ms.sms.enums.SendChannelEnum;
|
||||
import com.sl.ms.sms.enums.SmsContentTypeEnum;
|
||||
import com.sl.ms.sms.enums.SmsTypeEnum;
|
||||
import com.sl.transport.common.entity.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@TableName("sl_sms_third_channel")
|
||||
public class SmsThirdChannelEntity extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 短信类型,1:文字短信,2:语音短信
|
||||
*/
|
||||
private SmsTypeEnum smsType;
|
||||
|
||||
/**
|
||||
* 内容类型,1:短信验证码,2:营销短信
|
||||
*/
|
||||
private SmsContentTypeEnum contentType;
|
||||
|
||||
/**
|
||||
* 短信code,短信微服务发放的code,与sms_code是一对多的关系
|
||||
*/
|
||||
private String smsCode;
|
||||
|
||||
/**
|
||||
* 第三方平台模板code
|
||||
*/
|
||||
private String templateCode;
|
||||
|
||||
/**
|
||||
* 第三方短信平台码
|
||||
*/
|
||||
private SendChannelEnum sendChannel;
|
||||
|
||||
/**
|
||||
* 签名
|
||||
*/
|
||||
private String signName;
|
||||
|
||||
/**
|
||||
* 优先级,数字越大优先级越高
|
||||
*/
|
||||
private Integer smsPriority;
|
||||
|
||||
/**
|
||||
* 三方平台对应的账户信息,如:accessKeyId、accessKeySecret等,以json格式存储,使用时自行解析
|
||||
*/
|
||||
private String account;
|
||||
|
||||
/**
|
||||
* 通道状态1:使用 中,2:已经停用
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 是否删除
|
||||
*/
|
||||
private Boolean isDelete;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.sl.ms.sms.enums;
|
||||
|
||||
import cn.hutool.core.util.EnumUtil;
|
||||
import com.baomidou.mybatisplus.annotation.EnumValue;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import com.sl.transport.common.enums.BaseEnum;
|
||||
|
||||
/**
|
||||
* 三方发送平台枚举
|
||||
*/
|
||||
public enum SendChannelEnum implements BaseEnum {
|
||||
|
||||
ALI_YUN(1, "阿里云短信平台,详情:https://www.aliyun.com/product/sms");
|
||||
|
||||
@EnumValue
|
||||
@JsonValue
|
||||
private Integer code;
|
||||
private String value;
|
||||
|
||||
SendChannelEnum(Integer code, String value) {
|
||||
this.code = code;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public static SendChannelEnum codeOf(Integer code) {
|
||||
return EnumUtil.getBy(SendChannelEnum::getCode, code);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.sl.ms.sms.enums;
|
||||
|
||||
import com.sl.transport.common.enums.BaseExceptionEnum;
|
||||
|
||||
/**
|
||||
* 交易枚举
|
||||
*/
|
||||
public enum SmsExceptionEnum implements BaseExceptionEnum {
|
||||
|
||||
SMS_CHANNEL_DOES_NOT_EXIST(1001, "短信通道不存在");
|
||||
|
||||
private Integer code;
|
||||
private Integer status;
|
||||
private String value;
|
||||
|
||||
SmsExceptionEnum(Integer code, String value) {
|
||||
this.code = code;
|
||||
this.value = value;
|
||||
this.status = 500;
|
||||
}
|
||||
|
||||
SmsExceptionEnum(Integer code, Integer status, String value) {
|
||||
this.code = code;
|
||||
this.value = value;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.sl.ms.sms.handler;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.sl.ms.sms.annotation.SendChannel;
|
||||
import com.sl.ms.sms.entity.SmsRecordEntity;
|
||||
import com.sl.ms.sms.entity.SmsThirdChannelEntity;
|
||||
import com.sl.ms.sms.enums.SendChannelEnum;
|
||||
import com.sl.ms.sms.enums.SendStatusEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@SendChannel(type = SendChannelEnum.ALI_YUN)
|
||||
public class AliYunSmsSendHandler implements SmsSendHandler {
|
||||
|
||||
@Override
|
||||
public void send(SmsThirdChannelEntity smsThirdChannelEntity, List<SmsRecordEntity> smsRecordEntities) {
|
||||
//第三方发送短信验证码
|
||||
log.info("smsRecordEntities:{}", smsRecordEntities);
|
||||
log.info("smsThirdChannelEntity:{}", smsThirdChannelEntity);
|
||||
CollUtil.forEach(smsRecordEntities, (value, index) -> {
|
||||
log.info("短信发送成功 ...");
|
||||
//设置发送成功状态
|
||||
value.setStatus(SendStatusEnum.SUCCESS);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.sl.ms.sms.handler;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.sl.ms.sms.annotation.SendChannel;
|
||||
import com.sl.ms.sms.enums.SendChannelEnum;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Handler工厂,用于获取指定类型的具体短信通道的实例对象
|
||||
*/
|
||||
public class HandlerFactory {
|
||||
|
||||
private HandlerFactory() {
|
||||
|
||||
}
|
||||
|
||||
public static <T> T get(SendChannelEnum sendChannelEnum, Class<T> handler) {
|
||||
Map<String, T> beans = SpringUtil.getBeansOfType(handler);
|
||||
for (Map.Entry<String, T> entry : beans.entrySet()) {
|
||||
SendChannel sendChannelAnnotation = entry.getValue().getClass().getAnnotation(SendChannel.class);
|
||||
if (ObjectUtil.isNotEmpty(sendChannelAnnotation) && ObjectUtil.equal(sendChannelEnum, sendChannelAnnotation.type())) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T> T get(String payChannel, Class<T> handler) {
|
||||
return get(SendChannelEnum.valueOf(payChannel), handler);
|
||||
}
|
||||
|
||||
public static <T> T get(Integer code, Class<T> handler) {
|
||||
return get(SendChannelEnum.codeOf(code), handler);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.sl.ms.sms.handler;
|
||||
|
||||
import com.sl.ms.sms.entity.SmsRecordEntity;
|
||||
import com.sl.ms.sms.entity.SmsThirdChannelEntity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 对接三方平台发送短信
|
||||
*/
|
||||
public interface SmsSendHandler {
|
||||
|
||||
/**
|
||||
* 发送短信,发送成功后需要修改状态为成功状态
|
||||
*
|
||||
* @param smsThirdChannelEntity 发送通道信息
|
||||
* @param smsRecordEntities 待发送列表
|
||||
*/
|
||||
void send(SmsThirdChannelEntity smsThirdChannelEntity, List<SmsRecordEntity> smsRecordEntities);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.sl.ms.sms.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.sl.ms.sms.entity.SmsRecordEntity;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 短信发送记录 Mapper 接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface SmsRecordMapper extends BaseMapper<SmsRecordEntity> {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.sl.ms.sms.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.sl.ms.sms.entity.SmsThirdChannelEntity;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 三方平台发送通道mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface SmsThirdChannelMapper extends BaseMapper<SmsThirdChannelEntity> {
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.sl.ms.sms.service;
|
||||
|
||||
import com.sl.ms.sms.entity.SmsThirdChannelEntity;
|
||||
import com.sl.ms.sms.enums.SmsContentTypeEnum;
|
||||
import com.sl.ms.sms.enums.SmsTypeEnum;
|
||||
|
||||
public interface RouteService {
|
||||
|
||||
/**
|
||||
* 目前只根据优先级进行路由选出前五,然后随机选择渠道
|
||||
*
|
||||
* @param smsTypeEnum 短信类型
|
||||
* @param smsContentTypeEnum 内容类型
|
||||
* @param smsCode 短信code,短信微服务发放的code,与sms_code是一对多的关系
|
||||
* @return 选中的发送通道信息
|
||||
*/
|
||||
SmsThirdChannelEntity route(SmsTypeEnum smsTypeEnum, SmsContentTypeEnum smsContentTypeEnum,
|
||||
String smsCode);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.sl.ms.sms.service;
|
||||
|
||||
import com.sl.ms.sms.dto.SendResultDTO;
|
||||
import com.sl.ms.sms.dto.SmsInfoDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface SmsService {
|
||||
|
||||
/**
|
||||
* 发送短信
|
||||
*
|
||||
* @param smsInfoDTO 发送短信信息
|
||||
* @return 发送记录列表
|
||||
*/
|
||||
List<SendResultDTO> sendSms(SmsInfoDTO smsInfoDTO);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.sl.ms.sms.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.sl.ms.sms.entity.SmsThirdChannelEntity;
|
||||
import com.sl.ms.sms.enums.SmsContentTypeEnum;
|
||||
import com.sl.ms.sms.enums.SmsTypeEnum;
|
||||
import com.sl.ms.sms.mapper.SmsThirdChannelMapper;
|
||||
import com.sl.ms.sms.service.RouteService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class RouteServiceImpl extends ServiceImpl<SmsThirdChannelMapper, SmsThirdChannelEntity> implements RouteService {
|
||||
|
||||
/**
|
||||
* 目前只根据优先级进行路由选出前五,然后随机选择渠道
|
||||
*
|
||||
* @param smsTypeEnum 短信类型
|
||||
* @param smsContentTypeEnum 内容类型
|
||||
* @param smsCode 短信code,短信微服务发放的code,与sms_code是一对多的关系
|
||||
* @return 选中的发送通道信息
|
||||
*/
|
||||
@Override
|
||||
public SmsThirdChannelEntity route(SmsTypeEnum smsTypeEnum, SmsContentTypeEnum smsContentTypeEnum, String smsCode) {
|
||||
|
||||
//取前五条记录
|
||||
LambdaQueryWrapper<SmsThirdChannelEntity> queryWrapper = Wrappers.<SmsThirdChannelEntity>lambdaQuery()
|
||||
.eq(SmsThirdChannelEntity::getSmsType, smsTypeEnum)
|
||||
.eq(SmsThirdChannelEntity::getContentType, smsContentTypeEnum)
|
||||
.eq(SmsThirdChannelEntity::getSmsCode, smsCode)
|
||||
.eq(SmsThirdChannelEntity::getStatus, 1) //使用中的状态
|
||||
.orderByDesc(SmsThirdChannelEntity::getSmsPriority).last("LIMIT 5");
|
||||
|
||||
//查询数据
|
||||
List<SmsThirdChannelEntity> smsThirdChannelEntities = super.list(queryWrapper);
|
||||
if (CollUtil.isEmpty(smsThirdChannelEntities)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//随机选择
|
||||
int index = RandomUtil.randomInt(0, CollUtil.size(smsThirdChannelEntities));
|
||||
return CollUtil.get(smsThirdChannelEntities, index);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.sl.ms.sms.service.impl;
|
||||
|
||||
import cn.hutool.core.stream.StreamUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.sl.ms.sms.dto.SendResultDTO;
|
||||
import com.sl.ms.sms.dto.SmsInfoDTO;
|
||||
import com.sl.ms.sms.entity.SmsRecordEntity;
|
||||
import com.sl.ms.sms.entity.SmsThirdChannelEntity;
|
||||
import com.sl.ms.sms.enums.SendStatusEnum;
|
||||
import com.sl.ms.sms.enums.SmsExceptionEnum;
|
||||
import com.sl.ms.sms.handler.HandlerFactory;
|
||||
import com.sl.ms.sms.handler.SmsSendHandler;
|
||||
import com.sl.ms.sms.mapper.SmsRecordMapper;
|
||||
import com.sl.ms.sms.service.RouteService;
|
||||
import com.sl.ms.sms.service.SmsService;
|
||||
import com.sl.transport.common.exception.SLException;
|
||||
import com.sl.transport.common.util.BeanUtil;
|
||||
import com.sl.transport.common.util.ObjectUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class SmsServiceImpl extends ServiceImpl<SmsRecordMapper, SmsRecordEntity> implements SmsService {
|
||||
|
||||
@Resource
|
||||
private RouteService routeService;
|
||||
|
||||
@Override
|
||||
public List<SendResultDTO> sendSms(SmsInfoDTO smsInfoDTO) {
|
||||
// TODO 参数校验 1.数据校验 2.接口幂等性校验
|
||||
|
||||
//路由短信发送通道
|
||||
SmsThirdChannelEntity smsThirdChannelEntity = routeService.route(smsInfoDTO.getSmsType(),
|
||||
smsInfoDTO.getContentType(), smsInfoDTO.getSmsCode());
|
||||
|
||||
if (ObjectUtil.isEmpty(smsThirdChannelEntity)) {
|
||||
throw new SLException(SmsExceptionEnum.SMS_CHANNEL_DOES_NOT_EXIST);
|
||||
}
|
||||
|
||||
//获取service
|
||||
SmsSendHandler smsSendHandler = HandlerFactory.get(smsThirdChannelEntity.getSendChannel(), SmsSendHandler.class);
|
||||
if (ObjectUtil.isEmpty(smsSendHandler)) {
|
||||
throw new SLException(SmsExceptionEnum.SMS_CHANNEL_DOES_NOT_EXIST);
|
||||
}
|
||||
|
||||
//生成批次id
|
||||
long batchId = IdWorker.getId();
|
||||
|
||||
//封装数据
|
||||
List<SmsRecordEntity> smsRecordEntities = StreamUtil.of(smsInfoDTO.getMobiles())
|
||||
.map(mobile -> SmsRecordEntity.builder()
|
||||
.batchId(batchId) //批次id
|
||||
.appName(smsInfoDTO.getAppName()) //微服务名称
|
||||
.smsContent(smsInfoDTO.getSmsContent()) //短信内容
|
||||
.sendChannelId(smsThirdChannelEntity.getId()) //发送渠道id
|
||||
.mobile(mobile) //手机号
|
||||
.status(SendStatusEnum.FAIL) //默认失败状态
|
||||
.build())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
//发送短信
|
||||
smsSendHandler.send(smsThirdChannelEntity, smsRecordEntities);
|
||||
|
||||
//入库
|
||||
super.saveBatch(smsRecordEntities);
|
||||
|
||||
return BeanUtil.copyToList(smsRecordEntities, SendResultDTO.class);
|
||||
}
|
||||
|
||||
}
|
||||
7
sl-express-ms-sms-service/src/main/resources/banner.txt
Normal file
7
sl-express-ms-sms-service/src/main/resources/banner.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
_ ${spring.application.name} ${application.version}
|
||||
___ | | ___ __ __ _ __ _ __ ___ ___ ___ Port: ${server.port}
|
||||
/ __|| | _____ / _ \\ \/ /| '_ \ | '__|/ _ \/ __|/ __| Pid: ${pid} Profile(s): ${AnsiColor.GREEN}${spring.profiles.active}${AnsiColor.DEFAULT}
|
||||
\__ \| ||_____|| __/ > < | |_) || | | __/\__ \\__ \
|
||||
|___/|_| \___|/_/\_\| .__/ |_| \___||___/|___/ https://sl-express.itheima.net/
|
||||
|_|
|
||||
@@ -0,0 +1,28 @@
|
||||
server:
|
||||
port: 18095
|
||||
tomcat:
|
||||
uri-encoding: UTF-8
|
||||
threads:
|
||||
max: 1000
|
||||
min-spare: 30
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
username: nacos
|
||||
password: nacos
|
||||
server-addr: 192.168.150.101:8848
|
||||
discovery:
|
||||
namespace: ecae68ba-7b43-4473-a980-4ddeb6157bdc
|
||||
ip: 192.168.150.1
|
||||
config:
|
||||
namespace: ecae68ba-7b43-4473-a980-4ddeb6157bdc
|
||||
shared-configs: #共享配置
|
||||
- data-id: shared-spring-mysql.yml
|
||||
group: SHARED_GROUP
|
||||
refresh: false
|
||||
- data-id: shared-spring-mybatis-plus.yml
|
||||
group: SHARED_GROUP
|
||||
refresh: false
|
||||
- data-id: shared-spring-rabbitmq.yml
|
||||
group: SHARED_GROUP
|
||||
refresh: false
|
||||
@@ -0,0 +1,27 @@
|
||||
server:
|
||||
port: 18095
|
||||
tomcat:
|
||||
uri-encoding: UTF-8
|
||||
threads:
|
||||
max: 1000
|
||||
min-spare: 30
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
username: nacos
|
||||
password: vO5/dZ9,iL
|
||||
server-addr: nacos-service.yjy-public-slwl-java-prod.svc.cluster.local:8848
|
||||
discovery:
|
||||
namespace: 92312ba8-1119-440f-81af-c29618df303b
|
||||
config:
|
||||
namespace: 92312ba8-1119-440f-81af-c29618df303b
|
||||
shared-configs: #共享配置
|
||||
- data-id: shared-spring-mysql.yml
|
||||
group: SHARED_GROUP
|
||||
refresh: false
|
||||
- data-id: shared-spring-mybatis-plus.yml
|
||||
group: SHARED_GROUP
|
||||
refresh: false
|
||||
- data-id: shared-spring-rabbitmq.yml
|
||||
group: SHARED_GROUP
|
||||
refresh: false
|
||||
@@ -0,0 +1,27 @@
|
||||
server:
|
||||
port: 18095
|
||||
tomcat:
|
||||
uri-encoding: UTF-8
|
||||
threads:
|
||||
max: 1000
|
||||
min-spare: 30
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
username: nacos
|
||||
password: nacos
|
||||
server-addr: 192.168.150.101:8848
|
||||
discovery:
|
||||
namespace: ecae68ba-7b43-4473-a980-4ddeb6157bdc
|
||||
config:
|
||||
namespace: ecae68ba-7b43-4473-a980-4ddeb6157bdc
|
||||
shared-configs: #共享配置
|
||||
- data-id: shared-spring-mysql.yml
|
||||
group: SHARED_GROUP
|
||||
refresh: false
|
||||
- data-id: shared-spring-mybatis-plus.yml
|
||||
group: SHARED_GROUP
|
||||
refresh: false
|
||||
- data-id: shared-spring-rabbitmq.yml
|
||||
group: SHARED_GROUP
|
||||
refresh: false
|
||||
@@ -0,0 +1,27 @@
|
||||
server:
|
||||
port: 18095
|
||||
tomcat:
|
||||
uri-encoding: UTF-8
|
||||
threads:
|
||||
max: 1000
|
||||
min-spare: 30
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
username: nacos
|
||||
password: nacos
|
||||
server-addr: nacos-service.yjy-public-slwl-java.svc.cluster.local:8848
|
||||
discovery:
|
||||
namespace: 92312ba8-1119-440f-81af-c29618df303b
|
||||
config:
|
||||
namespace: 92312ba8-1119-440f-81af-c29618df303b
|
||||
shared-configs: #共享配置
|
||||
- data-id: shared-spring-mysql.yml
|
||||
group: SHARED_GROUP
|
||||
refresh: false
|
||||
- data-id: shared-spring-mybatis-plus.yml
|
||||
group: SHARED_GROUP
|
||||
refresh: false
|
||||
- data-id: shared-spring-rabbitmq.yml
|
||||
group: SHARED_GROUP
|
||||
refresh: false
|
||||
23
sl-express-ms-sms-service/src/main/resources/bootstrap.yml
Normal file
23
sl-express-ms-sms-service/src/main/resources/bootstrap.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
application:
|
||||
version: v1.0
|
||||
logging:
|
||||
config: classpath:logback-spring.xml
|
||||
spring:
|
||||
application:
|
||||
name: sl-express-ms-sms
|
||||
profiles:
|
||||
active: local
|
||||
mvc:
|
||||
pathmatch:
|
||||
#解决异常:swagger Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
|
||||
#因为Springfox使用的路径匹配是基于AntPathMatcher的,而Spring Boot 2.6.X使用的是PathPatternMatcher
|
||||
matching-strategy: ant_path_matcher
|
||||
sl:
|
||||
swagger:
|
||||
package-path: com.sl.ms.sms.controller
|
||||
title: 神领物流 - 短信微服务接口文档
|
||||
description: 该微服务完成短信、验证码发送。
|
||||
contact-name: 传智教育·研究院
|
||||
contact-url: http://www.itcast.cn/
|
||||
contact-email: yjy@itcast.cn
|
||||
version: ${application.version}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。-->
|
||||
<!--scanPeriod: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。-->
|
||||
<!--debug: 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。-->
|
||||
<configuration debug="false" scan="false" scanPeriod="60 seconds">
|
||||
<springProperty scope="context" name="appName" source="spring.application.name"/>
|
||||
<!--文件名-->
|
||||
<property name="logback.appname" value="${appName}"/>
|
||||
<!--文件位置-->
|
||||
<property name="logback.logdir" value="/data/logs"/>
|
||||
|
||||
<!-- 定义控制台输出 -->
|
||||
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<layout class="ch.qos.logback.classic.PatternLayout">
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} - [%thread] - %-5level - %logger{50} - %msg%n</pattern>
|
||||
</layout>
|
||||
</appender>
|
||||
|
||||
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>DEBUG</level>
|
||||
</filter>
|
||||
<File>${logback.logdir}/${logback.appname}/${logback.appname}.log</File>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<FileNamePattern>${logback.logdir}/${logback.appname}/${logback.appname}.%d{yyyy-MM-dd}.log.zip</FileNamePattern>
|
||||
<maxHistory>90</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<charset>UTF-8</charset>
|
||||
<pattern>%d [%thread] %-5level %logger{36} %line - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
|
||||
<!--evel:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,-->
|
||||
<!--不能设置为INHERITED或者同义词NULL。默认是DEBUG。-->
|
||||
<root level="INFO">
|
||||
<appender-ref ref="stdout"/>
|
||||
</root>
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user