TASK: 发送邮件功能及发送失败后的重试机制

This commit is contained in:
2025-07-31 15:57:47 +08:00
parent 7edc959432
commit 739e637267
12 changed files with 5131 additions and 4826 deletions

View File

@@ -0,0 +1,96 @@
package com.ai.da.common.RabbitMQ;
import com.ai.da.common.utils.MailUtil;
import com.ai.da.model.dto.BasicEmailParamDTO;
import com.ai.da.service.EmailService;
import com.alibaba.fastjson.JSONObject;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.core.io.InputStreamSource;
import org.springframework.stereotype.Component;
import software.amazon.awssdk.core.exception.RetryableException;
import org.springframework.amqp.core.Message;
import javax.annotation.Resource;
import javax.mail.MessagingException;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;
@Slf4j
@Component
public class EmailRetryConsumer {
@Resource
private MailUtil mailUtil;
@Resource
private MQPublisher mqPublisher;
@Resource
private EmailService emailService;
// @RabbitListener(queues = "#{rabbitMQProperties.deadLetter.queue}")
public void handleRetry(Map<String, String> mailParams, Message message, Channel channel) throws IOException {
long tag = message.getMessageProperties().getDeliveryTag();
try {
log.info("死信队列收到消息:{}", message);
// 处理邮件发送参数
BasicEmailParamDTO basicEmailParamDTO = JSONObject.parseObject(mailParams.get("dto"), BasicEmailParamDTO.class);
String fileName = mailParams.get("filename");
InputStreamSource inputStreamSource = Objects.isNull(mailParams.get("source")) ?
null : JSONObject.parseObject(mailParams.get("source"), InputStreamSource.class);
JSONObject templateParams = JSONObject.parseObject(mailParams.get("templateParams"), JSONObject.class);
String templateName = mailParams.get("templatePath");
long logId = Long.parseLong(mailParams.get("logId"));
basicEmailParamDTO.setContent(mailUtil.setContent(templateParams, templateName));
// 发邮件
int lastReturnCode = mailUtil.sendMail(basicEmailParamDTO, fileName, inputStreamSource);
if (lastReturnCode == 250) {
log.info("邮件发送成功Subject : {}", basicEmailParamDTO.getSubject());
emailService.updateStatus(logId, EmailService.DELIVERED);
} else if (lastReturnCode == 450) {
log.info("目标邮箱 {} 暂时不可用,请稍后重试", (Object) basicEmailParamDTO.getMailTo());
// 重试
retry(mailParams, message, channel, tag, logId);
} else if (lastReturnCode == 550) {
log.info("目标邮箱 {} 不可用,邮件发送失败", (Object) basicEmailParamDTO.getMailTo());
emailService.updateStatus(logId, EmailService.FAILED);
} else {
log.info("邮件发送失败Subject : {}, 状态码: {}", basicEmailParamDTO.getSubject(), lastReturnCode);
retry(mailParams, message, channel, tag, logId);
emailService.updateStatus(logId, EmailService.FAILED);
}
channel.basicAck(tag, false);
} catch (RetryableException e) {
log.info("邮件重试发生异常:RetryableException -> {}", e.getMessage());
channel.basicAck(tag, false); // 确认原消息
} catch (Exception e) {
log.info("邮件重试发生异常:Exception -> {}", e.getMessage());
channel.basicAck(tag, false); // 确认原消息
}
}
private int getRetryAttempt(Message message) {
Integer attempt = message.getMessageProperties()
.getHeader("x-retry-attempt");
return attempt != null ? attempt : 1;
}
private void retry(Map<String, String> mailParams, Message message, Channel channel, long tag, long logId) throws IOException{
int attempt = getRetryAttempt(message);
if (attempt >= 3) { // 最大重试次数
channel.basicReject(tag, false);
emailService.updateStatus(logId, EmailService.FAILED);
log.error("重试结束,邮件最终发送失败: {}", mailParams);
} else {
log.info("重新将邮件信息发送到重试队列");
mqPublisher.sendEmailMsg(mailParams, attempt);
channel.basicAck(tag, false); // 确认原消息
// 更新数据库
emailService.updateRetryCount(logId, attempt + 1);
}
}
}

View File

@@ -1,43 +1,82 @@
package com.ai.da.common.RabbitMQ; package com.ai.da.common.RabbitMQ;
import org.springframework.amqp.core.Queue; import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@Configuration @Configuration
public class MQConfig { public class MQConfig {
@Autowired @Autowired
private RabbitMQProperties rabbitMQProperties; private RabbitMQProperties rabbitMQProperties;
@Bean @Bean
public Queue generateQueue() { public Queue generateQueue() {
return new Queue(rabbitMQProperties.getQueues().getGenerate()); return new Queue(rabbitMQProperties.getQueues().getGenerate());
} }
@Bean @Bean
public Queue SRQueue() { public Queue SRQueue() {
return new Queue(rabbitMQProperties.getQueues().getSr()); return new Queue(rabbitMQProperties.getQueues().getSr());
} }
@Bean @Bean
public Queue SRResultQueue() { public Queue SRResultQueue() {
return new Queue(rabbitMQProperties.getQueues().getSrResult()); return new Queue(rabbitMQProperties.getQueues().getSrResult());
} }
@Bean @Bean
public Queue generateResultQueue() { public Queue generateResultQueue() {
return new Queue(rabbitMQProperties.getQueues().getGenerateResult()); return new Queue(rabbitMQProperties.getQueues().getGenerateResult());
} }
@Bean @Bean
public Queue toProductImageResultQueue() { public Queue toProductImageResultQueue() {
return new Queue(rabbitMQProperties.getQueues().getToProductImageResult()); return new Queue(rabbitMQProperties.getQueues().getToProductImageResult());
} }
@Bean @Bean
public Queue relightResultQueue() { public Queue relightResultQueue() {
return new Queue(rabbitMQProperties.getQueues().getRelightResult()); return new Queue(rabbitMQProperties.getQueues().getRelightResult());
} }
}
@Bean
public Queue poseTransformQueue() {
return new Queue(rabbitMQProperties.getQueues().getPoseTransform());
}
@Bean
public Queue mailRetryQueue() {
// 普通队列不绑定DLX首次失败后才进入MQ
// durable 持久化队列
return QueueBuilder.durable(rabbitMQProperties.getQueues().getEmailRetry())
// 关键参数:绑定死信交换机
.withArgument("x-dead-letter-exchange", rabbitMQProperties.getDeadLetter().getExchange())
// 可选:指定死信路由键(默认使用原消息的路由键)
.withArgument("x-dead-letter-routing-key", rabbitMQProperties.getDeadLetter().getRoutingKey())
.build();
}
// 新增死信交换机
@Bean
public DirectExchange deadLetterExchange() {
return new DirectExchange(rabbitMQProperties.getDeadLetter().getExchange());
}
// 新增死信队列
@Bean
public Queue deadLetterQueue() {
return QueueBuilder.durable(rabbitMQProperties.getDeadLetter().getQueue()).build();
}
// 绑定死信队列
@Bean
public Binding deadLetterBinding() {
return BindingBuilder.bind(deadLetterQueue())
.to(deadLetterExchange())
.with(rabbitMQProperties.getDeadLetter().getRoutingKey());
}
}

View File

@@ -1,29 +1,56 @@
package com.ai.da.common.RabbitMQ; package com.ai.da.common.RabbitMQ;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate; import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Map;
@Slf4j
@Component @Slf4j
public class MQPublisher { @Component
public class MQPublisher {
@Autowired
private RabbitMQProperties rabbitMQProperties; @Autowired
private RabbitMQProperties rabbitMQProperties;
@Autowired
private AmqpTemplate amqpTemplate; @Autowired
private AmqpTemplate amqpTemplate;
public void sendGenerateMessage(String mm) {
log.info("send message: " + mm); public void sendGenerateMessage(String mm) {
amqpTemplate.convertAndSend(rabbitMQProperties.getQueues().getGenerate(), mm); log.info("send generate message: {}", mm);
} amqpTemplate.convertAndSend(rabbitMQProperties.getQueues().getGenerate(), mm);
}
public void sendSRMessage(String mm) {
log.info("send message: " + mm); public void sendSRMessage(String mm) {
amqpTemplate.convertAndSend(rabbitMQProperties.getQueues().getSr(), mm); log.info("send message: {}", mm);
} amqpTemplate.convertAndSend(rabbitMQProperties.getQueues().getSr(), mm);
} }
/**
*
* @param mailParams 含有的字段
* {"dto": basicEmailParamDTO, "filename": fileName, "source": inputStreamSource,
* "templateParams": jsonObject, "templatePath": path}
* 邮件发送参数,附件文件名,附件数据
* @param retryTimes 重试次数初始为0
*/
public void sendEmailMsg(Map<String, String> mailParams, int retryTimes){
log.info("send email MQ message: {} ", mailParams);
// // 重新入队(指数退避) 时间单位:毫秒
long newDelay = (long) (5000 * Math.pow(2, retryTimes + 1));
log.info("send email MQ delay: {} ms, retry attempt: {}", newDelay, retryTimes + 1);
amqpTemplate.convertAndSend(
rabbitMQProperties.getQueues().getEmailRetry(),
mailParams,
m -> {
m.getMessageProperties().setExpiration(String.valueOf(newDelay));
m.getMessageProperties().setHeader("x-retry-attempt", retryTimes + 1);
return m;
}
);
}
}

View File

@@ -1,35 +1,45 @@
package com.ai.da.common.RabbitMQ; package com.ai.da.common.RabbitMQ;
import lombok.Data; import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Component @Component
@ConfigurationProperties(prefix = "rabbitmq") @ConfigurationProperties(prefix = "rabbitmq")
@Data @Data
public class RabbitMQProperties { public class RabbitMQProperties {
private Queues queues; private Queues queues;
private Exchange exchange; private Exchange exchange;
private DeadLetter deadLetter; // 新增死信配置
@Data
public static class Queues { @Data
private String generate; public static class Queues {
private String sr; private String generate;
private String srResult; private String sr;
private String generateResult; private String srResult;
private String toProductImageResult; private String generateResult;
private String relightResult; private String toProductImageResult;
private String poseTransform; private String relightResult;
private String designBatch; private String poseTransform;
private String relightBatch; private String emailRetry;
private String toProductImageBatch; private String designBatch;
private String poseTransformBatch; private String relightBatch;
} private String toProductImageBatch;
private String poseTransformBatch;
@Data }
public static class Exchange {
private String generate; @Data
} public static class Exchange {
} private String generate;
}
// 新增死信配置内部类
@Data
public static class DeadLetter {
private String exchange;
private String queue;
private String routingKey;
}
}

View File

@@ -1,168 +1,176 @@
package com.ai.da.common.utils; package com.ai.da.common.utils;
import com.ai.da.model.dto.BasicEmailParamDTO; import com.ai.da.model.dto.BasicEmailParamDTO;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.sun.mail.smtp.SMTPTransport; import com.sun.mail.smtp.SMTPTransport;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.InputStreamSource; import org.springframework.core.io.InputStreamSource;
import org.springframework.mail.MailException; import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thymeleaf.TemplateEngine; import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context; import org.thymeleaf.context.Context;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.mail.MessagingException; import javax.mail.MessagingException;
import javax.mail.internet.*; import javax.mail.internet.*;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@Slf4j @Slf4j
@Component @Component
public class MailUtil { public class MailUtil {
@Resource @Resource
private JavaMailSender javaMailSender; private JavaMailSender javaMailSender;
@Resource @Resource
private TemplateEngine templateEngine; private TemplateEngine templateEngine;
/** /**
* 发送邮件 - 默认发件人 * 发送邮件 - 默认发件人
* *
* @param basicEmailParamDTO 发送邮件所需参数 * @param basicEmailParamDTO 发送邮件所需参数
* @param inputStreamSource 附件(如果有) * @param inputStreamSource 附件(如果有)
*/ */
public int sendMail(BasicEmailParamDTO basicEmailParamDTO, String fileName, InputStreamSource inputStreamSource) throws MessagingException { public int sendMail(BasicEmailParamDTO basicEmailParamDTO, String fileName, InputStreamSource inputStreamSource) throws MessagingException {
MimeMessage mimeMessage = createSimpleMail(basicEmailParamDTO, fileName, inputStreamSource); MimeMessage mimeMessage = createSimpleMail(basicEmailParamDTO, fileName, inputStreamSource);
// 提取配置 // 提取配置
String host; String host;
String username; String username;
String password; String password;
if (StringUtil.isNullOrEmpty(basicEmailParamDTO.getServiceAddress())) { if (StringUtil.isNullOrEmpty(basicEmailParamDTO.getServiceAddress())) {
host = ((JavaMailSenderImpl) javaMailSender).getHost(); host = ((JavaMailSenderImpl) javaMailSender).getHost();
} else { } else {
host = basicEmailParamDTO.getServiceAddress(); host = basicEmailParamDTO.getServiceAddress();
} }
if (StringUtil.isNullOrEmpty(basicEmailParamDTO.getSenderUser())) { if (StringUtil.isNullOrEmpty(basicEmailParamDTO.getSenderUser())) {
username = ((JavaMailSenderImpl) javaMailSender).getUsername(); username = ((JavaMailSenderImpl) javaMailSender).getUsername();
} else { } else {
username = basicEmailParamDTO.getSenderUser(); username = basicEmailParamDTO.getSenderUser();
} }
if (StringUtil.isNullOrEmpty(basicEmailParamDTO.getServiceAddress())) { if (StringUtil.isNullOrEmpty(basicEmailParamDTO.getServiceAddress())) {
password = ((JavaMailSenderImpl) javaMailSender).getPassword(); password = ((JavaMailSenderImpl) javaMailSender).getPassword();
} else { } else {
password = basicEmailParamDTO.getPassword(); password = basicEmailParamDTO.getPassword();
} }
return sendMail(mimeMessage, host, username, password); return sendMail(mimeMessage, host, username, password);
} }
private int sendMail(MimeMessage mimeMessage, String host, String username, String password) throws MessagingException { private int sendMail(MimeMessage mimeMessage, String host, String username, String password) throws MessagingException {
SMTPTransport transport = null; SMTPTransport transport = null;
try { try {
// 获取 SMTPTransport // 获取 SMTPTransport
transport = (SMTPTransport) mimeMessage.getSession().getTransport("smtp"); transport = (SMTPTransport) mimeMessage.getSession().getTransport("smtp");
// 连接到 SMTP 服务器 // 连接到 SMTP 服务器
transport.connect(host, username, password); transport.connect(host, username, password);
// 发送邮件 // 发送邮件
transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients()); transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());
// 获取 SMTP 服务器的响应 // 获取 SMTP 服务器的响应
String lastServerResponse = transport.getLastServerResponse(); String lastServerResponse = transport.getLastServerResponse();
int lastReturnCode = transport.getLastReturnCode(); int lastReturnCode = transport.getLastReturnCode();
log.info("SMTP 状态码: {}, SMTP 服务器响应: {}", lastReturnCode, lastServerResponse); log.info("SMTP 状态码: {}, SMTP 服务器响应: {}", lastReturnCode, lastServerResponse);
return lastReturnCode; return lastReturnCode;
} catch (MailException | MessagingException e) { } catch (MailException | MessagingException e) {
// 记录日志或执行其他补偿逻辑 // 记录日志或执行其他补偿逻辑
log.info("邮件发送失败:{}", e.getMessage()); log.info("邮件发送失败:{}", e.getMessage());
} finally { } finally {
// 关闭连接 // 关闭连接
assert transport != null; assert transport != null;
transport.close(); transport.close();
} }
return 0; return 0;
} }
/** /**
* 创建一封邮件 * 创建一封邮件
* *
* @param basicEmailParamDTO 创建邮件需要的参数 * @param basicEmailParamDTO 创建邮件需要的参数
* @param inputStreamSource 附件(如果有) * @param inputStreamSource 附件(如果有)
* @return 一封邮件 * @return 一封邮件
*/ */
private MimeMessage createSimpleMail(BasicEmailParamDTO basicEmailParamDTO, String fileName, InputStreamSource inputStreamSource) throws MessagingException { private MimeMessage createSimpleMail(BasicEmailParamDTO basicEmailParamDTO, String fileName, InputStreamSource inputStreamSource) throws MessagingException {
// 创建邮件对象 // 创建邮件对象
MimeMessage message = javaMailSender.createMimeMessage(); MimeMessage message = javaMailSender.createMimeMessage();
// 使用 MimeMessageHelper 简化邮件内容和附件的设置 // 使用 MimeMessageHelper 简化邮件内容和附件的设置
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(message, true); MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(message, true);
// 设置发件人 // 设置发件人
mimeMessageHelper.setFrom(new InternetAddress(basicEmailParamDTO.getSenderUserMail())); mimeMessageHelper.setFrom(new InternetAddress(basicEmailParamDTO.getSenderUserMail()));
// 设置收件人 // 设置收件人
mimeMessageHelper.setTo(basicEmailParamDTO.getMailTo()); mimeMessageHelper.setTo(basicEmailParamDTO.getMailTo());
// 设置抄送人 // 设置抄送人
if (basicEmailParamDTO.getCc() != null && basicEmailParamDTO.getCc().length > 0) { if (basicEmailParamDTO.getCc() != null && basicEmailParamDTO.getCc().length > 0) {
mimeMessageHelper.setCc(basicEmailParamDTO.getCc()); mimeMessageHelper.setCc(basicEmailParamDTO.getCc());
} }
// 设置暗送人 // 设置暗送人
if (basicEmailParamDTO.getBcc() != null && basicEmailParamDTO.getBcc().length > 0) { if (basicEmailParamDTO.getBcc() != null && basicEmailParamDTO.getBcc().length > 0) {
mimeMessageHelper.setBcc(basicEmailParamDTO.getBcc()); mimeMessageHelper.setBcc(basicEmailParamDTO.getBcc());
} }
// 设置邮件主题 // 设置邮件主题
mimeMessageHelper.setSubject(basicEmailParamDTO.getSubject()); mimeMessageHelper.setSubject(basicEmailParamDTO.getSubject());
// 设置邮件内容HTML 格式) // 设置邮件内容HTML 格式)
mimeMessageHelper.setText(basicEmailParamDTO.getContent(), true); mimeMessageHelper.setText(basicEmailParamDTO.getContent(), true);
// 设置附件 // 设置附件
if (inputStreamSource != null) { if (inputStreamSource != null) {
mimeMessageHelper.addAttachment(fileName, inputStreamSource); mimeMessageHelper.addAttachment(fileName, inputStreamSource);
} }
return message; return message;
} }
/** /**
* 设置实体参数 * 设置实体参数
* *
* @param mailTo 接收邮件的邮箱地址 * @param mailTo 接收邮件的邮箱地址
* @param jsonObject 模板中变量的值 * @param jsonObject 模板中变量的值
* @return 返回一个MailEntity * @return 返回一个MailEntity
* @throws AddressException 邮箱地址值异常 * @throws AddressException 邮箱地址值异常
*/ */
public BasicEmailParamDTO setBasicEmailParams(List<String> mailTo, JSONObject jsonObject, String templatePath, String title) throws AddressException { public BasicEmailParamDTO setBasicEmailParams(List<String> mailTo, JSONObject jsonObject, String templatePath, String title) throws AddressException {
BasicEmailParamDTO basicEmailParamDTO = new BasicEmailParamDTO(); BasicEmailParamDTO basicEmailParamDTO = new BasicEmailParamDTO();
basicEmailParamDTO.setSenderUserMail("info@aida.com.hk"); basicEmailParamDTO.setSenderUserMail("info@aida.com.hk");
basicEmailParamDTO.setMailTo(getInternetAddressList(mailTo)); basicEmailParamDTO.setMailTo(getInternetAddressList(mailTo));
basicEmailParamDTO.setSubject(title); basicEmailParamDTO.setSubject(title);
// todo 邮件模板不存在的报错与重试机制 // todo 邮件模板不存在的报错与重试机制
basicEmailParamDTO.setContent(setContent(jsonObject, templatePath)); basicEmailParamDTO.setContent(setContent(jsonObject, templatePath));
return basicEmailParamDTO; return basicEmailParamDTO;
} }
/** public BasicEmailParamDTO setBasicEmailParams(List<String> mailTo, String title) throws AddressException {
* 将地址转换为InternetAddress类型 BasicEmailParamDTO basicEmailParamDTO = new BasicEmailParamDTO();
* basicEmailParamDTO.setSenderUserMail("info@aida.com.hk");
* @param addressList 普通的地址字符串列表 basicEmailParamDTO.setMailTo(getInternetAddressList(mailTo));
* @return InternetAddress类型的地址列表 basicEmailParamDTO.setSubject(title);
* @throws AddressException 地址异常 return basicEmailParamDTO;
*/ }
public InternetAddress[] getInternetAddressList(List<String> addressList) throws AddressException {
InternetAddress[] toAddress = new InternetAddress[addressList.size()]; /**
for (String address : addressList) { * 将地址转换为InternetAddress类型
toAddress[addressList.indexOf(address)] = new InternetAddress(address); *
} * @param addressList 普通的地址字符串列表
return toAddress; * @return InternetAddress类型的地址列表
} * @throws AddressException 地址异常
*/
public String setContent(JSONObject jsonObject, String templatePath) { public InternetAddress[] getInternetAddressList(List<String> addressList) throws AddressException {
Context context = new Context(); InternetAddress[] toAddress = new InternetAddress[addressList.size()];
if (Objects.nonNull(jsonObject)) { for (String address : addressList) {
for (String key : jsonObject.keySet()) { toAddress[addressList.indexOf(address)] = new InternetAddress(address);
context.setVariable(key, jsonObject.get(key)); }
} return toAddress;
} }
return templateEngine.process(templatePath, context);
} public String setContent(JSONObject jsonObject, String templatePath) {
Context context = new Context();
} if (Objects.nonNull(jsonObject)) {
for (String key : jsonObject.keySet()) {
context.setVariable(key, jsonObject.get(key));
}
}
return templateEngine.process(templatePath, context);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -426,45 +426,7 @@ public class SendEmailUtil {
} }
} }
private final static Long GENERATE_EXCEPTION_WARNING_ID = 122589L; // todo ?需要保留吗
public static void sendGenerateExceptionWarning(String message) {
try {
// 实例化一个认证对象
Credential cred = new Credential(SECRET_ID, SECRET_KEy);
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("ses.tencentcloudapi.com");
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
SesClient client = new SesClient(cred, "ap-hongkong", clientProfile);
SendEmailRequest req = new SendEmailRequest();
req.setFromEmailAddress(CODE_CREATE_SEND_ADDRESS);
req.setDestination(new String[]{"xupei3360@163.com"});
// 根据邮件类型设置不同的主题和模板
String subject = "";
Template template = new Template();
subject = "Warning: AiDA 3.0 Generate Exception Warning";
template.setTemplateID(GENERATE_EXCEPTION_WARNING_ID);
JSONObject parameter = new JSONObject();
parameter.put("errorMessage", message);
parameter.put("time", DateUtil.dateToStr(new Date(), DateUtil.YYYY_MM_DD_HH_MM_SS));
template.setTemplateData(parameter.toJSONString());
req.setSubject(subject);
req.setTemplate(template);
// 发送邮件
SendEmailResponse resp = client.SendEmail(req);
log.info("短信发送结果res###{}", SendEmailResponse.toJsonString(resp));
} catch (TencentCloudSDKException e) {
log.info("邮件发送失败###{}", e.toString());
throw new BusinessException("failed.to.send.mail");
}
}
private final static Long QUESTIONNAIRE_FEEDBACK_EN_ID = 124151L; private final static Long QUESTIONNAIRE_FEEDBACK_EN_ID = 124151L;
private final static Long QUESTIONNAIRE_FEEDBACK_CN_ID = 124156L; private final static Long QUESTIONNAIRE_FEEDBACK_CN_ID = 124156L;
@@ -606,6 +568,7 @@ public class SendEmailUtil {
} }
} }
// todo 目前该定时器已取消,是否需要保留该模板?
private final static Long NEW_USER_REGISTER_NOTIFICATION_EN = 126919L; private final static Long NEW_USER_REGISTER_NOTIFICATION_EN = 126919L;
public static void notificationForRegisterUser(String receiverAddress) { public static void notificationForRegisterUser(String receiverAddress) {
@@ -644,51 +607,6 @@ public class SendEmailUtil {
} }
} }
private final static Long CHANGE_MAILBOX_CONFIRM_CN = 128278L;
private final static Long CHANGE_MAILBOX_CONFIRM_EN = 128277L;
public static void changeMailboxConfirm(String receiverAddress, String language, String name, String link) {
try {
// 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey此处还需注意密钥对的保密
// 代码泄露可能会导致 SecretId 和 SecretKey 泄露并威胁账号下所有资源的安全性。以下代码示例仅供参考建议采用更安全的方式来使用密钥请参见https://cloud.tencent.com/document/product/1278/85305
// 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
Credential cred = new Credential(SECRET_ID, SECRET_KEy);
// 实例化一个http选项可选的没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("ses.tencentcloudapi.com");
// 实例化一个client选项可选的没有特殊需求可以跳过
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
// 实例化要请求产品的client对象,clientProfile是可选的
SesClient client = new SesClient(cred, "ap-hongkong", clientProfile);
// 实例化一个请求对象,每个接口都会对应一个request对象
SendEmailRequest req = new SendEmailRequest();
req.setFromEmailAddress(SEND_ADDRESS);
req.setDestination(new String[]{receiverAddress});
Template template = new Template();
if (language.equals("ENGLISH")) {
req.setSubject("Change the email address bound to the AiDA account");
template.setTemplateID(CHANGE_MAILBOX_CONFIRM_EN);
} else {
req.setSubject("更换AiDA账号绑定的邮箱地址");
template.setTemplateID(CHANGE_MAILBOX_CONFIRM_CN);
}
JSONObject param = new JSONObject();
param.put("userName", name);
param.put("link", link);
template.setTemplateData(param.toJSONString());
req.setTemplate(template);
// 返回的resp是一个SendEmailResponse的实例与请求对象对应
SendEmailResponse resp = client.SendEmail(req);
log.info("短信发送结果res###{}", SendEmailResponse.toJsonString(resp));
} catch (TencentCloudSDKException e) {
log.info("邮件发送失败###{}", e.toString());
throw new BusinessException("failed.to.send.mail");
}
}
private final static Long UPLOAD_TIMEOUT_REMINDER = 128324L; private final static Long UPLOAD_TIMEOUT_REMINDER = 128324L;
public static void uploadTimeoutReminder(String userName, String time) { public static void uploadTimeoutReminder(String userName, String time) {
@@ -732,7 +650,7 @@ public class SendEmailUtil {
} }
} }
private final static Long HALFPRICEPROMOTION_CN_ID = 128582L; /*private final static Long HALFPRICEPROMOTION_CN_ID = 128582L;
private final static Long HALFPRICEPROMOTION_EN_ID = 128583L; private final static Long HALFPRICEPROMOTION_EN_ID = 128583L;
public static void halfPricePromotion(Account account, String senderAddress, int type) { public static void halfPricePromotion(Account account, String senderAddress, int type) {
@@ -821,7 +739,7 @@ public class SendEmailUtil {
log.info("邮件发送失败###{}", e.toString()); log.info("邮件发送失败###{}", e.toString());
throw new BusinessException("failed.to.send.mail"); throw new BusinessException("failed.to.send.mail");
} }
} }*/
private final static Long CANCEL_MERCHANT_EN = 130720L; private final static Long CANCEL_MERCHANT_EN = 130720L;
// private final static Long NEW_MERCHANT_EN = 130721L; // private final static Long NEW_MERCHANT_EN = 130721L;
@@ -1074,4 +992,52 @@ public class SendEmailUtil {
} }
} }
private final static Long CN_2025_618 = 141425L;
private final static Long EN_2025_618 = 141424L;
public static void send618PromotionEmailTemp(String receiver, String language){
try {
// 实例化一个认证对象
Credential cred = new Credential(SECRET_ID, SECRET_KEy);
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("ses.tencentcloudapi.com");
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
SesClient client = new SesClient(cred, "ap-hongkong", clientProfile);
SendEmailRequest req = new SendEmailRequest();
req.setFromEmailAddress(CODE_CREATE_SEND_ADDRESS);
req.setDestination(new String[]{receiver});
// 根据邮件类型设置不同的主题和模板
String subject = "";
Template template = new Template();
// if (type == 1) {
// subject = "Upcoming System Upgrade for AiDA 3.0";
// template.setTemplateID(UPGRADE_NOTIFICATION_ID);
// }else {
// subject = "即将到来的AiDA 3.0系统升级";
// template.setTemplateID(UPGRADE_NOTIFICATION_ID_CHINESE);
// }
if (language.equals("ENGLISH")) {
subject = "Welcome back Subscribe AiDA with the discount code to enjoy 50% OFF!";
template.setTemplateID(EN_2025_618);
}else {
subject = "设计时速狂飙AiDA 618半价让灵感永不限流";
template.setTemplateID(CN_2025_618);
}
req.setSubject(subject);
req.setTemplate(template);
// 发送邮件
SendEmailResponse resp = client.SendEmail(req);
log.info("邮件发送成功,收件人地址:{}", receiver);
log.info("短信发送结果res###{}", SendEmailResponse.toJsonString(resp));
} catch (TencentCloudSDKException e) {
log.info(receiver);
log.error("邮件发送失败###{},收件人地址:{}", e.toString(), receiver);
}
}
} }

View File

@@ -1,138 +1,145 @@
package com.ai.da.service; package com.ai.da.service;
import com.ai.da.mapper.primary.entity.Account; import com.ai.da.mapper.primary.entity.Account;
import com.ai.da.mapper.primary.entity.TrialOrder; import com.ai.da.mapper.primary.entity.TrialOrder;
import com.ai.da.model.dto.AffiliateEmailParamsDTO; import com.ai.da.model.dto.AffiliateEmailParamsDTO;
import com.ai.da.model.dto.BasicEmailParamDTO; import com.ai.da.model.dto.BasicEmailParamDTO;
import com.ai.da.model.dto.SubscriptionEmailParamsDTO; import com.ai.da.model.dto.SubscriptionEmailParamsDTO;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import org.springframework.core.io.InputStreamSource; import org.springframework.core.io.InputStreamSource;
import java.util.List; import java.util.List;
public interface EmailService { public interface EmailService {
String FAILED = "failed";
void loadSingleEmailTemplate(String templatePath); String DELIVERED = "delivered";
String RETRYING = "retrying";
void loadTemplatesFromResources(String resourcesPath);
void loadSingleEmailTemplate(String templatePath);
/**
* 发邮件 void loadTemplatesFromResources(String resourcesPath);
*
* @param mailTo 收件人邮箱 /**
* @param jsonObject 动态邮件模板参数 * 发邮件
* @param templateName 邮件模板名(只有文件名且需要带文件后缀) *
* @param title 邮件标题 * @param mailTo 收件人邮箱
* @param fileName 附件文件名。没有附件置为null * @param jsonObject 动态邮件模板参数
* @param inputStreamSource 附件文件数据。没有附件置为null * @param templateName 邮件模板名(只有文件名且需要带文件后缀)
*/ * @param title 邮件标题
* @param fileName 附件文件名。没有附件置为null
void sendEmail(List<String> mailTo, JSONObject jsonObject, String templateName, String title, String fileName, InputStreamSource inputStreamSource); * @param inputStreamSource 附件文件数据。没有附件置为null
*/
/**
* 适用于 需要自定义发件人信息 void sendEmail(List<String> mailTo, JSONObject jsonObject, String templateName, String title, String fileName, InputStreamSource inputStreamSource);
*
* @param jsonObject 模板参数 void updateRetryCount(Long logId, int retryCount);
* @param basicEmailParamDTO 包含发件人信息、邮件标题、收件人信息
* @param templateName 使用的模板文件名 void updateStatus(Long logId, String status);
* @param fileName 附件文件名
* @param inputStreamSource 附件文件信息 /**
*/ * 适用于 需要自定义发件人信息
*
void sendEmail(JSONObject jsonObject, BasicEmailParamDTO basicEmailParamDTO, String templateName, String fileName, InputStreamSource inputStreamSource); * @param jsonObject 模板参数
* @param basicEmailParamDTO 包含发件人信息、邮件标题、收件人信息
// 登入模板id * @param templateName 使用的模板文件名
String LOGIN_TEMPLATE_ID = "58020_login_aida_en.html"; * @param fileName 附件文件名
// 修改密码模板id * @param inputStreamSource 附件文件信息
String UPDATE_PWD_TEMPLATE_ID = "58022_update_password.html"; */
// 异常ip模板id
String EXCEPTION_ID_TEMPLATE_ID = "58021_exception_ip.html"; void sendEmail(JSONObject jsonObject, BasicEmailParamDTO basicEmailParamDTO, String templateName, String fileName, InputStreamSource inputStreamSource);
// 绑定邮箱模板id
String BIND_MAILBOX_TEMPLATE_ID = "132754_绑定邮箱.html"; // 登入模板id
// 更换绑定邮箱 String LOGIN_TEMPLATE_ID = "58020_login_aida_en.html";
String CHANGE_MAILBOX_TEMPLATE_ID = "128210_change_mailbox_en.html"; // 修改密码模板id
String UPDATE_PWD_TEMPLATE_ID = "58022_update_password.html";
/** // 异常ip模板id
* 发送登录相关的邮件 String EXCEPTION_ID_TEMPLATE_ID = "58021_exception_ip.html";
* // 绑定邮箱模板id
* @param receiverAddress 收件地址 String BIND_MAILBOX_TEMPLATE_ID = "132754_绑定邮箱.html";
* @param ip 请求ip // 更换绑定邮箱
* @param templateId 模板ID名 String CHANGE_MAILBOX_TEMPLATE_ID = "128210_change_mailbox_en.html";
* @param verifyCode 验证码
*/ /**
Boolean send(String receiverAddress, String ip, String templateId, String verifyCode); * 发送登录相关的邮件
*
/** * @param receiverAddress 收件地址
* 发送试用订单相关的邮件 * @param ip 请求ip
* * @param templateId 模板ID名
* @param receiverAddress 收件人邮箱地址 * @param verifyCode 验证码
* @param trialOrder 试用订单相关参数 */
* @param emailType 邮件类型1 - 提交试用请求2 - 审批通过3 - 试用请求通过通知 Boolean send(String receiverAddress, String ip, String templateId, String verifyCode);
* @param country 通过城市判断邮件模板的语言
* @param link /**
*/ * 发送试用订单相关的邮件
void sendCustomEmail(String receiverAddress, TrialOrder trialOrder, int emailType, String country, Boolean link); *
* @param receiverAddress 收件人邮箱地址
/** * @param trialOrder 试用订单相关参数
* 发送昨日的试用订单用户数据 * @param emailType 邮件类型1 - 提交试用请求2 - 审批通过3 - 试用请求通过通知
* * @param country 通过城市判断邮件模板的语言
* @param receiverAddress 收件人地址 * @param link
* @param fileName 附件文件名 */
* @param inputStreamSource 附件文件数据 void sendCustomEmail(String receiverAddress, TrialOrder trialOrder, int emailType, String country, Boolean link);
*/
void sendExcelEmail(List<String> receiverAddress, String fileName, InputStreamSource inputStreamSource); /**
* 发送昨日的试用订单用户数据
/** *
* 发送昨日的试用订单用户数据--无试用订单情况 * @param receiverAddress 收件人地址
* * @param fileName 附件文件名
* @param receiverAddress 收件人地址 * @param inputStreamSource 附件文件数据
*/ */
void sendNoExcelEmail(List<String> receiverAddress); void sendExcelEmail(List<String> receiverAddress, String fileName, InputStreamSource inputStreamSource);
/** /**
* 向账号快要到期的用户发送提醒邮件 * 发送昨日的试用订单用户数据--无试用订单情况
* *
* @param account 账号信息 * @param receiverAddress 收件人地址
*/ */
void sendWillBeExpiredEmail(Account account); void sendNoExcelEmail(List<String> receiverAddress);
/** /**
* 发送系统升级通知邮件 * 向账号快要到期的用户发送提醒邮件
* *
* @param account 用户信息 * @param account 账号信息
* @param senderAddress 发件人邮件 */
* @param type 邮件类型 void sendWillBeExpiredEmail(Account account);
*/
void sendUpgradeNotification(Account account, String senderAddress, Integer type); /**
* 发送系统升级通知邮件
/** *
* 通知在Code_Create上付费的用户AiDA账号的更新 * @param account 用户信息
* * @param senderAddress 发件人邮件
* @param receiverAddress 收件人地址 * @param type 邮件类型
* @param emailType 邮件类型 */
* @param country 国家(确定发送邮件的语言) void sendUpgradeNotification(Account account, String senderAddress, Integer type);
* @param userName 用户名
* @param date 账号到期时间 /**
*/ * 通知在Code_Create上付费的用户AiDA账号的更新
void notificationForPaidUser(String receiverAddress, int emailType, String country, String userName, String date); *
* @param receiverAddress 收件人地址
/** * @param emailType 邮件类型
* 广场用户注册通知邮件 * @param country 国家(确定发送邮件的语言)
* * @param userName 用户名
* @param userEmail * @param date 账号到期时间
* @param randomVerifyCode */
* @return void notificationForPaidUser(String receiverAddress, int emailType, String country, String userName, String date);
*/
Boolean designWorksRegister(String userEmail, String randomVerifyCode); /**
* 广场用户注册通知邮件
void uploadTimeoutReminder(String userName, String time); *
* @param userEmail
boolean subscriptionEmailReminder(String type, SubscriptionEmailParamsDTO subscriptionEmailParamsDTO, String language, String receiverAddress); * @param randomVerifyCode
* @return
void affiliateEmailReminder(List<String> receiverAddress, AffiliateEmailParamsDTO paramsDTO, String type); */
Boolean designWorksRegister(String userEmail, String randomVerifyCode);
void creditsPurchaseReminder(String username, String quantity, String amount);
void uploadTimeoutReminder(String userName, String time);
void commonExceptionReminder(String functionName, List<String> destination);
} boolean subscriptionEmailReminder(String type, SubscriptionEmailParamsDTO subscriptionEmailParamsDTO, String language, String receiverAddress);
void affiliateEmailReminder(List<String> receiverAddress, AffiliateEmailParamsDTO paramsDTO, String type);
void creditsPurchaseReminder(String username, String quantity, String amount);
void commonExceptionReminder(String functionName, List<String> destination);
}

View File

@@ -1,388 +1,394 @@
package com.ai.da.service.impl; package com.ai.da.service.impl;
import com.ai.da.common.config.exception.BusinessException; import com.ai.da.common.config.exception.BusinessException;
import com.ai.da.common.constant.CommonConstant; import com.ai.da.common.constant.CommonConstant;
import com.ai.da.common.context.UserContext; import com.ai.da.common.context.UserContext;
import com.ai.da.common.response.PageBaseResponse; import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.common.response.ResultEnum; import com.ai.da.common.response.ResultEnum;
import com.ai.da.common.utils.CopyUtil; import com.ai.da.common.utils.CopyUtil;
import com.ai.da.common.utils.RedisUtil; import com.ai.da.common.utils.RedisUtil;
import com.ai.da.common.utils.SendEmailUtil; import com.ai.da.common.utils.SendEmailUtil;
import com.ai.da.mapper.primary.AffiliateIncomeMapper; import com.ai.da.mapper.primary.AffiliateIncomeMapper;
import com.ai.da.mapper.primary.AffiliateMapper; import com.ai.da.mapper.primary.AffiliateMapper;
import com.ai.da.mapper.primary.ProductCouponsMapper; import com.ai.da.mapper.primary.ProductCouponsMapper;
import com.ai.da.mapper.primary.SubscriptionInfoMapper; import com.ai.da.mapper.primary.SubscriptionInfoMapper;
import com.ai.da.mapper.primary.entity.*; import com.ai.da.mapper.primary.entity.*;
import com.ai.da.model.dto.AffiliateEmailParamsDTO; import com.ai.da.model.dto.AffiliateEmailParamsDTO;
import com.ai.da.model.dto.AffiliateQueryDTO; import com.ai.da.model.dto.AffiliateQueryDTO;
import com.ai.da.model.vo.AffiliateInvitationDetailsVO; import com.ai.da.model.vo.AffiliateInvitationDetailsVO;
import com.ai.da.model.vo.AffiliateVO; import com.ai.da.model.vo.AffiliateVO;
import com.ai.da.model.vo.AuthPrincipalVo; import com.ai.da.model.vo.AuthPrincipalVo;
import com.ai.da.service.*; import com.ai.da.service.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mysql.cj.util.StringUtils; import com.mysql.cj.util.StringUtils;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
@Service @Service
@Slf4j @Slf4j
public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate> implements AffiliateService { public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate> implements AffiliateService {
@Resource @Resource
private OrderInfoService orderInfoService; private OrderInfoService orderInfoService;
@Resource @Resource
private AccountService accountService; private AccountService accountService;
@Resource @Resource
private PaymentInfoService paymentInfoService; private PaymentInfoService paymentInfoService;
@Resource @Resource
private SubscriptionInfoMapper subscriptionInfoMapper; private SubscriptionInfoMapper subscriptionInfoMapper;
@Resource @Resource
private AffiliateIncomeMapper affiliateIncomeMapper; private AffiliateIncomeMapper affiliateIncomeMapper;
@Resource @Resource
private StripeService stripeService; private StripeService stripeService;
@Resource @Resource
private ProductCouponsMapper productCouponsMapper; private ProductCouponsMapper productCouponsMapper;
@Resource @Resource
private RedisUtil redisUtil; private RedisUtil redisUtil;
@Resource
// 推广者注册 private EmailService emailService;
public Boolean registerAsAnAffiliate(String promotionMethod){
AuthPrincipalVo userHolder = UserContext.getUserHolder(); // 推广者注册
// 判断该用户是否已注册 public Boolean registerAsAnAffiliate(String promotionMethod){
QueryWrapper<Affiliate> qw = new QueryWrapper<>(); AuthPrincipalVo userHolder = UserContext.getUserHolder();
qw.eq("account_id", userHolder.getId()); // 判断该用户是否已注册
Affiliate affiliate = baseMapper.selectOne(qw); QueryWrapper<Affiliate> qw = new QueryWrapper<>();
if (Objects.isNull(affiliate)){ qw.eq("account_id", userHolder.getId());
affiliate = new Affiliate(); Affiliate affiliate = baseMapper.selectOne(qw);
affiliate.setAccountId(userHolder.getId()); if (Objects.isNull(affiliate)){
affiliate.setStatus("Pending"); affiliate = new Affiliate();
affiliate.setCreateTime(LocalDateTime.now()); affiliate.setAccountId(userHolder.getId());
affiliate.setPromotionMethod(promotionMethod); affiliate.setStatus("Pending");
baseMapper.insert(affiliate); affiliate.setCreateTime(LocalDateTime.now());
// 邮件通知审批者 affiliate.setPromotionMethod(promotionMethod);
String merchantEmail = "kimwong@code-create.com.hk"; baseMapper.insert(affiliate);
String developer = "xupei3360@163.com"; // 邮件通知审批者
String[] receiverEmail = {merchantEmail, developer}; String merchantEmail = "kimwong@code-create.com.hk";
SendEmailUtil.affiliateEmailReminder(receiverEmail, new AffiliateEmailParamsDTO(userHolder.getUsername(), promotionMethod), "new"); String developer = "xupei3360@163.com";
}else { String[] receiverEmail = {merchantEmail, developer};
throw new BusinessException("You have registered an Affiliate", ResultEnum.PROMPT.getCode()); SendEmailUtil.affiliateEmailReminder(receiverEmail, new AffiliateEmailParamsDTO(userHolder.getUsername(), promotionMethod), "new");
} // emailService.affiliateEmailReminder(Arrays.asList(/*merchantEmail,*/ developer), new AffiliateEmailParamsDTO(userHolder.getUsername(), promotionMethod), "new");
return true; }else {
} throw new BusinessException("You have registered an Affiliate", ResultEnum.PROMPT.getCode());
}
public PageBaseResponse<AffiliateVO> getAffiliateList(AffiliateQueryDTO affiliateQueryDTO){ return true;
log.info("parameter => {}", affiliateQueryDTO.toString()); }
int offset = (affiliateQueryDTO.getPage() - 1) * affiliateQueryDTO.getSize(); public PageBaseResponse<AffiliateVO> getAffiliateList(AffiliateQueryDTO affiliateQueryDTO){
List<AffiliateVO> affiliateList = baseMapper.getAffiliateList(affiliateQueryDTO.getStatus(), log.info("parameter => {}", affiliateQueryDTO.toString());
affiliateQueryDTO.getStartTime(),
affiliateQueryDTO.getEndTime(), int offset = (affiliateQueryDTO.getPage() - 1) * affiliateQueryDTO.getSize();
affiliateQueryDTO.getOrder(), List<AffiliateVO> affiliateList = baseMapper.getAffiliateList(affiliateQueryDTO.getStatus(),
affiliateQueryDTO.getAffiliateId(), affiliateQueryDTO.getStartTime(),
affiliateQueryDTO.getSize(), affiliateQueryDTO.getEndTime(),
offset affiliateQueryDTO.getOrder(),
); affiliateQueryDTO.getAffiliateId(),
if (CollectionUtils.isEmpty(affiliateList)) { affiliateQueryDTO.getSize(),
return PageBaseResponse.success(new Page<>()); offset
}else { );
int totalCount = baseMapper.queryAffiliateTotalCount(affiliateQueryDTO.getStatus(), if (CollectionUtils.isEmpty(affiliateList)) {
affiliateQueryDTO.getStartTime(), return PageBaseResponse.success(new Page<>());
affiliateQueryDTO.getEndTime(), }else {
affiliateQueryDTO.getAffiliateId() int totalCount = baseMapper.queryAffiliateTotalCount(affiliateQueryDTO.getStatus(),
); affiliateQueryDTO.getStartTime(),
IPage<AffiliateVO> orderListVOIPage = new Page<>(); affiliateQueryDTO.getEndTime(),
Integer size = affiliateQueryDTO.getSize(); affiliateQueryDTO.getAffiliateId()
orderListVOIPage.setSize(size); );
orderListVOIPage.setRecords(affiliateList); IPage<AffiliateVO> orderListVOIPage = new Page<>();
orderListVOIPage.setCurrent(affiliateQueryDTO.getPage()); Integer size = affiliateQueryDTO.getSize();
orderListVOIPage.setPages((long)Math.ceil((double) totalCount / size)); orderListVOIPage.setSize(size);
orderListVOIPage.setTotal(totalCount); orderListVOIPage.setRecords(affiliateList);
return PageBaseResponse.success(orderListVOIPage); orderListVOIPage.setCurrent(affiliateQueryDTO.getPage());
} orderListVOIPage.setPages((long)Math.ceil((double) totalCount / size));
/*QueryWrapper<Affiliate> qw = new QueryWrapper<>(); orderListVOIPage.setTotal(totalCount);
qw.eq(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getStatus()), "status", affiliateQueryDTO.getStatus()) return PageBaseResponse.success(orderListVOIPage);
.gt(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getStartTime()), "create_time", affiliateQueryDTO.getStartTime()) }
.lt(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getEndTime()), "create_time", affiliateQueryDTO.getEndTime()) /*QueryWrapper<Affiliate> qw = new QueryWrapper<>();
.eq(!Objects.isNull(affiliateQueryDTO.getAffiliateId()), "id", affiliateQueryDTO.getAffiliateId()) qw.eq(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getStatus()), "status", affiliateQueryDTO.getStatus())
.orderByDesc(affiliateQueryDTO.getOrder().equals("DESC"), "create_time"); .gt(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getStartTime()), "create_time", affiliateQueryDTO.getStartTime())
Page<Affiliate> affiliatePage = baseMapper.selectPage(new Page<>(affiliateQueryDTO.getPage(), affiliateQueryDTO.getSize()), qw); .lt(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getEndTime()), "create_time", affiliateQueryDTO.getEndTime())
affiliatePage.convert((Function<Affiliate, AffiliateVO>) affiliate-> { .eq(!Objects.isNull(affiliateQueryDTO.getAffiliateId()), "id", affiliateQueryDTO.getAffiliateId())
AffiliateVO affiliateVO = CopyUtil.copyObject(affiliate, AffiliateVO.class); .orderByDesc(affiliateQueryDTO.getOrder().equals("DESC"), "create_time");
affiliateVO.setUsername(); Page<Affiliate> affiliatePage = baseMapper.selectPage(new Page<>(affiliateQueryDTO.getPage(), affiliateQueryDTO.getSize()), qw);
}); affiliatePage.convert((Function<Affiliate, AffiliateVO>) affiliate-> {
return affiliatePage;*/ AffiliateVO affiliateVO = CopyUtil.copyObject(affiliate, AffiliateVO.class);
} affiliateVO.setUsername();
});
public AffiliateVO personalAffiliateCenter(){ return affiliatePage;*/
QueryWrapper<Affiliate> qw = new QueryWrapper<>(); }
Long accountId = UserContext.getUserHolder().getId();
qw.eq("account_id", accountId); public AffiliateVO personalAffiliateCenter(){
Affiliate affiliate = baseMapper.selectOne(qw); QueryWrapper<Affiliate> qw = new QueryWrapper<>();
AffiliateVO affiliateVO = CopyUtil.copyObject(affiliate, AffiliateVO.class); Long accountId = UserContext.getUserHolder().getId();
affiliateVO.setLinkViewCount(getAffiliateLinkViewCount(affiliate.getId())); qw.eq("account_id", accountId);
return affiliateVO; Affiliate affiliate = baseMapper.selectOne(qw);
} AffiliateVO affiliateVO = CopyUtil.copyObject(affiliate, AffiliateVO.class);
affiliateVO.setLinkViewCount(getAffiliateLinkViewCount(affiliate.getId()));
public double[] getPersonalMonthlyIncome(int year){ return affiliateVO;
Long accountId = UserContext.getUserHolder().getId(); }
List<Map<String, Object>> personalMonthlyIncome = affiliateIncomeMapper.getPersonalMonthlyIncome(accountId, year);
double[] commissions = new double[12]; public double[] getPersonalMonthlyIncome(int year){
personalMonthlyIncome.forEach(income -> { Long accountId = UserContext.getUserHolder().getId();
int month = Integer.parseInt(income.get("yearMonth").toString()); List<Map<String, Object>> personalMonthlyIncome = affiliateIncomeMapper.getPersonalMonthlyIncome(accountId, year);
commissions[month-1] = (double)income.get("totalCommission"); double[] commissions = new double[12];
}); personalMonthlyIncome.forEach(income -> {
int month = Integer.parseInt(income.get("yearMonth").toString());
return commissions; commissions[month-1] = (double)income.get("totalCommission");
} });
// 审批申请 return commissions;
public Boolean applicationApproval(Long id, Boolean isApproved, Float commission){ }
Affiliate affiliate = baseMapper.selectById(id);
// 审批申请
// 1、更新db状态 public Boolean applicationApproval(Long id, Boolean isApproved, Float commission){
if (isApproved){ Affiliate affiliate = baseMapper.selectById(id);
// 更新状态
affiliate.setStatus("Active"); // 1、更新db状态
affiliate.setApproved(true); if (isApproved){
affiliate.setLink(CommonConstant.AFFILIATE_LINK + affiliate.getId()); // 更新状态
if (Objects.isNull(commission)) { affiliate.setStatus("Active");
// 未设置佣金比例的情况下默认25% affiliate.setApproved(true);
affiliate.setCommissionPercent(25f); affiliate.setLink(CommonConstant.AFFILIATE_LINK + affiliate.getId());
} else { if (Objects.isNull(commission)) {
affiliate.setCommissionPercent(commission); // 未设置佣金比例的情况下默认25%
} affiliate.setCommissionPercent(25f);
} else { } else {
affiliate.setStatus("Refused"); affiliate.setCommissionPercent(commission);
affiliate.setApproved(false); }
} } else {
affiliate.setUpdateTime(LocalDateTime.now()); affiliate.setStatus("Refused");
baseMapper.updateById(affiliate); affiliate.setApproved(false);
}
// 2、将批准结果邮件通知用户 affiliate.setUpdateTime(LocalDateTime.now());
Account account = accountService.getById(affiliate.getAccountId()); baseMapper.updateById(affiliate);
String[] userEmail = {account.getUserEmail()};
String userName = account.getUserName(); // 2、将批准结果邮件通知用户
if (isApproved){ Account account = accountService.getById(affiliate.getAccountId());
SendEmailUtil.affiliateEmailReminder(userEmail, new AffiliateEmailParamsDTO(userName), "accepted"); String[] userEmail = {account.getUserEmail()};
}else { String userName = account.getUserName();
SendEmailUtil.affiliateEmailReminder(userEmail, new AffiliateEmailParamsDTO(userName), "refused"); if (isApproved){
} SendEmailUtil.affiliateEmailReminder(userEmail, new AffiliateEmailParamsDTO(userName), "accepted");
return true; // emailService.affiliateEmailReminder(Collections.singletonList(account.getUserEmail()), new AffiliateEmailParamsDTO(userName), "accepted");
} }else {
SendEmailUtil.affiliateEmailReminder(userEmail, new AffiliateEmailParamsDTO(userName), "refused");
public void updateCommissionPercentage(Long id, Float commission){ // emailService.affiliateEmailReminder(Collections.singletonList(account.getUserEmail()), new AffiliateEmailParamsDTO(userName), "refused");
Affiliate affiliate = baseMapper.selectById(id); }
if (Objects.isNull(affiliate)){ return true;
log.info("未知affiliate id :{}", id); }
throw new BusinessException("unknown affiliate");
} public void updateCommissionPercentage(Long id, Float commission){
if (!Objects.equals(affiliate.getCommissionPercent(), commission)){ Affiliate affiliate = baseMapper.selectById(id);
affiliate.setCommissionPercent(commission); if (Objects.isNull(affiliate)){
affiliate.setUpdateTime(LocalDateTime.now()); log.info("未知affiliate id :{}", id);
baseMapper.updateById(affiliate); throw new BusinessException("unknown affiliate");
} }
} if (!Objects.equals(affiliate.getCommissionPercent(), commission)){
affiliate.setCommissionPercent(commission);
// 定时计算佣金 affiliate.setUpdateTime(LocalDateTime.now());
public void updateAffiliateInfoWithPayment(){ baseMapper.updateById(affiliate);
// id存redis }
String lastTime = redisUtil.getFromString(RedisUtil.PAYMENT_INFO_LAST_SCAN_TIME); }
String currentTime = LocalDateTime.now().toString();
// 1、查上次更新之后有无新订单 // 定时计算佣金
QueryWrapper<PaymentInfo> queryWrapper = new QueryWrapper<>(); public void updateAffiliateInfoWithPayment(){
if (!StringUtil.isNullOrEmpty(lastTime)){ // id存redis
queryWrapper.gt("create_time", lastTime); String lastTime = redisUtil.getFromString(RedisUtil.PAYMENT_INFO_LAST_SCAN_TIME);
} String currentTime = LocalDateTime.now().toString();
queryWrapper.eq("type","new").eq("trade_state", "paid"); // 1、查上次更新之后有无新订单
QueryWrapper<PaymentInfo> queryWrapper = new QueryWrapper<>();
List<PaymentInfo> paymentInfos = paymentInfoService.getBaseMapper().selectList(queryWrapper); if (!StringUtil.isNullOrEmpty(lastTime)){
if (!paymentInfos.isEmpty()){ queryWrapper.gt("create_time", lastTime);
paymentInfos.forEach(paymentInfo -> { }
// 2、根据order_no查付款用户id queryWrapper.eq("type","new").eq("trade_state", "paid");
OrderInfo orderInfo = orderInfoService.getOrderByOrderNo(paymentInfo.getOrderNo());
if (Objects.isNull(orderInfo)){ List<PaymentInfo> paymentInfos = paymentInfoService.getBaseMapper().selectList(queryWrapper);
return; if (!paymentInfos.isEmpty()){
} paymentInfos.forEach(paymentInfo -> {
Long accountId = orderInfo.getAccountId(); // 2、根据order_no查付款用户id
// 3、查该用户之前是否有初次订阅的订单 OrderInfo orderInfo = orderInfoService.getOrderByOrderNo(paymentInfo.getOrderNo());
QueryWrapper<OrderInfo> qwOrderInfo = new QueryWrapper<>(); if (Objects.isNull(orderInfo)){
qwOrderInfo.eq("account_id", accountId).eq("is_first_subscription", 1); return;
List<OrderInfo> orderInfos = orderInfoService.getBaseMapper().selectList(qwOrderInfo); }
// 该用户首次订阅(非首次订阅,不分配佣金) Long accountId = orderInfo.getAccountId();
if (orderInfos.isEmpty()){ // 3、查该用户之前是否有初次订阅的订单
// 查询是否绑定affiliateId QueryWrapper<OrderInfo> qwOrderInfo = new QueryWrapper<>();
Account account = accountService.getById(accountId); qwOrderInfo.eq("account_id", accountId).eq("is_first_subscription", 1);
if (!Objects.isNull(account.getInvitationCode())){ List<OrderInfo> orderInfos = orderInfoService.getBaseMapper().selectList(qwOrderInfo);
log.info("结算订单id为{}的佣金", orderInfo.getId()); // 该用户首次订阅(非首次订阅,不分配佣金)
// 3、若有, 直接更新affiliate的所得 if (orderInfos.isEmpty()){
Affiliate affiliate = baseMapper.selectById(account.getInvitationCode()); // 查询是否绑定affiliateId
Float payerTotal = paymentInfo.getPayerTotal(); Account account = accountService.getById(accountId);
if (!Objects.isNull(account.getInvitationCode())){
if (payerTotal > 0){ log.info("结算订单id为{}的佣金", orderInfo.getId());
// 分配新用户首次订阅所付费用 预设的佣金比例 作为佣金 // 3、若有, 直接更新affiliate的所得
BigDecimal commission = BigDecimal.valueOf(payerTotal).multiply(BigDecimal.valueOf(affiliate.getCommissionPercent() / 100)); Affiliate affiliate = baseMapper.selectById(account.getInvitationCode());
BigDecimal monthlyEarning = BigDecimal.valueOf(affiliate.getMonthlyEarnings()).add(commission); Float payerTotal = paymentInfo.getPayerTotal();
BigDecimal unpaidEarnings = BigDecimal.valueOf(affiliate.getUnpaidEarnings()).add(commission);
int visits = affiliate.getVisits() + 1; if (payerTotal > 0){
affiliate.setMonthlyEarnings(monthlyEarning.floatValue()); // 分配新用户首次订阅所付费用 预设的佣金比例 作为佣金
affiliate.setUnpaidEarnings(unpaidEarnings.floatValue()); BigDecimal commission = BigDecimal.valueOf(payerTotal).multiply(BigDecimal.valueOf(affiliate.getCommissionPercent() / 100));
affiliate.setVisits(visits); BigDecimal monthlyEarning = BigDecimal.valueOf(affiliate.getMonthlyEarnings()).add(commission);
affiliate.setUpdateTime(LocalDateTime.now()); BigDecimal unpaidEarnings = BigDecimal.valueOf(affiliate.getUnpaidEarnings()).add(commission);
baseMapper.updateById(affiliate); int visits = affiliate.getVisits() + 1;
affiliate.setMonthlyEarnings(monthlyEarning.floatValue());
orderInfo.setIsCommissionCalculated((byte)1); affiliate.setUnpaidEarnings(unpaidEarnings.floatValue());
affiliate.setVisits(visits);
// 4、添加到t_affiliate_income affiliate.setUpdateTime(LocalDateTime.now());
AffiliateIncome affiliateIncome = new AffiliateIncome(); baseMapper.updateById(affiliate);
affiliateIncome.setAffiliateId(affiliate.getId());
affiliateIncome.setAffiliateAccountId(affiliate.getAccountId()); orderInfo.setIsCommissionCalculated((byte)1);
affiliateIncome.setInviteeAccountId(accountId);
affiliateIncome.setAmount(payerTotal); // 4、添加到t_affiliate_income
affiliateIncome.setPaymentInfoId(paymentInfo.getId()); AffiliateIncome affiliateIncome = new AffiliateIncome();
affiliateIncome.setPaymentTime(paymentInfo.getCreateTime()); affiliateIncome.setAffiliateId(affiliate.getId());
affiliateIncome.setCommission(commission.floatValue()); affiliateIncome.setAffiliateAccountId(affiliate.getAccountId());
affiliateIncome.setCreateTime(LocalDateTime.now()); affiliateIncome.setInviteeAccountId(accountId);
affiliateIncomeMapper.insert(affiliateIncome); affiliateIncome.setAmount(payerTotal);
} affiliateIncome.setPaymentInfoId(paymentInfo.getId());
} affiliateIncome.setPaymentTime(paymentInfo.getCreateTime());
orderInfo.setIsFirstSubscription((byte)1); affiliateIncome.setCommission(commission.floatValue());
orderInfo.setUpdateTime(LocalDateTime.now()); affiliateIncome.setCreateTime(LocalDateTime.now());
orderInfoService.updateById(orderInfo); affiliateIncomeMapper.insert(affiliateIncome);
} }
}); }
} orderInfo.setIsFirstSubscription((byte)1);
redisUtil.addToString(RedisUtil.PAYMENT_INFO_LAST_SCAN_TIME, currentTime); orderInfo.setUpdateTime(LocalDateTime.now());
} orderInfoService.updateById(orderInfo);
}
public Boolean affiliateLinkViewsIncrease(Long affiliateId) { });
redisUtil.increaseAffiliateLinkViewCount(affiliateId); }
return Boolean.TRUE; redisUtil.addToString(RedisUtil.PAYMENT_INFO_LAST_SCAN_TIME, currentTime);
} }
private Long getAffiliateLinkViewCount(Long affiliateId) { public Boolean affiliateLinkViewsIncrease(Long affiliateId) {
return redisUtil.getAffiliateLinkViewCount(affiliateId); redisUtil.increaseAffiliateLinkViewCount(affiliateId);
} return Boolean.TRUE;
}
// 查看每个affiliate带来收入的详情
@Override private Long getAffiliateLinkViewCount(Long affiliateId) {
public IPage<AffiliateInvitationDetailsVO> getEachAffiliateGeneratedRevenue(AffiliateQueryDTO affiliateQueryDTO) { return redisUtil.getAffiliateLinkViewCount(affiliateId);
if (Objects.isNull(affiliateQueryDTO.getAffiliateId())){ }
throw new BusinessException("Please specify the affiliate ID.", ResultEnum.PROMPT.getCode());
} // 查看每个affiliate带来收入的详情
@Override
QueryWrapper<AffiliateIncome> affiliateIncomeQueryWrapper = new QueryWrapper<>(); public IPage<AffiliateInvitationDetailsVO> getEachAffiliateGeneratedRevenue(AffiliateQueryDTO affiliateQueryDTO) {
affiliateIncomeQueryWrapper if (Objects.isNull(affiliateQueryDTO.getAffiliateId())){
.gt(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getStartTime()), "create_time", affiliateQueryDTO.getStartTime()) throw new BusinessException("Please specify the affiliate ID.", ResultEnum.PROMPT.getCode());
.lt(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getEndTime()), "create_time", affiliateQueryDTO.getEndTime()) }
.eq(!Objects.isNull(affiliateQueryDTO.getAffiliateId()), "affiliate_id", affiliateQueryDTO.getAffiliateId())
.orderByDesc(affiliateQueryDTO.getOrder().equals("DESC"), "create_time"); QueryWrapper<AffiliateIncome> affiliateIncomeQueryWrapper = new QueryWrapper<>();
IPage<AffiliateIncome> affiliateIncomePage = affiliateIncomeMapper.selectPage(new Page<>(affiliateQueryDTO.getPage(), affiliateQueryDTO.getSize()), affiliateIncomeQueryWrapper); affiliateIncomeQueryWrapper
return affiliateIncomePage.convert((Function<AffiliateIncome, AffiliateInvitationDetailsVO>) affiliateIncome -> { .gt(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getStartTime()), "create_time", affiliateQueryDTO.getStartTime())
AffiliateInvitationDetailsVO affiliateInvitationDetailsVO = CopyUtil.copyObject(affiliateIncome, AffiliateInvitationDetailsVO.class); .lt(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getEndTime()), "create_time", affiliateQueryDTO.getEndTime())
affiliateInvitationDetailsVO.setAccountId(affiliateIncome.getInviteeAccountId()); .eq(!Objects.isNull(affiliateQueryDTO.getAffiliateId()), "affiliate_id", affiliateQueryDTO.getAffiliateId())
affiliateInvitationDetailsVO.setUsername(accountService.getBaseMapper().selectById(affiliateIncome.getInviteeAccountId()).getUserName()); .orderByDesc(affiliateQueryDTO.getOrder().equals("DESC"), "create_time");
affiliateInvitationDetailsVO.setFirstSubscriptionPaymentAmount(affiliateIncome.getAmount()); IPage<AffiliateIncome> affiliateIncomePage = affiliateIncomeMapper.selectPage(new Page<>(affiliateQueryDTO.getPage(), affiliateQueryDTO.getSize()), affiliateIncomeQueryWrapper);
affiliateInvitationDetailsVO.setCommission(affiliateIncome.getCommission()); return affiliateIncomePage.convert((Function<AffiliateIncome, AffiliateInvitationDetailsVO>) affiliateIncome -> {
affiliateInvitationDetailsVO.setTime(affiliateIncome.getPaymentTime()); AffiliateInvitationDetailsVO affiliateInvitationDetailsVO = CopyUtil.copyObject(affiliateIncome, AffiliateInvitationDetailsVO.class);
return affiliateInvitationDetailsVO; affiliateInvitationDetailsVO.setAccountId(affiliateIncome.getInviteeAccountId());
}); affiliateInvitationDetailsVO.setUsername(accountService.getBaseMapper().selectById(affiliateIncome.getInviteeAccountId()).getUserName());
} affiliateInvitationDetailsVO.setFirstSubscriptionPaymentAmount(affiliateIncome.getAmount());
affiliateInvitationDetailsVO.setCommission(affiliateIncome.getCommission());
public void commissionCalculation(Integer year, Integer month) { affiliateInvitationDetailsVO.setTime(affiliateIncome.getPaymentTime());
if (Objects.isNull(year)) { return affiliateInvitationDetailsVO;
year = LocalDateTime.now().getYear(); });
} }
if (Objects.isNull(month)) {
month = LocalDateTime.now().getMonthValue(); public void commissionCalculation(Integer year, Integer month) {
} if (Objects.isNull(year)) {
year = LocalDateTime.now().getYear();
List<Map<String, Object>> monthlyAffiliateIncome = affiliateIncomeMapper.getMonthlyAffiliateIncome(year, month); }
// 1、总收入(近一个月通过affiliate产生的收入),未支付的金额 affiliate表中unpaid的总和 if (Objects.isNull(month)) {
Double totalAmount = 0.0; month = LocalDateTime.now().getMonthValue();
Double totalCommission = 0.0; }
if (!monthlyAffiliateIncome.isEmpty()){
Map<String, Object> monthlyIncome = monthlyAffiliateIncome.get(0); List<Map<String, Object>> monthlyAffiliateIncome = affiliateIncomeMapper.getMonthlyAffiliateIncome(year, month);
totalAmount = (Double) monthlyIncome.get("totalAmount"); // 1、总收入(近一个月通过affiliate产生的收入),未支付的金额 affiliate表中unpaid的总和
totalCommission = (Double) monthlyIncome.get("totalCommission"); Double totalAmount = 0.0;
} Double totalCommission = 0.0;
if (!monthlyAffiliateIncome.isEmpty()){
// 2、本月新注册的Affiliate Map<String, Object> monthlyIncome = monthlyAffiliateIncome.get(0);
Map<String, Long> monthlyApprovedAffiliate = baseMapper.getMonthlyApprovedAffiliate(year, month); totalAmount = (Double) monthlyIncome.get("totalAmount");
Long count = monthlyApprovedAffiliate.get("count"); totalCommission = (Double) monthlyIncome.get("totalCommission");
}
AffiliateEmailParamsDTO affiliateEmailParamsDTO = new AffiliateEmailParamsDTO();
affiliateEmailParamsDTO.setTotalProgramRevenue(totalAmount.toString()); // 2、本月新注册的Affiliate
affiliateEmailParamsDTO.setNewApprovedAffiliates(count.toString()); Map<String, Long> monthlyApprovedAffiliate = baseMapper.getMonthlyApprovedAffiliate(year, month);
affiliateEmailParamsDTO.setUnpaidEarnings(totalCommission.toString()); Long count = monthlyApprovedAffiliate.get("count");
affiliateEmailParamsDTO.setPaidEarnings("0");
AffiliateEmailParamsDTO affiliateEmailParamsDTO = new AffiliateEmailParamsDTO();
String merchantEmail = "kimwong@code-create.com.hk"; affiliateEmailParamsDTO.setTotalProgramRevenue(totalAmount.toString());
String developer = "xupei3360@163.com"; affiliateEmailParamsDTO.setNewApprovedAffiliates(count.toString());
String[] receiverEmail = {merchantEmail, developer}; affiliateEmailParamsDTO.setUnpaidEarnings(totalCommission.toString());
// 邮件通知 affiliateEmailParamsDTO.setPaidEarnings("0");
SendEmailUtil.affiliateEmailReminder(receiverEmail, affiliateEmailParamsDTO, "summary");
} String merchantEmail = "kimwong@code-create.com.hk";
String developer = "xupei3360@163.com";
@Override String[] receiverEmail = {merchantEmail, developer};
public Affiliate getByAccountId(Long accountId) { // 邮件通知
QueryWrapper<Affiliate> queryWrapper = new QueryWrapper<>(); SendEmailUtil.affiliateEmailReminder(receiverEmail, affiliateEmailParamsDTO, "summary");
queryWrapper.eq("account_id", accountId).orderByDesc("id").last("limit 1"); // emailService.affiliateEmailReminder(Arrays.asList(/*merchantEmail,*/ developer), affiliateEmailParamsDTO, "summary");
}
return baseMapper.selectOne(queryWrapper);
} @Override
public Affiliate getByAccountId(Long accountId) {
public void calcCouponsCommission(){ QueryWrapper<Affiliate> queryWrapper = new QueryWrapper<>();
// id存redis queryWrapper.eq("account_id", accountId).orderByDesc("id").last("limit 1");
String lastTime = redisUtil.getFromString(RedisUtil.PAYMENT_INFO_LAST_SCAN_TIME);
String currentTime = LocalDateTime.now().toString(); return baseMapper.selectOne(queryWrapper);
// 1、查上次更新之后有无使用了优惠券的新订单 }
QueryWrapper<PaymentInfo> queryWrapper = new QueryWrapper<>();
if (!StringUtil.isNullOrEmpty(lastTime)){ public void calcCouponsCommission(){
queryWrapper.gt("create_time", lastTime) // id存redis
.lt("create_time", currentTime) String lastTime = redisUtil.getFromString(RedisUtil.PAYMENT_INFO_LAST_SCAN_TIME);
.isNotNull("promotion_code"); String currentTime = LocalDateTime.now().toString();
} // 1、查上次更新之后有无使用了优惠券的新订单
List<PaymentInfo> paymentInfos = paymentInfoService.getBaseMapper().selectList(queryWrapper); QueryWrapper<PaymentInfo> queryWrapper = new QueryWrapper<>();
if (!StringUtil.isNullOrEmpty(lastTime)){
// key:推广码, value:用户支付的金额 queryWrapper.gt("create_time", lastTime)
HashMap<String, Float> codeAmount = new HashMap<>(); .lt("create_time", currentTime)
if (!paymentInfos.isEmpty()){ .isNotNull("promotion_code");
for (PaymentInfo paymentInfo : paymentInfos){ }
String promotionCode = paymentInfo.getPromotionCode(); List<PaymentInfo> paymentInfos = paymentInfoService.getBaseMapper().selectList(queryWrapper);
Float sum = codeAmount.get(promotionCode);
if (sum == null || sum == 0.0f){ // key:推广码, value:用户支付的金额
codeAmount.put(promotionCode, paymentInfo.getPayerTotal()); HashMap<String, Float> codeAmount = new HashMap<>();
}else { if (!paymentInfos.isEmpty()){
codeAmount.put(promotionCode, sum + paymentInfo.getPayerTotal()); for (PaymentInfo paymentInfo : paymentInfos){
} String promotionCode = paymentInfo.getPromotionCode();
} Float sum = codeAmount.get(promotionCode);
for (Map.Entry<String, Float> entry : codeAmount.entrySet()){ if (sum == null || sum == 0.0f){
String promotionCode = entry.getKey(); codeAmount.put(promotionCode, paymentInfo.getPayerTotal());
ProductCoupons productCoupons = stripeService.getProductCoupon(promotionCode, null); }else {
if (!Objects.isNull(productCoupons)){ codeAmount.put(promotionCode, sum + paymentInfo.getPayerTotal());
// 2、计算支付金额的总和更新totalEarningscommissionunpaidCommission }
float sum = productCoupons.getTotalEarnings() + entry.getValue(); }
productCoupons.setTotalEarnings(sum); for (Map.Entry<String, Float> entry : codeAmount.entrySet()){
float commission = sum * productCoupons.getCommissionRate() / 100; String promotionCode = entry.getKey();
productCoupons.setCommission(commission); ProductCoupons productCoupons = stripeService.getProductCoupon(promotionCode, null);
productCoupons.setUnpaidCommission(commission - productCoupons.getPaidCommission()); if (!Objects.isNull(productCoupons)){
productCouponsMapper.updateById(productCoupons); // 2、计算支付金额的总和更新totalEarningscommissionunpaidCommission
} float sum = productCoupons.getTotalEarnings() + entry.getValue();
} productCoupons.setTotalEarnings(sum);
} float commission = sum * productCoupons.getCommissionRate() / 100;
redisUtil.addToString(RedisUtil.PAYMENT_INFO_LAST_SCAN_TIME, currentTime); productCoupons.setCommission(commission);
} productCoupons.setUnpaidCommission(commission - productCoupons.getPaidCommission());
productCouponsMapper.updateById(productCoupons);
}
} }
}
redisUtil.addToString(RedisUtil.PAYMENT_INFO_LAST_SCAN_TIME, currentTime);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff