邮件-腾讯云
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
|
||||
package com.accompany.email.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Data
|
||||
@RefreshScope
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "tencent.ses")
|
||||
public class TencentSesConfig {
|
||||
|
||||
private String secretId;
|
||||
private String secretKey;
|
||||
private String region;
|
||||
|
||||
}
|
@@ -8,4 +8,6 @@ package com.accompany.email.enums;
|
||||
public enum SesTypeEnum {
|
||||
|
||||
AWS,
|
||||
TENCENT,
|
||||
;
|
||||
}
|
||||
|
@@ -0,0 +1,23 @@
|
||||
package com.accompany.email.strategy;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author: liaozetao
|
||||
* @date: 2023/8/14 14:57
|
||||
* @description:
|
||||
*/
|
||||
@Data
|
||||
public class EmailContext {
|
||||
|
||||
/**
|
||||
* 手机号码
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
private String code;
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package com.accompany.email.strategy;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author: liaozetao
|
||||
* @date: 2023/8/14 15:06
|
||||
* @description:
|
||||
*/
|
||||
@Data
|
||||
public class EmailResponse {
|
||||
|
||||
/**
|
||||
* 异常编码
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 异常信息
|
||||
*/
|
||||
private String message;
|
||||
}
|
@@ -26,6 +26,12 @@
|
||||
<version>2.30.37</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.tencentcloudapi</groupId>
|
||||
<artifactId>tencentcloud-sdk-java</artifactId>
|
||||
<version>${tencentcloud-sdk-java.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@@ -0,0 +1,61 @@
|
||||
package com.accompany.email.config;
|
||||
|
||||
import com.tencentcloudapi.common.Credential;
|
||||
import com.tencentcloudapi.common.profile.ClientProfile;
|
||||
import com.tencentcloudapi.common.profile.HttpProfile;
|
||||
import com.tencentcloudapi.ses.v20201002.SesClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class TencentSecClientConfig {
|
||||
|
||||
@Autowired
|
||||
private TencentSesConfig config;
|
||||
|
||||
@Bean
|
||||
public Credential credential() {
|
||||
/* 必要步骤:
|
||||
* 实例化一个认证对象,入参需要传入腾讯云账户密钥对secretId,secretKey。
|
||||
* 这里采用的是从环境变量读取的方式,需要在环境变量中先设置这两个值。
|
||||
* 你也可以直接在代码中写死密钥对,但是小心不要将代码复制、上传或者分享给他人,
|
||||
* 以免泄露密钥对危及你的财产安全。
|
||||
* SecretId、SecretKey 查询: https://console.cloud.tencent.com/cam/capi */
|
||||
return new Credential(config.getSecretId(), config.getSecretKey());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HttpProfile httpProfile() {
|
||||
// 实例化一个http选项,可选,没有特殊需求可以跳过
|
||||
HttpProfile httpProfile = new HttpProfile();
|
||||
// 设置代理(无需要直接忽略)
|
||||
// httpProfile.setProxyHost("真实代理ip");
|
||||
// httpProfile.setProxyPort(真实代理端口);
|
||||
/* SDK默认使用POST方法。
|
||||
* 如果你一定要使用GET方法,可以在这里设置。GET方法无法处理一些较大的请求 */
|
||||
httpProfile.setReqMethod("POST");
|
||||
/* SDK有默认的超时时间,非必要请不要进行调整
|
||||
* 如有需要请在代码中查阅以获取最新的默认值 */
|
||||
httpProfile.setConnTimeout(60);
|
||||
/* 指定接入地域域名,默认就近地域接入域名为 sms.tencentcloudapi.com ,也支持指定地域域名访问,例如广州地域的域名为 sms.ap-guangzhou.tencentcloudapi.com */
|
||||
httpProfile.setEndpoint("ses.tencentcloudapi.com");
|
||||
return httpProfile;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SesClient sesClient(Credential credential, HttpProfile httpProfile) {
|
||||
/* 非必要步骤:
|
||||
* 实例化一个客户端配置对象,可以指定超时时间等配置 */
|
||||
ClientProfile clientProfile = new ClientProfile();
|
||||
/* SDK默认用TC3-HMAC-SHA256进行签名
|
||||
* 非必要请不要修改这个字段 */
|
||||
clientProfile.setSignMethod("HmacSHA256");
|
||||
clientProfile.setHttpProfile(httpProfile);
|
||||
/* 实例化要请求产品(以sms为例)的client对象
|
||||
* 第二个参数是地域信息,可以直接填写字符串ap-guangzhou,支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8 */
|
||||
//return new SesClient(credential, config.getRegion(), clientProfile);
|
||||
return new SesClient(credential, config.getRegion(), clientProfile);
|
||||
}
|
||||
|
||||
}
|
@@ -9,15 +9,15 @@ import com.accompany.common.utils.RandomUtil;
|
||||
import com.accompany.common.utils.StringUtils;
|
||||
import com.accompany.core.exception.ServiceException;
|
||||
import com.accompany.core.service.base.BaseService;
|
||||
import com.accompany.email.enums.SesTypeEnum;
|
||||
import com.accompany.email.strategy.EmailContext;
|
||||
import com.accompany.email.strategy.EmailFactory;
|
||||
import com.accompany.email.strategy.EmailResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.Assert;
|
||||
import software.amazon.awssdk.awscore.exception.AwsErrorDetails;
|
||||
import software.amazon.awssdk.awscore.internal.AwsErrorCode;
|
||||
import software.amazon.awssdk.services.sesv2.model.SendEmailResponse;
|
||||
import software.amazon.awssdk.services.sesv2.model.SesV2Exception;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
@@ -34,8 +34,6 @@ public class EmailService extends BaseService {
|
||||
@Autowired
|
||||
private RedissonClient redissonClient;
|
||||
@Autowired
|
||||
private AwsSecService awsSecService;
|
||||
@Autowired
|
||||
private EmailRecordService emailRecordService;
|
||||
|
||||
private final String EMAIL_REGEX = "^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$";
|
||||
@@ -67,31 +65,21 @@ public class EmailService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
String responseCode = null;
|
||||
String responseMsg = null;
|
||||
EmailContext emailContext = new EmailContext();
|
||||
emailContext.setEmail(emailAddress);
|
||||
emailContext.setCode(code);
|
||||
|
||||
try {
|
||||
SendEmailResponse response = awsSecService.sendMailCode(emailAddress, code, EmailConstant.EXPIRE_MINUTE);
|
||||
responseCode = String.valueOf(response.sdkHttpResponse().statusCode());
|
||||
responseMsg = response.sdkHttpResponse().statusText().orElse(null);
|
||||
EmailResponse response = EmailFactory.getInstance(SesTypeEnum.TENCENT.name()).sendEmailCode(emailContext);
|
||||
|
||||
log.info("[email] to {} code {} responseCode {} responseMsg {}", emailAddress, code, responseCode, responseMsg);
|
||||
log.info("[email] to {} code {} responseCode {} responseMsg {}", emailAddress, code, response.getCode(), response.getMessage());
|
||||
|
||||
if (rateLimiter != null) {
|
||||
rateLimiter.tryAcquire();
|
||||
rateLimiter.expire(Duration.of(1, ChronoUnit.DAYS));
|
||||
}
|
||||
|
||||
} catch (SesV2Exception e){
|
||||
log.error("[email] error {}", e.getMessage(), e);
|
||||
|
||||
AwsErrorDetails awsErrorDetails = e.awsErrorDetails();
|
||||
responseCode = awsErrorDetails.errorCode();
|
||||
responseMsg = awsErrorDetails.errorMessage();
|
||||
if (rateLimiter != null) {
|
||||
rateLimiter.tryAcquire();
|
||||
rateLimiter.expire(Duration.of(1, ChronoUnit.DAYS));
|
||||
}
|
||||
|
||||
//新增短信记录
|
||||
emailRecordService.saveRecord(emailAddress, deviceInfo, ip, code, type, responseCode, responseMsg);
|
||||
emailRecordService.saveRecord(emailAddress, deviceInfo, ip, code, type, response.getCode(), response.getMessage());
|
||||
//todo 响应编码校验
|
||||
|
||||
// 写入短信发送频率
|
||||
|
@@ -0,0 +1,51 @@
|
||||
package com.accompany.email.service;
|
||||
|
||||
import com.accompany.email.config.EmailConfig;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
|
||||
import com.tencentcloudapi.ses.v20201002.SesClient;
|
||||
import com.tencentcloudapi.ses.v20201002.models.SendEmailRequest;
|
||||
import com.tencentcloudapi.ses.v20201002.models.SendEmailResponse;
|
||||
import com.tencentcloudapi.ses.v20201002.models.Simple;
|
||||
import com.tencentcloudapi.ses.v20201002.models.Template;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class TencentSecService {
|
||||
|
||||
@Autowired
|
||||
private EmailConfig emailConfig;
|
||||
@Autowired
|
||||
private SesClient client;
|
||||
|
||||
public SendEmailResponse sendMailCode(String emailAddress, String code, Integer expire) throws TencentCloudSDKException {
|
||||
String appName = emailConfig.getAppName();
|
||||
//String logoUrl = emailConfig.getLogoUrl();
|
||||
String sender = emailConfig.getSender();
|
||||
|
||||
String title = String.format("%s verification code", appName);
|
||||
String subject = String.format("%s is %s", title, code);
|
||||
|
||||
SendEmailRequest request = new SendEmailRequest();
|
||||
request.setFromEmailAddress(sender);
|
||||
request.setDestination(new String[]{emailAddress});
|
||||
request.setSubject(subject);
|
||||
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("vvvv", code);
|
||||
|
||||
Template template = new Template();
|
||||
template.setTemplateID(66001L);
|
||||
template.setTemplateData(JSON.toJSONString(params));
|
||||
|
||||
request.setTemplate(template);
|
||||
request.setTriggerType(1L);
|
||||
|
||||
return client.SendEmail(request);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
package com.accompany.email.strategy;
|
||||
|
||||
import com.accompany.common.constant.EmailConstant;
|
||||
import com.accompany.core.base.SpringContextHolder;
|
||||
import com.accompany.email.enums.SesTypeEnum;
|
||||
import com.accompany.email.service.AwsSecService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import software.amazon.awssdk.awscore.exception.AwsErrorDetails;
|
||||
import software.amazon.awssdk.services.sesv2.model.SendEmailResponse;
|
||||
import software.amazon.awssdk.services.sesv2.model.SesV2Exception;
|
||||
|
||||
@Slf4j
|
||||
public class AwsEmailStrategy extends EmailStrategy{
|
||||
@Override
|
||||
public String type() {
|
||||
return SesTypeEnum.AWS.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmailResponse sendEmailCode(EmailContext context) {
|
||||
EmailResponse r = new EmailResponse();
|
||||
|
||||
try {
|
||||
SendEmailResponse response = SpringContextHolder.getBean(AwsSecService.class).sendMailCode(context.getEmail(), context.getCode(), EmailConstant.EXPIRE_MINUTE);
|
||||
|
||||
r.setCode(String.valueOf(response.sdkHttpResponse().statusCode()));
|
||||
r.setMessage(response.sdkHttpResponse().statusText().orElse(null));
|
||||
} catch (SesV2Exception e){
|
||||
log.error("[email] error {}", e.getMessage(), e);
|
||||
|
||||
AwsErrorDetails awsErrorDetails = e.awsErrorDetails();
|
||||
r.setCode(awsErrorDetails.errorCode());
|
||||
r.setMessage(awsErrorDetails.errorMessage());
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validResCode(String resCode) {
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
package com.accompany.email.strategy;
|
||||
|
||||
import com.accompany.email.enums.SesTypeEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @author: liaozetao
|
||||
* @date: 2023/8/14 16:05
|
||||
* @description:
|
||||
*/
|
||||
@Slf4j
|
||||
public class EmailFactory {
|
||||
|
||||
public static EmailStrategy getInstance(String type) {
|
||||
EmailStrategy strategy = null;
|
||||
if (SesTypeEnum.AWS.name().equalsIgnoreCase(type)) {
|
||||
strategy = new AwsEmailStrategy();
|
||||
} else if (SesTypeEnum.TENCENT.name().equalsIgnoreCase(type)){
|
||||
strategy = new TencentEmailStrategy();
|
||||
}
|
||||
log.info("email sdk type : {}", type);
|
||||
return strategy;
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
package com.accompany.email.strategy;
|
||||
|
||||
/**
|
||||
* @author: liaozetao
|
||||
* @date: 2023/8/14 14:59
|
||||
* @description:
|
||||
*/
|
||||
public abstract class EmailStrategy {
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public abstract String type();
|
||||
|
||||
|
||||
/**
|
||||
* 发送验证码
|
||||
*
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
public abstract EmailResponse sendEmailCode(EmailContext context);
|
||||
/**
|
||||
* 校验返回结果
|
||||
*
|
||||
* @param resCode
|
||||
*/
|
||||
public abstract void validResCode(String resCode);
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
|
||||
package com.accompany.email.strategy;
|
||||
|
||||
import com.accompany.common.constant.EmailConstant;
|
||||
import com.accompany.core.base.SpringContextHolder;
|
||||
import com.accompany.email.enums.SesTypeEnum;
|
||||
import com.accompany.email.service.TencentSecService;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
|
||||
import com.tencentcloudapi.ses.v20201002.models.SendEmailResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class TencentEmailStrategy extends EmailStrategy{
|
||||
@Override
|
||||
public String type() {
|
||||
return SesTypeEnum.TENCENT.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmailResponse sendEmailCode(EmailContext context) {
|
||||
EmailResponse r = new EmailResponse();
|
||||
|
||||
try {
|
||||
SendEmailResponse response = SpringContextHolder.getBean(TencentSecService.class).sendMailCode(context.getEmail(), context.getCode(), EmailConstant.EXPIRE_MINUTE);
|
||||
|
||||
r.setCode("200");
|
||||
r.setMessage("OK");
|
||||
|
||||
log.info("sendEmailCode success {}", JSON.toJSONString(response));
|
||||
|
||||
} catch (TencentCloudSDKException e) {
|
||||
//todo log
|
||||
|
||||
log.error("sendEmailCode exception", e);
|
||||
|
||||
r.setCode(e.getErrorCode());
|
||||
r.setMessage(e.getMessage());
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validResCode(String resCode) {
|
||||
|
||||
}
|
||||
}
|
@@ -1,8 +1,5 @@
|
||||
package com.accompany.sms.strategy;
|
||||
|
||||
import com.accompany.sms.strategy.SmsContext;
|
||||
import com.accompany.sms.strategy.SmsResponse;
|
||||
|
||||
/**
|
||||
* @author: liaozetao
|
||||
* @date: 2023/8/14 14:59
|
||||
|
Reference in New Issue
Block a user