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