This commit is contained in:
shuhongfan
2023-09-04 16:40:17 +08:00
commit cf5ac25c14
8267 changed files with 1305066 additions and 0 deletions

3
sl-express-ms-sms-service/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.idea
target/
*.iml

View 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"]

View 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字段值一致**

View 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>

View File

@@ -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();
}
}

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
});
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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> {
}

View File

@@ -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> {
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View 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/
|_|

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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}

View File

@@ -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>