临时提交

This commit is contained in:
zengzefeng 2021-02-23 15:33:05 +08:00
parent a50db6bf7f
commit b4be8e987a
29 changed files with 418 additions and 263 deletions

View File

@ -1,6 +1,11 @@
package cn.iocoder.dashboard.framework.sms.client; package cn.iocoder.dashboard.framework.sms.client;
import cn.iocoder.dashboard.framework.sms.core.SmsBody;
import cn.iocoder.dashboard.framework.sms.core.SmsResult;
import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty;
import lombok.extern.slf4j.Slf4j;
import java.util.Collection;
/** /**
* 抽象短息客户端 * 抽象短息客户端
@ -8,7 +13,8 @@ import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty;
* @author zzf * @author zzf
* @date 2021/2/1 9:28 * @date 2021/2/1 9:28
*/ */
public abstract class AbstractSmsClient<R> implements SmsClient<R> { @Slf4j
public abstract class AbstractSmsClient implements SmsClient {
/** /**
* 短信渠道参数 * 短信渠道参数
@ -29,4 +35,43 @@ public abstract class AbstractSmsClient<R> implements SmsClient<R> {
return channelVO; return channelVO;
} }
@Override
public SmsResult send(String templateApiId, SmsBody smsBody, Collection<String> targets) {
SmsResult result;
try {
beforeSend(templateApiId, smsBody, targets);
result = doSend(templateApiId, smsBody, targets);
afterSend(templateApiId, smsBody, targets, result);
} catch (Exception e) {
// exception handle
log.debug(e.getMessage(), e);
return failResult("发送异常: " + e.getMessage());
}
return result;
}
/**
* 发送消息
*
* @param templateApiId 短信模板唯一标识
* @param smsBody 消息内容
* @param targets 发送对象列表
* @return 短信发送结果
*/
public abstract SmsResult doSend(String templateApiId, SmsBody smsBody, Collection<String> targets) throws Exception;
protected void beforeSend(String templateApiId, SmsBody smsBody, Collection<String> targets) throws Exception {
}
protected void afterSend(String templateApiId, SmsBody smsBody, Collection<String> targets, SmsResult result) throws Exception {
}
SmsResult failResult(String message) {
SmsResult resultBody = new SmsResult();
resultBody.setSuccess(false);
resultBody.setMessage(message);
return resultBody;
}
} }

View File

@ -1,10 +1,14 @@
package cn.iocoder.dashboard.framework.sms.client; package cn.iocoder.dashboard.framework.sms.client;
import cn.hutool.core.date.DateUtil;
import cn.iocoder.dashboard.framework.sms.core.SmsBody; import cn.iocoder.dashboard.framework.sms.core.SmsBody;
import cn.iocoder.dashboard.framework.sms.core.SmsResult; import cn.iocoder.dashboard.framework.sms.core.SmsResult;
import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail;
import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty;
import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient; import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse; import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.http.MethodType; import com.aliyuncs.http.MethodType;
@ -13,7 +17,9 @@ import com.aliyuncs.profile.IClientProfile;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List;
/** /**
* 阿里短信实现类 * 阿里短信实现类
@ -22,7 +28,7 @@ import java.util.Collection;
* @date 2021/1/25 14:17 * @date 2021/1/25 14:17
*/ */
@Slf4j @Slf4j
public class AliyunSmsClient extends AbstractSmsClient<SendSmsResponse> { public class AliyunSmsClient extends AbstractSmsClient {
private static final String OK = "OK"; private static final String OK = "OK";
@ -53,35 +59,36 @@ public class AliyunSmsClient extends AbstractSmsClient<SendSmsResponse> {
@Override @Override
public SmsResult<SendSmsResponse> send(SmsBody smsBody, Collection<String> targets) { public SmsResult doSend(String templateApiId, SmsBody smsBody, Collection<String> targets) throws Exception {
SendSmsRequest request = new SendSmsRequest(); SendSmsRequest request = new SendSmsRequest();
request.setSysMethod(MethodType.POST); request.setSysMethod(MethodType.POST);
request.setPhoneNumbers(StringUtils.join(targets, ",")); request.setPhoneNumbers(StringUtils.join(targets, ","));
request.setSignName(channelVO.getApiSignatureId()); request.setSignName(channelVO.getApiSignatureId());
request.setTemplateCode(channelVO.getTemplateByTemplateCode(smsBody.getTemplateCode()).getApiTemplateId()); request.setTemplateCode(templateApiId);
request.setTemplateParam(smsBody.getParamsStr()); request.setTemplateParam(smsBody.getParamsStr());
// TODO FROM 芋艿 TO zzftry catch 咱是不是可以交给 abstract 来做这样异常处理重试限流等等都可以酱紫 // TODO FROM 芋艿 TO zzftry catch 咱是不是可以交给 abstract 来做这样异常处理重试限流等等都可以酱紫 DONE
try { SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
boolean result = OK.equals(sendSmsResponse.getCode()); boolean result = OK.equals(sendSmsResponse.getCode());
if (!result) { if (!result) {
log.debug("send fail[code={}, message={}]", sendSmsResponse.getCode(), sendSmsResponse.getMessage()); log.debug("send fail[code={}, message={}]", sendSmsResponse.getCode(), sendSmsResponse.getMessage());
}
SmsResult<SendSmsResponse> resultBody = new SmsResult<>();
resultBody.setSuccess(result);
resultBody.setResult(sendSmsResponse);
return resultBody;
} catch (Exception e) {
log.debug(e.getMessage(), e);
return failResult("发送异常: " + e.getMessage());
} }
} SmsResult resultBody = new SmsResult();
resultBody.setSuccess(result);
QuerySendDetailsRequest querySendDetailsRequest = new QuerySendDetailsRequest();
querySendDetailsRequest.setBizId(sendSmsResponse.getBizId());
SmsResult<SendSmsResponse> failResult(String message) { QuerySendDetailsResponse acsResponse = acsClient.getAcsResponse(querySendDetailsRequest);
SmsResult<SendSmsResponse> resultBody = new SmsResult<>(); List<SmsResultDetail> resultDetailList = new ArrayList<>(Integer.parseInt(acsResponse.getTotalCount()));
resultBody.setSuccess(false); acsResponse.getSmsSendDetailDTOs().forEach(s -> {
resultBody.setMessage(message); SmsResultDetail resultDetail = new SmsResultDetail();
resultDetail.setCreateTime(DateUtil.parseDateTime(s.getSendDate()));
resultDetail.setMessage(s.getContent());
resultDetail.setPhone(s.getPhoneNum());
resultDetail.setStatus(Math.toIntExact(s.getSendStatus()));
resultDetailList.add(resultDetail);
});
resultBody.setResult(resultDetailList);
return resultBody; return resultBody;
} }

View File

@ -11,15 +11,16 @@ import java.util.Collection;
* @author zzf * @author zzf
* @date 2021/1/25 14:14 * @date 2021/1/25 14:14
*/ */
public interface SmsClient<R> { public interface SmsClient {
/** /**
* 发送消息 * 发送消息
* *
* @param smsBody 消息内容 * @param templateApiId 短信模板唯一标识
* @param targets 发送对象列表 * @param smsBody 消息内容
* @return 是否发送成功 * @param targets 发送对象列表
* @return 短信发送结果
*/ */
SmsResult<R> send(SmsBody smsBody, Collection<String> targets); SmsResult send(String templateApiId, SmsBody smsBody, Collection<String> targets);
} }

View File

@ -1,15 +1,14 @@
package cn.iocoder.dashboard.framework.sms.core; package cn.iocoder.dashboard.framework.sms.core;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.dashboard.common.enums.SmsChannelEnum;
import cn.iocoder.dashboard.common.exception.ServiceException; import cn.iocoder.dashboard.common.exception.ServiceException;
import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
import cn.iocoder.dashboard.framework.sms.client.AliyunSmsClient; import cn.iocoder.dashboard.framework.sms.client.AliyunSmsClient;
import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;
import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty;
import cn.iocoder.dashboard.framework.sms.core.property.SmsTemplateProperty;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -24,7 +23,17 @@ import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
@Component @Component
public class SmsClientFactory { public class SmsClientFactory {
private final Map<Long, AbstractSmsClient<?>> smsSenderMap = new ConcurrentHashMap<>(8); /**
* channelId: client map
* 保存 渠道id: 对应短信客户端 的map
*/
private final Map<Long, AbstractSmsClient> smsSenderMap = new ConcurrentHashMap<>(8);
/**
* templateCode: TemplateProperty map
* 保存 模板编码模板信息 的map
*/
private final Map<String, SmsTemplateProperty> templatePropertyMap = new ConcurrentHashMap<>(16);
/** /**
* 创建短信客户端 * 创建短信客户端
@ -33,20 +42,13 @@ public class SmsClientFactory {
* @return 客户端id(默认channelId) * @return 客户端id(默认channelId)
*/ */
public Long createClient(SmsChannelProperty propertyVO) { public Long createClient(SmsChannelProperty propertyVO) {
// TODO FROM 芋艿 TO zzf参数的校验可以考虑统一使用 validation // TODO FROM 芋艿 TO zzf参数的校验可以考虑统一使用 validation DONE
if (StrUtil.isBlank(propertyVO.getCode())) { AbstractSmsClient sender = createClient(SmsChannelEnum.getByCode(propertyVO.getCode()), propertyVO);
throw ServiceExceptionUtil.exception(PARAM_VALUE_IS_NULL, "短信渠道编码");
}
if (ObjectUtil.isNull(propertyVO.getId())) {
throw ServiceExceptionUtil.exception(PARAM_VALUE_IS_NULL, "短信渠道ID");
}
AbstractSmsClient<?> sender = createClient(SmsChannelEnum.getByCode(propertyVO.getCode()), propertyVO);
smsSenderMap.put(propertyVO.getId(), sender); smsSenderMap.put(propertyVO.getId(), sender);
return propertyVO.getId(); return propertyVO.getId();
} }
private AbstractSmsClient<?> createClient(SmsChannelEnum channelEnum, SmsChannelProperty channelVO) { private AbstractSmsClient createClient(SmsChannelEnum channelEnum, SmsChannelProperty channelVO) {
if (channelEnum == null) { if (channelEnum == null) {
throw new ServiceException(INVALID_CHANNEL_CODE); throw new ServiceException(INVALID_CHANNEL_CODE);
} }
@ -66,7 +68,38 @@ public class SmsClientFactory {
* @param channelId 渠道id * @param channelId 渠道id
* @return 短信id * @return 短信id
*/ */
public AbstractSmsClient<?> getClient(Long channelId) { public AbstractSmsClient getClient(Long channelId) {
return smsSenderMap.get(channelId); return smsSenderMap.get(channelId);
} }
/**
* 添加或修改短信模板信息缓存
*/
public void addOrUpdateTemplateCache(Collection<SmsTemplateProperty> templateProperties) {
templateProperties.forEach(s -> templatePropertyMap.put(s.getCode(), s));
}
/**
* 添加或修改短信模板信息缓存
*/
public void addOrUpdateTemplateCache(SmsTemplateProperty templateProperty) {
templatePropertyMap.put(templateProperty.getCode(), templateProperty);
}
/**
* 根据短信模板编码获取模板唯一标识
*
* @param templateCode 短信模板编码
* @return 短信id
*/
public String getTemplateApiIdByCode(String templateCode) {
SmsTemplateProperty smsTemplateProperty = templatePropertyMap.get(templateCode);
if (smsTemplateProperty == null) {
throw new ServiceException(SMS_TEMPLATE_NOT_FOUND);
}
return smsTemplateProperty.getApiTemplateId();
}
} }

View File

@ -3,17 +3,24 @@ package cn.iocoder.dashboard.framework.sms.core;
import lombok.Data; import lombok.Data;
import java.io.Serializable; import java.io.Serializable;
import java.util.List;
/** /**
* 消息内容实体类 * 消息内容实体类
*/ */
@Data @Data
public class SmsResult<T> implements Serializable { public class SmsResult implements Serializable {
/** /**
* 是否成功 * 是否成功
*/ */
private Boolean success; // TODO FROM 芋艿 to zzf未来要加一个 code将不同平台的短信失败的情况做一次统一的收敛 // TODO FROM 芋艿 to zzf未来要加一个 code将不同平台的短信失败的情况做一次统一的收敛 DONE
private Boolean success;
/**
* 状态码
*/
private String code;
/** /**
* 提示 * 提示
@ -23,5 +30,7 @@ public class SmsResult<T> implements Serializable {
/** /**
* 返回值 * 返回值
*/ */
private T result; // TODO FROM 芋艿 to zzf是不是统一各个平台的返回结果这样对调用方来说统一因为作为统一的短信客户端最好让上层不太需要知道太具体黑河诶 // TODO FROM 芋艿 to zzf是不是统一各个平台的返回结果这样对调用方来说统一因为作为统一的短信客户端最好让上层不太需要知道太具体黑河诶 DONE
private List<SmsResultDetail> result;
} }

View File

@ -0,0 +1,33 @@
package cn.iocoder.dashboard.framework.sms.core;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 消息内容实体类
*/
@Data
public class SmsResultDetail implements Serializable {
/**
* 状态 1成功 2失败 3等待回执
*/
private Integer status;
/**
* 接收手机号
*/
private String phone;
/**
* 提示
*/
private String message;
/**
* 时间
*/
private Date createTime;
}

View File

@ -1,13 +1,10 @@
package cn.iocoder.dashboard.common.enums; package cn.iocoder.dashboard.framework.sms.core.enums;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import java.util.function.Predicate;
import java.util.stream.Stream;
/** /**
* 短信渠道枚举 TODO FROM 芋艿 TO zzf属于短信的枚举类可以放到 framework/sms * 短信渠道枚举 TODO FROM 芋艿 TO zzf属于短信的枚举类可以放到 framework/sms DONE
* *
* @author zzf * @author zzf
* @date 2021/1/25 10:56 * @date 2021/1/25 10:56
@ -19,7 +16,7 @@ public enum SmsChannelEnum {
ALI("ALI", "阿里"), ALI("ALI", "阿里"),
HUA_WEI("HUA_WEI", "华为"), HUA_WEI("HUA_WEI", "华为"),
QI_NIU("QI_NIU", "七牛"), QI_NIU("QI_NIU", "七牛"),
TEN_XUN("TEN_XUN", "腾讯"); // TODO FROM 芋艿 to zzfTEN 有后鼻音哈要被马爸爸打了 TENCENT("TENCENT", "腾讯"); // TODO FROM 芋艿 to zzfTEN 有后鼻音哈要被马爸爸打了 DONE
private final String code; private final String code;

View File

@ -3,6 +3,8 @@ package cn.iocoder.dashboard.framework.sms.core.property;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
@ -19,40 +21,37 @@ public class SmsChannelProperty implements Serializable {
/** /**
* id * id
*/ */
@NotNull(message = "短信渠道ID不能为空")
private Long id; private Long id;
/** /**
* 编码(来自枚举类 阿里华为七牛等) * 编码(来自枚举类 阿里华为七牛等)
*/ */
@NotEmpty(message = "短信渠道编码不能为空")
private String code; private String code;
/** /**
* 渠道账号id * 渠道账号id
*/ */
@NotEmpty(message = "渠道账号id不能为空")
private String apiKey; private String apiKey;
/** /**
* 渠道账号秘钥 * 渠道账号秘钥
*/ */
@NotEmpty(message = "渠道账号秘钥不能为空")
private String apiSecret; private String apiSecret;
/** /**
* 实际渠道签名唯一标识 * 实际渠道签名唯一标识
*/ */
@NotEmpty(message = "实际渠道签名唯一标识不能为空")
private String apiSignatureId; private String apiSignatureId;
/** /**
* 签名值 * 签名值
*/ */
@NotEmpty(message = "签名值不能为空")
private String signature; private String signature;
/**
* 该渠道名下的短信模板集合
*/
private List<SmsTemplateProperty> templateList;
public SmsTemplateProperty getTemplateByTemplateCode(String tempCode) {
return templateList.stream().filter(s -> s.getCode().equals(tempCode)).findFirst().get();
}
} }

View File

@ -3,8 +3,12 @@ package cn.iocoder.dashboard.framework.sms.core.property;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotEmpty;
/** /**
* 渠道模板VO类 TODO FROM 芋艿 TO zzf模板是不是不要提供到 client 里面而是交给 factory 统一维护就好不然模板修改时候刷新 client 会比较麻烦 * 渠道模板VO类
* TODO FROM 芋艿 TO zzf模板是不是不要提供到 client 里面而是交给 factory 统一维护就好不然模板修改时候刷新 client 会比较麻烦
*
* *
* @author zzf * @author zzf
* @date 2021/1/25 17:03 * @date 2021/1/25 17:03
@ -13,6 +17,12 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode @EqualsAndHashCode
public class SmsTemplateProperty { public class SmsTemplateProperty {
/**
* 渠道id
*/
@NotEmpty(message = "短信渠道编码不能为空")
private Long channelId;
/** /**
* 业务编码(来自数据字典, 用户自定义业务场景 一个场景可以有多个模板) * 业务编码(来自数据字典, 用户自定义业务场景 一个场景可以有多个模板)
*/ */
@ -21,18 +31,19 @@ public class SmsTemplateProperty {
/** /**
* 编码 * 编码
*/ */
@NotEmpty(message = "短信模板编码不能为空")
private String code; private String code;
/** /**
* 实际渠道模板唯一标识 * 实际渠道模板唯一标识
*/ */
@NotEmpty(message = "短信模板唯一标识不能为空")
private String apiTemplateId; private String apiTemplateId;
/** /**
* 内容 * 内容
*/ */
@NotEmpty(message = "短信模板内容不能为空")
private String content; private String content;
} }

View File

@ -5,8 +5,8 @@ import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO; import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO; import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO; import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsChannelDO; import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsChannelDO;
import cn.iocoder.dashboard.modules.system.service.sms.SmsChannelService; import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -23,25 +23,25 @@ import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
public class SmsChannelController { public class SmsChannelController {
@Resource @Resource
private SmsChannelService service; private SysSmsChannelService service;
@ApiOperation("获取渠道/签名分页") @ApiOperation("获取渠道/签名分页")
@GetMapping("/page") @GetMapping("/page")
public CommonResult<PageResult<SmsChannelDO>> getPermissionInfo(@Validated SmsChannelPageReqVO reqVO) { public CommonResult<PageResult<SysSmsChannelDO>> getPermissionInfo(@Validated SmsChannelPageReqVO reqVO) {
return success(service.pageChannels(reqVO)); return success(service.pageSmsChannels(reqVO));
} }
@ApiOperation("获取渠道枚举") @ApiOperation("获取渠道枚举")
@GetMapping("/list/channel-enum") @GetMapping("/list/channel-enum")
public CommonResult<List<SmsChannelEnumRespVO>> getChannelEnums() { public CommonResult<List<SmsChannelEnumRespVO>> getChannelEnums() {
return success(service.getChannelEnums()); return success(service.getSmsChannelEnums());
} }
@ApiOperation("添加消息渠道") @ApiOperation("添加消息渠道")
@PostMapping("/create") @PostMapping("/create")
public CommonResult<Long> add(@Validated @RequestBody SmsChannelCreateReqVO reqVO) { public CommonResult<Long> add(@Validated @RequestBody SmsChannelCreateReqVO reqVO) {
return success(service.createChannel(reqVO)); return success(service.createSmsChannel(reqVO));
} }

View File

@ -1,13 +1,13 @@
package cn.iocoder.dashboard.modules.system.convert.sms; package cn.iocoder.dashboard.modules.system.convert.sms;
import cn.iocoder.dashboard.common.enums.SmsChannelEnum; import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;
import cn.iocoder.dashboard.common.pojo.PageResult; import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO; import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO; import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO; import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserUpdateReqVO; import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserUpdateReqVO;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsChannelDO; import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsChannelDO;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mapping; import org.mapstruct.Mapping;
@ -21,17 +21,19 @@ public interface SmsChannelConvert {
SmsChannelConvert INSTANCE = Mappers.getMapper(SmsChannelConvert.class); SmsChannelConvert INSTANCE = Mappers.getMapper(SmsChannelConvert.class);
@Mapping(source = "records", target = "list") @Mapping(source = "records", target = "list")
PageResult<SmsChannelDO> convertPage(IPage<SmsChannelDO> page); PageResult<SysSmsChannelDO> convertPage(IPage<SysSmsChannelDO> page);
SmsChannelDO convert(SmsChannelCreateReqVO bean); SysSmsChannelDO convert(SmsChannelCreateReqVO bean);
SmsChannelDO convert(SysUserUpdateReqVO bean); SysSmsChannelDO convert(SysUserUpdateReqVO bean);
List<SmsChannelEnumRespVO> convertEnum(List<SmsChannelEnum> bean); List<SmsChannelEnumRespVO> convertEnum(List<SmsChannelEnum> bean);
List<SmsChannelAllVO> convert(List<SmsChannelDO> bean); List<SmsChannelAllVO> convert(List<SysSmsChannelDO> bean);
List<SmsChannelProperty> convertProperty(List<SmsChannelAllVO> list); List<SmsChannelProperty> convertProperty(List<SmsChannelAllVO> list);
List<SmsChannelProperty> convertProperties(List<SysSmsChannelDO> list);
} }

View File

@ -2,8 +2,8 @@ package cn.iocoder.dashboard.modules.system.convert.sms;
import cn.iocoder.dashboard.common.pojo.PageResult; import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsTemplateVO; import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsTemplateVO;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsChannelDO; import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsChannelDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsTemplateDO; import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsTemplateDO;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mapping; import org.mapstruct.Mapping;
@ -17,10 +17,10 @@ public interface SmsTemplateConvert {
SmsTemplateConvert INSTANCE = Mappers.getMapper(SmsTemplateConvert.class); SmsTemplateConvert INSTANCE = Mappers.getMapper(SmsTemplateConvert.class);
@Mapping(source = "records", target = "list") @Mapping(source = "records", target = "list")
PageResult<SmsChannelDO> convertPage(IPage<SmsChannelDO> page); PageResult<SysSmsChannelDO> convertPage(IPage<SysSmsChannelDO> page);
List<SmsTemplateVO> convert(List<SmsTemplateDO> bean); List<SmsTemplateVO> convert(List<SysSmsTemplateDO> bean);
SmsTemplateVO convert(SmsTemplateDO bean); SmsTemplateVO convert(SysSmsTemplateDO bean);
} }

View File

@ -1,39 +0,0 @@
package cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms;
import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsTemplateDO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface SmsTemplateMapper extends BaseMapper<SmsTemplateDO> {
/**
* 根据短信渠道id查询短信模板集合
*
* @param channelId 渠道id
* @return 模板集合
*/
default List<SmsTemplateDO> selectListByChannelId(Long channelId) {
return selectList(new LambdaQueryWrapper<SmsTemplateDO>()
.eq(SmsTemplateDO::getChannelId, channelId)
.eq(SmsTemplateDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
.orderByAsc(SmsTemplateDO::getId)
);
}
/**
* 查询有效短信模板集合
*
* @return 有效短信模板集合
*/
default List<SmsTemplateDO> selectEnabledList() {
return selectList(new LambdaQueryWrapper<SmsTemplateDO>()
.eq(SmsTemplateDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
.orderByAsc(SmsTemplateDO::getId)
);
}
}

View File

@ -4,7 +4,7 @@ import cn.hutool.core.util.StrUtil;
import cn.iocoder.dashboard.common.enums.CommonStatusEnum; import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.framework.mybatis.core.util.MyBatisUtils; import cn.iocoder.dashboard.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO; import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsChannelDO; import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsChannelDO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
@ -13,19 +13,19 @@ import org.apache.ibatis.annotations.Mapper;
import java.util.List; import java.util.List;
@Mapper @Mapper
public interface SmsChannelMapper extends BaseMapper<SmsChannelDO> { public interface SysSmsChannelMapper extends BaseMapper<SysSmsChannelDO> {
default IPage<SmsChannelDO> selectChannelPage(SmsChannelPageReqVO reqVO) { default IPage<SysSmsChannelDO> selectChannelPage(SmsChannelPageReqVO reqVO) {
return selectPage(MyBatisUtils.buildPage(reqVO), new LambdaQueryWrapper<SmsChannelDO>() return selectPage(MyBatisUtils.buildPage(reqVO), new LambdaQueryWrapper<SysSmsChannelDO>()
.like(StrUtil.isNotBlank(reqVO.getName()), SmsChannelDO::getName, reqVO.getName()) .like(StrUtil.isNotBlank(reqVO.getName()), SysSmsChannelDO::getName, reqVO.getName())
.like(StrUtil.isNotBlank(reqVO.getSignature()), SmsChannelDO::getName, reqVO.getSignature()) .like(StrUtil.isNotBlank(reqVO.getSignature()), SysSmsChannelDO::getName, reqVO.getSignature())
); );
} }
default List<SmsChannelDO> selectEnabledList() { default List<SysSmsChannelDO> selectEnabledList() {
return selectList(new LambdaQueryWrapper<SmsChannelDO>() return selectList(new LambdaQueryWrapper<SysSmsChannelDO>()
.eq(SmsChannelDO::getStatus, CommonStatusEnum.ENABLE.getStatus()) .eq(SysSmsChannelDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
.orderByAsc(SmsChannelDO::getId) .orderByAsc(SysSmsChannelDO::getId)
); );
} }
} }

View File

@ -1,10 +1,10 @@
package cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms; package cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsLogDO; import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsLogDO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
@Mapper @Mapper
public interface SmsLogMapper extends BaseMapper<SmsLogDO> { public interface SysSmsLogMapper extends BaseMapper<SysSmsLogDO> {
} }

View File

@ -0,0 +1,39 @@
package cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms;
import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsTemplateDO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface SysSmsTemplateMapper extends BaseMapper<SysSmsTemplateDO> {
/**
* 根据短信渠道id查询短信模板集合
*
* @param channelId 渠道id
* @return 模板集合
*/
default List<SysSmsTemplateDO> selectListByChannelId(Long channelId) {
return selectList(new LambdaQueryWrapper<SysSmsTemplateDO>()
.eq(SysSmsTemplateDO::getChannelId, channelId)
.eq(SysSmsTemplateDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
.orderByAsc(SysSmsTemplateDO::getId)
);
}
/**
* 查询有效短信模板集合
*
* @return 有效短信模板集合
*/
default List<SysSmsTemplateDO> selectEnabledList() {
return selectList(new LambdaQueryWrapper<SysSmsTemplateDO>()
.eq(SysSmsTemplateDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
.orderByAsc(SysSmsTemplateDO::getId)
);
}
}

View File

@ -15,7 +15,7 @@ import lombok.EqualsAndHashCode;
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@TableName(value = "sms_channel", autoResultMap = true) @TableName(value = "sms_channel", autoResultMap = true)
public class SmsChannelDO extends BaseDO { public class SysSmsChannelDO extends BaseDO {
/** /**
* 自增编号 * 自增编号

View File

@ -18,7 +18,7 @@ import java.util.Date;
@EqualsAndHashCode @EqualsAndHashCode
@Accessors(chain = true) @Accessors(chain = true)
@TableName(value = "sms_log", autoResultMap = true) @TableName(value = "sms_log", autoResultMap = true)
public class SmsLogDO implements Serializable { public class SysSmsLogDO implements Serializable {
/** /**
* 自增编号 * 自增编号

View File

@ -16,7 +16,7 @@ import java.util.Date;
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@TableName(value = "sms_template", autoResultMap = true) @TableName(value = "sms_template", autoResultMap = true)
public class SmsTemplateDO extends BaseDO { public class SysSmsTemplateDO extends BaseDO {
/** /**
* 自增编号 * 自增编号

View File

@ -4,7 +4,7 @@ import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageLi
import cn.iocoder.dashboard.framework.sms.core.SmsResult; import cn.iocoder.dashboard.framework.sms.core.SmsResult;
import cn.iocoder.dashboard.modules.system.mq.message.dept.SysDeptRefreshMessage; import cn.iocoder.dashboard.modules.system.mq.message.dept.SysDeptRefreshMessage;
import cn.iocoder.dashboard.modules.system.mq.message.sms.SmsSendMessage; import cn.iocoder.dashboard.modules.system.mq.message.sms.SmsSendMessage;
import cn.iocoder.dashboard.modules.system.service.sms.SmsService; import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -20,12 +20,12 @@ import javax.annotation.Resource;
public class SmsSendConsumer extends AbstractChannelMessageListener<SmsSendMessage> { public class SmsSendConsumer extends AbstractChannelMessageListener<SmsSendMessage> {
@Resource @Resource
private SmsService smsService; private SysSmsService sysSmsService;
@Override @Override
public void onMessage(SmsSendMessage message) { public void onMessage(SmsSendMessage message) {
log.info("[onMessage][收到 发送短信 消息]"); log.info("[onMessage][收到 发送短信 消息]");
SmsResult<?> send = smsService.send(message.getSmsBody(), message.getTargetPhones()); SmsResult send = sysSmsService.send(message.getSmsBody(), message.getTargetPhones());
} }
} }

View File

@ -6,7 +6,7 @@ import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO; import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO; import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO; import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsChannelDO; import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsChannelDO;
import java.util.List; import java.util.List;
@ -16,7 +16,7 @@ import java.util.List;
* @author zzf * @author zzf
* @date 2021/1/25 9:24 * @date 2021/1/25 9:24
*/ */
public interface SmsChannelService { public interface SysSmsChannelService {
// TODO FROM 芋艿 to ZZFSmsChannelService=SysSmsChannelService增加 Sys 前缀算在系统模块里 // TODO FROM 芋艿 to ZZFSmsChannelService=SysSmsChannelService增加 Sys 前缀算在系统模块里
// TODO FROM 芋艿 to ZZF方法名保持不去掉 Sms 前缀虽然长点嘿嘿 // TODO FROM 芋艿 to ZZF方法名保持不去掉 Sms 前缀虽然长点嘿嘿
@ -32,7 +32,7 @@ public interface SmsChannelService {
* @param reqVO 参数对象 * @param reqVO 参数对象
* @return 短信渠道分页对象 * @return 短信渠道分页对象
*/ */
PageResult<SmsChannelDO> pageChannels(SmsChannelPageReqVO reqVO); PageResult<SysSmsChannelDO> pageSmsChannels(SmsChannelPageReqVO reqVO);
/** /**
* 创建新的渠道信息 * 创建新的渠道信息
@ -40,14 +40,14 @@ public interface SmsChannelService {
* @param reqVO 参数对象 * @param reqVO 参数对象
* @return 渠道id * @return 渠道id
*/ */
Long createChannel(SmsChannelCreateReqVO reqVO); Long createSmsChannel(SmsChannelCreateReqVO reqVO);
/** /**
* 获取短信渠道枚举/渠道编码 * 获取短信渠道枚举/渠道编码
* *
* @return 短信渠道枚举/渠道编码 * @return 短信渠道枚举/渠道编码
*/ */
List<SmsChannelEnumRespVO> getChannelEnums(); List<SmsChannelEnumRespVO> getSmsChannelEnums();
/** /**
* 根据短信模板编码获取短信客户端 * 根据短信模板编码获取短信客户端
@ -55,12 +55,20 @@ public interface SmsChannelService {
* @param templateCode 短信模板编码 * @param templateCode 短信模板编码
* @return 短信客户端 * @return 短信客户端
*/ */
AbstractSmsClient<?> getClient(String templateCode); AbstractSmsClient getSmsClient(String templateCode);
/**
* 根据短信模板编码获取模板唯一标识
*
* @param templateCode 短信模板编码
* @return 短信客户端
*/
String getSmsTemplateApiIdByCode(String templateCode);
/** /**
* 查询渠道(包含名下模块)信息集合 * 查询渠道(包含名下模块)信息集合
* *
* @return 渠道(包含名下模块)信息集合 * @return 渠道(包含名下模块)信息集合
*/ */
List<SmsChannelAllVO> listChannelAllEnabledInfo(); List<SmsChannelAllVO> listSmsChannelAllEnabledInfo();
} }

View File

@ -12,7 +12,7 @@ import java.util.List;
* @author zzf * @author zzf
* @date 2021/1/25 9:24 * @date 2021/1/25 9:24
*/ */
public interface SmsLogService { public interface SysSmsLogService {
/** /**
* 发送短信前的日志处理 * 发送短信前的日志处理
* *
@ -22,9 +22,11 @@ public interface SmsLogService {
* @param isAsync 是否异步发送 * @param isAsync 是否异步发送
* @return 生成的日志id * @return 生成的日志id
*/ */
// TODO FROM 芋艿 to ZZF: async 是针对发送的方式对于日志不一定需要关心这样短信日志实际就发送前插入发送后更新结果 // TODO FROM 芋艿 to ZZF: async 是针对发送的方式对于日志不一定需要关心这样短信日志实际就发送前插入发送后更新结果.
// 这里只用于记录状态毕竟异步可能推送失败此时日志可记录该状态
// TODO FROM 芋艿 to ZZF短信日志群发的情况应该是每个手机一条哈虽然是群发但是可能部分成功部分失败对应到短信平台实际也是多条 // TODO FROM 芋艿 to ZZF短信日志群发的情况应该是每个手机一条哈虽然是群发但是可能部分成功部分失败对应到短信平台实际也是多条
Long beforeSendLog(SmsBody smsBody, List<String> targetPhones, AbstractSmsClient<?> client, Boolean isAsync); Long beforeSendLog(SmsBody smsBody, List<String> targetPhones, AbstractSmsClient client, Boolean isAsync);
/** /**
* 发送消息后的日志处理 * 发送消息后的日志处理
@ -32,6 +34,6 @@ public interface SmsLogService {
* @param logId 日志id * @param logId 日志id
* @param result 消息结果 * @param result 消息结果
*/ */
void afterSendLog(Long logId, SmsResult<?> result); void afterSendLog(Long logId, SmsResult result);
} }

View File

@ -14,7 +14,7 @@ import java.util.List;
* @author zzf * @author zzf
* @date 2021/1/25 9:24 * @date 2021/1/25 9:24
*/ */
public interface SmsService { public interface SysSmsService {
/** /**
* 发送消息 * 发送消息
@ -23,7 +23,7 @@ public interface SmsService {
* @param targetPhones 发送对象手机号列表 * @param targetPhones 发送对象手机号列表
* @return 是否发送成功 * @return 是否发送成功
*/ */
SmsResult<?> send(SmsBody smsBody, List<String> targetPhones); SmsResult send(SmsBody smsBody, List<String> targetPhones);
/** /**
* 发送消息 * 发送消息
@ -32,7 +32,7 @@ public interface SmsService {
* @param targetPhone 发送对象手机号 * @param targetPhone 发送对象手机号
* @return 是否发送成功 * @return 是否发送成功
*/ */
default SmsResult<?> send(SmsBody smsBody, String targetPhone) { default SmsResult send(SmsBody smsBody, String targetPhone) {
if (StringUtils.isBlank(targetPhone)) { if (StringUtils.isBlank(targetPhone)) {
return failResult("targetPhone must not null."); return failResult("targetPhone must not null.");
} }
@ -47,7 +47,7 @@ public interface SmsService {
* @param targetPhones 发送对象手机号数组 * @param targetPhones 发送对象手机号数组
* @return 是否发送成功 * @return 是否发送成功
*/ */
default SmsResult<?> send(SmsBody smsBody, String... targetPhones) { default SmsResult send(SmsBody smsBody, String... targetPhones) {
if (targetPhones == null) { if (targetPhones == null) {
return failResult("targetPhones must not null."); return failResult("targetPhones must not null.");
} }
@ -91,8 +91,8 @@ public interface SmsService {
} }
default SmsResult<?> failResult(String message) { default SmsResult failResult(String message) {
SmsResult<?> resultBody = new SmsResult<>(); SmsResult resultBody = new SmsResult();
resultBody.setSuccess(false); resultBody.setSuccess(false);
resultBody.setMessage(message); resultBody.setMessage(message);
return resultBody; return resultBody;

View File

@ -6,5 +6,5 @@ package cn.iocoder.dashboard.modules.system.service.sms;
* @author zzf * @author zzf
* @date 2021/1/25 9:24 * @date 2021/1/25 9:24
*/ */
public interface SmsTemplateService { public interface SysSmsTemplateService {
} }

View File

@ -1,52 +0,0 @@
package cn.iocoder.dashboard.modules.system.service.sms.impl;
import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
import cn.iocoder.dashboard.framework.sms.core.SmsBody;
import cn.iocoder.dashboard.framework.sms.core.SmsResult;
import cn.iocoder.dashboard.modules.system.mq.producer.sms.SmsProducer;
import cn.iocoder.dashboard.modules.system.service.sms.SmsChannelService;
import cn.iocoder.dashboard.modules.system.service.sms.SmsLogService;
import cn.iocoder.dashboard.modules.system.service.sms.SmsService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* 短信日志Service实现类
*
* @author zzf
* @date 2021/1/25 9:25
*/
@Service
public class SmsServiceImpl implements SmsService {
@Resource
private SmsChannelService channelService;
@Resource
private SmsLogService smsLogService;
@Resource
private SmsProducer smsProducer;
@Override
public SmsResult<?> send(SmsBody smsBody, List<String> targetPhones) {
AbstractSmsClient<?> client = channelService.getClient(smsBody.getTemplateCode());
Long logId = smsLogService.beforeSendLog(smsBody, targetPhones, client, false);
SmsResult<?> result = client.send(smsBody, targetPhones);
smsLogService.afterSendLog(logId, result);
return result;
}
// TODO FROM 芋艿 to ZZF可能要讨论下对于短信发送来说貌似只提供异步发送即可对于业务来说一定不能依赖短信的发送结果
@Override
public void sendAsync(SmsBody smsBody, List<String> targetPhones) {
AbstractSmsClient<?> client = channelService.getClient(smsBody.getTemplateCode());
smsLogService.beforeSendLog(smsBody, targetPhones, client, true);
smsProducer.sendSmsSendMessage(smsBody, targetPhones);
}
}

View File

@ -1,10 +1,10 @@
package cn.iocoder.dashboard.modules.system.service.sms.impl; package cn.iocoder.dashboard.modules.system.service.sms.impl;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.dashboard.common.enums.SmsChannelEnum;
import cn.iocoder.dashboard.common.pojo.PageResult; import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
import cn.iocoder.dashboard.framework.sms.core.SmsClientFactory; import cn.iocoder.dashboard.framework.sms.core.SmsClientFactory;
import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;
import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO; import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO; import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
@ -12,11 +12,11 @@ import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageR
import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO; import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
import cn.iocoder.dashboard.modules.system.convert.sms.SmsChannelConvert; import cn.iocoder.dashboard.modules.system.convert.sms.SmsChannelConvert;
import cn.iocoder.dashboard.modules.system.convert.sms.SmsTemplateConvert; import cn.iocoder.dashboard.modules.system.convert.sms.SmsTemplateConvert;
import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SmsChannelMapper; import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SysSmsChannelMapper;
import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SmsTemplateMapper; import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SysSmsTemplateMapper;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsChannelDO; import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsChannelDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsTemplateDO; import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsTemplateDO;
import cn.iocoder.dashboard.modules.system.service.sms.SmsChannelService; import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -35,12 +35,12 @@ import java.util.concurrent.ConcurrentHashMap;
* @date 2021/1/25 9:25 * @date 2021/1/25 9:25
*/ */
@Service @Service
public class SmsChannelServiceImpl implements SmsChannelService { public class SysSmsChannelServiceImpl implements SysSmsChannelService {
private final Map<String, Long> templateCode2ChannelIdMap = new ConcurrentHashMap<>(32); private final Map<String, Long> templateCode2ChannelIdMap = new ConcurrentHashMap<>(32);
@Autowired @Autowired
private SmsClientFactory smsClientFactory; private SmsClientFactory clientFactory;
/** /**
* 初始化短信客户端 * 初始化短信客户端
@ -48,51 +48,60 @@ public class SmsChannelServiceImpl implements SmsChannelService {
@PostConstruct @PostConstruct
@Override @Override
public void initSmsClient() { public void initSmsClient() {
List<SmsChannelAllVO> smsChannelAllVOList = listChannelAllEnabledInfo(); // 查询有效渠道信息
if (ObjectUtil.isEmpty(smsChannelAllVOList)) { List<SysSmsChannelDO> channelDOList = channelMapper.selectEnabledList();
return; List<SmsChannelProperty> propertyList = SmsChannelConvert.INSTANCE.convertProperties(channelDOList);
}
List<SmsChannelProperty> channelPropertyList = SmsChannelConvert.INSTANCE.convertProperty(smsChannelAllVOList); // 遍历渠道生成client并获取模板缓存
channelPropertyList.forEach(smsChannelProperty -> { propertyList.forEach(channelProperty -> {
Long clientId = smsClientFactory.createClient(smsChannelProperty); Long clientId = clientFactory.createClient(channelProperty);
smsChannelProperty.getTemplateList().forEach(smsTemplateVO -> { List<SysSmsTemplateDO> templateDOList = templateMapper.selectListByChannelId(channelProperty.getId());
templateCode2ChannelIdMap.put(smsTemplateVO.getCode(), clientId); if (ObjectUtil.isNotEmpty(templateDOList)) {
}); templateDOList.forEach(template -> {
templateCode2ChannelIdMap.put(template.getCode(), clientId);
});
SmsTemplateConvert.INSTANCE.convert(templateDOList);
}
}); });
} }
// TODO FROM 芋艿 to ZZFchannelMapper 保持命名统一 // TODO FROM 芋艿 to ZZFchannelMapper 保持命名统一 DONE
@Resource @Resource
private SmsChannelMapper mapper; private SysSmsChannelMapper channelMapper;
@Resource @Resource
private SmsTemplateMapper templateMapper; private SysSmsTemplateMapper templateMapper;
@Override @Override
public PageResult<SmsChannelDO> pageChannels(SmsChannelPageReqVO reqVO) { public PageResult<SysSmsChannelDO> pageSmsChannels(SmsChannelPageReqVO reqVO) {
return SmsChannelConvert.INSTANCE.convertPage(mapper.selectChannelPage(reqVO)); return SmsChannelConvert.INSTANCE.convertPage(channelMapper.selectChannelPage(reqVO));
} }
@Override @Override
public Long createChannel(SmsChannelCreateReqVO reqVO) { public Long createSmsChannel(SmsChannelCreateReqVO reqVO) {
SmsChannelDO channelDO = SmsChannelConvert.INSTANCE.convert(reqVO); SysSmsChannelDO channelDO = SmsChannelConvert.INSTANCE.convert(reqVO);
mapper.insert(channelDO); channelMapper.insert(channelDO);
return channelDO.getId(); return channelDO.getId();
} }
@Override @Override
public List<SmsChannelEnumRespVO> getChannelEnums() { public List<SmsChannelEnumRespVO> getSmsChannelEnums() {
return SmsChannelConvert.INSTANCE.convertEnum(Arrays.asList(SmsChannelEnum.values())); return SmsChannelConvert.INSTANCE.convertEnum(Arrays.asList(SmsChannelEnum.values()));
} }
@Override @Override
public AbstractSmsClient<?> getClient(String templateCode) { public AbstractSmsClient getSmsClient(String templateCode) {
return smsClientFactory.getClient(templateCode2ChannelIdMap.get(templateCode)); return clientFactory.getClient(templateCode2ChannelIdMap.get(templateCode));
} }
@Override @Override
public List<SmsChannelAllVO> listChannelAllEnabledInfo() { public String getSmsTemplateApiIdByCode(String templateCode) {
List<SmsChannelDO> channelDOList = mapper.selectEnabledList(); return clientFactory.getTemplateApiIdByCode(templateCode);
}
@Override
public List<SmsChannelAllVO> listSmsChannelAllEnabledInfo() {
List<SysSmsChannelDO> channelDOList = channelMapper.selectEnabledList();
if (ObjectUtil.isNull(channelDOList)) { if (ObjectUtil.isNull(channelDOList)) {
return null; return null;
} }
@ -100,7 +109,7 @@ public class SmsChannelServiceImpl implements SmsChannelService {
channelAllVOList.forEach(smsChannelDO -> { channelAllVOList.forEach(smsChannelDO -> {
List<SmsTemplateDO> templateDOList = templateMapper.selectListByChannelId(smsChannelDO.getId()); List<SysSmsTemplateDO> templateDOList = templateMapper.selectListByChannelId(smsChannelDO.getId());
if (ObjectUtil.isNull(templateDOList)) { if (ObjectUtil.isNull(templateDOList)) {
templateDOList = new ArrayList<>(); templateDOList = new ArrayList<>();
} }

View File

@ -3,14 +3,12 @@ package cn.iocoder.dashboard.modules.system.service.sms.impl;
import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
import cn.iocoder.dashboard.framework.sms.core.SmsBody; import cn.iocoder.dashboard.framework.sms.core.SmsBody;
import cn.iocoder.dashboard.framework.sms.core.SmsResult; import cn.iocoder.dashboard.framework.sms.core.SmsResult;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO; import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty;
import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsTemplateVO; import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SysSmsLogMapper;
import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SmsLogMapper; import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsLogDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsLogDO;
import cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum; import cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum;
import cn.iocoder.dashboard.modules.system.service.sms.SmsLogService; import cn.iocoder.dashboard.modules.system.service.sms.SysSmsLogService;
import cn.iocoder.dashboard.util.json.JsonUtils; import cn.iocoder.dashboard.util.json.JsonUtils;
import cn.iocoder.dashboard.util.string.StrUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
@ -23,42 +21,41 @@ import java.util.List;
* @date 2021/1/25 9:25 * @date 2021/1/25 9:25
*/ */
@Service @Service
public class SmsLogServiceImpl implements SmsLogService { public class SysSmsLogServiceImpl implements SysSmsLogService {
@Resource @Resource
private SmsLogMapper smsLogMapper; private SysSmsLogMapper logMapper;
@Override @Override
public Long beforeSendLog(SmsBody smsBody, List<String> targetPhones, AbstractSmsClient<?> client, Boolean isAsync) { public Long beforeSendLog(SmsBody smsBody, List<String> targetPhones, AbstractSmsClient client, Boolean isAsync) {
SmsLogDO smsLog = new SmsLogDO(); SysSmsLogDO smsLog = new SysSmsLogDO();
if (smsBody.getSmsLogId() != null) { if (smsBody.getSmsLogId() != null) {
smsLog.setId(smsBody.getSmsLogId()); smsLog.setId(smsBody.getSmsLogId());
smsLog.setSendStatus(SmsSendStatusEnum.SENDING.getStatus()); smsLog.setSendStatus(SmsSendStatusEnum.SENDING.getStatus());
smsLogMapper.updateById(smsLog); logMapper.updateById(smsLog);
return smsBody.getSmsLogId(); return smsBody.getSmsLogId();
} else { } else {
SmsChannelAllVO property = client.getProperty(); SmsChannelProperty property = client.getProperty();
SmsTemplateVO smsTemplate = property.getTemplateByTemplateCode(smsBody.getTemplateCode());
smsLog.setChannelCode(property.getCode()) smsLog.setChannelCode(property.getCode())
.setChannelId(property.getId()) .setChannelId(property.getId())
.setTemplateCode(smsTemplate.getCode()) .setTemplateCode(smsBody.getTemplateCode())
.setPhones(JsonUtils.toJsonString(targetPhones)) .setPhones(JsonUtils.toJsonString(targetPhones))
.setContent(StrUtils.replace(smsTemplate.getContent(), smsBody.getParams())); .setContent(smsBody.getParams().toString());
if (isAsync) { if (isAsync) {
smsLog.setSendStatus(SmsSendStatusEnum.ASYNC.getStatus()); smsLog.setSendStatus(SmsSendStatusEnum.ASYNC.getStatus());
} else { } else {
smsLog.setSendStatus(SmsSendStatusEnum.SENDING.getStatus()); smsLog.setSendStatus(SmsSendStatusEnum.SENDING.getStatus());
} }
smsLogMapper.insert(smsLog); logMapper.insert(smsLog);
return smsLog.getId(); return smsLog.getId();
} }
} }
@Override @Override
public void afterSendLog(Long logId, SmsResult<?> result) { public void afterSendLog(Long logId, SmsResult result) {
SmsLogDO smsLog = new SmsLogDO(); SysSmsLogDO smsLog = new SysSmsLogDO();
smsLog.setId(logId); smsLog.setId(logId);
if (result.getSuccess()) { if (result.getSuccess()) {
smsLog.setSendStatus(SmsSendStatusEnum.SUCCESS.getStatus()); smsLog.setSendStatus(SmsSendStatusEnum.SUCCESS.getStatus());
@ -66,7 +63,7 @@ public class SmsLogServiceImpl implements SmsLogService {
smsLog.setSendStatus(SmsSendStatusEnum.FAIL.getStatus()); smsLog.setSendStatus(SmsSendStatusEnum.FAIL.getStatus());
smsLog.setRemark(result.getMessage() + JsonUtils.toJsonString(result.getResult())); smsLog.setRemark(result.getMessage() + JsonUtils.toJsonString(result.getResult()));
} }
smsLogMapper.updateById(smsLog); logMapper.updateById(smsLog);
} }
} }

View File

@ -0,0 +1,54 @@
package cn.iocoder.dashboard.modules.system.service.sms.impl;
import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
import cn.iocoder.dashboard.framework.sms.core.SmsBody;
import cn.iocoder.dashboard.framework.sms.core.SmsResult;
import cn.iocoder.dashboard.modules.system.mq.producer.sms.SmsProducer;
import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService;
import cn.iocoder.dashboard.modules.system.service.sms.SysSmsLogService;
import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* 短信日志Service实现类
*
* @author zzf
* @date 2021/1/25 9:25
*/
@Service
public class SysSmsServiceImpl implements SysSmsService {
@Resource
private SysSmsChannelService channelService;
@Resource
private SysSmsLogService logService;
@Resource
private SmsProducer smsProducer;
@Override
public SmsResult send(SmsBody smsBody, List<String> targetPhones) {
AbstractSmsClient client = channelService.getSmsClient(smsBody.getTemplateCode());
String templateApiId = channelService.getSmsTemplateApiIdByCode(smsBody.getTemplateCode());
Long logId = logService.beforeSendLog(smsBody, targetPhones, client, false);
SmsResult result = client.send(templateApiId, smsBody, targetPhones);
logService.afterSendLog(logId, result);
return result;
}
// TODO FROM 芋艿 to ZZF可能要讨论下对于短信发送来说貌似只提供异步发送即可对于业务来说一定不能依赖短信的发送结果
// 我的想法是1很多短信比如验证码总还是需要知道是否发送成功的2别人可以不用我们不能没有3实现挺简单的个人觉得无需纠结
@Override
public void sendAsync(SmsBody smsBody, List<String> targetPhones) {
AbstractSmsClient client = channelService.getSmsClient(smsBody.getTemplateCode());
logService.beforeSendLog(smsBody, targetPhones, client, true);
smsProducer.sendSmsSendMessage(smsBody, targetPhones);
}
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.dashboard.modules.system.service.sms.impl; package cn.iocoder.dashboard.modules.system.service.sms.impl;
import cn.iocoder.dashboard.modules.system.service.sms.SmsTemplateService; import cn.iocoder.dashboard.modules.system.service.sms.SysSmsTemplateService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
/** /**
@ -10,5 +10,5 @@ import org.springframework.stereotype.Service;
* @date 2021/1/25 9:25 * @date 2021/1/25 9:25
*/ */
@Service @Service
public class SmsTemplateServiceImpl implements SmsTemplateService { public class SysSmsTemplateServiceImpl implements SysSmsTemplateService {
} }