添加Affiliate功能

This commit is contained in:
2024-12-16 10:26:02 +08:00
parent efe22de0a0
commit bf8af41f3f
20 changed files with 349 additions and 55 deletions

View File

@@ -79,7 +79,5 @@ public class CommonConstant {
public static final String TIME_FORMAT_MMM_dd_yyyy = "MMM. dd, yyyy";
public static final String AFFILIATE_LINK = "";
public static final String AFFILIATE_LINK = "https://www.aida.com.hk?ref=";
}

View File

@@ -9,8 +9,8 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
@Slf4j
@@ -81,4 +81,21 @@ public class DateUtil {
return String.valueOf(epochSecond).substring(0, 10);
}
public static String changeTimeStampFormat(Long timeStamp, String type, String format){
// 将秒级时间戳转换为毫秒级
if (type.equals("seconds")){
timeStamp = timeStamp * 1000;
}
// 输出格式
SimpleDateFormat outputFormat = new SimpleDateFormat(format, Locale.ENGLISH);
// 创建Date对象
Date date = new Date(timeStamp);
// 格式化输出
return outputFormat.format(date);
}
public static String changeTimeStampFormat(LocalDateTime localDate){
return localDate.format(DateTimeFormatter.ofPattern("MMM. dd, yyyy, EEEE", Locale.US));
}
}

View File

@@ -4,19 +4,23 @@ package com.ai.da.controller;
import com.ai.da.common.response.Response;
import com.ai.da.mapper.primary.entity.Affiliate;
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.service.AffiliateService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/api/affiliate")
@Api(tags = "Affiliate模块")
public class AffiliateController {
@Resource
@@ -46,7 +50,7 @@ public class AffiliateController {
return Response.success(affiliateService.applicationApproval(id, isApproved));
}
@ApiOperation(value = "审批affiliate申请")
@ApiOperation(value = "定时计算佣金")
@GetMapping("/testTask")
public Response<String> testTask() {
affiliateService.updateAffiliateInfoWithPayment();
@@ -59,5 +63,11 @@ public class AffiliateController {
return Response.success(affiliateService.affiliateLinkViewsIncrease(id));
}
@ApiOperation(value = "获取每个affiliate产生的收入")
@GetMapping("/getEachAffiliateGeneratedRevenue")
public Response<List<AffiliateInvitationDetailsVO>> getEachAffiliateGeneratedRevenue(@RequestParam("id") Long id, @RequestParam(required = false) String startTime, @RequestParam(required = false) String endTime) {
return Response.success(affiliateService.getEachAffiliateGeneratedRevenue(id, startTime, endTime));
}
}

View File

@@ -3,9 +3,10 @@ package com.ai.da.controller;
import com.ai.da.common.enums.OrderStatusEnum;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.common.response.Response;
import com.ai.da.mapper.primary.entity.OrderInfo;
import com.ai.da.model.dto.QueryPageByTimeDTO;
import com.ai.da.model.vo.OrderListVO;
import com.ai.da.service.OrderInfoService;
import com.ai.da.service.PaymentInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
@@ -22,10 +23,13 @@ public class OrderInfoController {
@Resource
private OrderInfoService orderInfoService;
@Resource
private PaymentInfoService paymentInfoService;
@ApiOperation("订单列表")
@PostMapping("/list")
public Response<PageBaseResponse<OrderInfo>> list(@Valid @RequestBody QueryPageByTimeDTO queryPageByTimeDTO){
PageBaseResponse<OrderInfo> orderByAccountId = orderInfoService.getOrderByPage(queryPageByTimeDTO);
public Response<PageBaseResponse<OrderListVO>> list(@Valid @RequestBody QueryPageByTimeDTO queryPageByTimeDTO){
PageBaseResponse<OrderListVO> orderByAccountId = paymentInfoService.getPaymentInfo(queryPageByTimeDTO);
// List<OrderInfo> list = orderInfoService.listOrderByCreateTimeDesc();
return Response.success(orderByAccountId);
}

View File

@@ -70,8 +70,8 @@ public class StripeController {
@ApiOperation("取消订阅")
@GetMapping("/cancelSubscription")
public Response<String> cancelSubscription(@RequestParam String subscriptionId) {
stripeService.cancelSubscription(subscriptionId);
public Response<String> cancelSubscription(@RequestParam String subscriptionId, @RequestParam String reason) {
stripeService.cancelSubscription(subscriptionId, reason);
return Response.success("success");
}
@ApiOperation("临时 取消订阅")

View File

@@ -1,7 +1,14 @@
package com.ai.da.mapper.primary;
import com.ai.da.mapper.primary.entity.PaymentInfo;
import com.ai.da.model.vo.OrderListVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
public interface PaymentInfoMapper extends BaseMapper<PaymentInfo> {
List<OrderListVO> selectPageOrderList(Long accountId, String startTime, String endTime, int offset, int pageSize);
int queryOrderListTotalCount(Long accountId, String startTime, String endTime);
}

View File

@@ -25,4 +25,6 @@ public class Affiliate extends BaseEntity{
private Boolean approved = false;
private String link;
private String promotionMethod;
}

View File

@@ -33,4 +33,7 @@ public class SubscriptionInfo extends BaseEntity{
// 当前订阅订单有效期结束时间
private Long currentPeriodEnd;
// 取消订阅原因
private String cancelReason;
}

View File

@@ -44,4 +44,19 @@ public class AccountLoginVO {
private List<AccountExtend> accountExtendList;
// 订阅id(stripe提供)
private String subscriptionId;
// 订阅状态
private String status;
// 订阅过期时间
private String expireTime;
// 订阅类型 month || year
private String subscriptionType;
// 是否自动续订
private boolean isAutoRenewal;
}

View File

@@ -0,0 +1,25 @@
package com.ai.da.model.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AffiliateInvitationDetailsVO {
private Long accountId;
private String username;
private Float firstSubscriptionPaymentAmount;
private Float commission;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private LocalDateTime time;
}

View File

@@ -0,0 +1,29 @@
package com.ai.da.model.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 用于订单列表展示(展示的是所有支付信息)
*/
@Data
public class OrderListVO {
private Long id;
private Float amount;
private String paymentMethod;
private String state;
private String orderType;
private String invoiceLink;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private LocalDateTime createTime;
}

View File

@@ -2,10 +2,13 @@ package com.ai.da.service;
import com.ai.da.mapper.primary.entity.Affiliate;
import com.ai.da.model.dto.AffiliateQueryDTO;
import com.ai.da.model.vo.AffiliateInvitationDetailsVO;
import com.ai.da.model.vo.AffiliateVO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface AffiliateService extends IService<Affiliate> {
Boolean registerAsAnAffiliate(String promotionMethod);
@@ -19,4 +22,6 @@ public interface AffiliateService extends IService<Affiliate> {
void updateAffiliateInfoWithPayment();
Boolean affiliateLinkViewsIncrease(Long id);
List<AffiliateInvitationDetailsVO> getEachAffiliateGeneratedRevenue(Long affiliateId, String startTime, String endTime);
}

View File

@@ -1,12 +1,16 @@
package com.ai.da.service;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.mapper.primary.entity.PaymentInfo;
import com.ai.da.model.dto.AlipayHKCallbackDTO;
import com.ai.da.model.dto.QueryPageByTimeDTO;
import com.ai.da.model.vo.OrderListVO;
import com.baomidou.mybatisplus.extension.service.IService;
import com.paypal.orders.Order;
import com.stripe.model.Charge;
import com.stripe.model.Invoice;
import java.util.List;
import java.util.Map;
public interface PaymentInfoService extends IService<PaymentInfo> {
@@ -23,7 +27,9 @@ public interface PaymentInfoService extends IService<PaymentInfo> {
PaymentInfo createOrUpdatePaymentInfoForStripe(Charge charge);
PaymentInfo getPaymentInfoByOrderId(String orderId);
List<PaymentInfo> getPaymentInfoByOrderNo(String orderId, String order);
void updatePaymentStatusById(Long id, String status, String content);
PageBaseResponse<OrderListVO> getPaymentInfo(QueryPageByTimeDTO queryPageByTimeDTO);
}

View File

@@ -1,5 +1,6 @@
package com.ai.da.service;
import com.ai.da.mapper.primary.entity.SubscriptionInfo;
import com.ai.da.model.dto.ProductPurchaseDTO;
import com.stripe.exception.StripeException;
@@ -13,6 +14,8 @@ public interface StripeService {
Boolean notify(HttpServletRequest request);
SubscriptionInfo getLatestSubscriptionInfoByAccountId(Long accountId);
String refund(String amount, String orderId, String reason);
void checkOrderStatus(String orderNo);
@@ -21,7 +24,7 @@ public interface StripeService {
Map<String, String> getPaymentMethodByInvoiceId(String invoiceId);
void cancelSubscription(String orderNo);
void cancelSubscription(String orderNo, String cancelReason);
void cancelSubscriptionTemp(String subscriptionId);

View File

@@ -104,6 +104,9 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
@Resource
private RedisUtil redisUtil;
@Resource
private StripeService stripeService;
@Override
@Transactional(rollbackFor = Exception.class)
public AccountPreLoginVO preLogin(AccountPreLoginDTO accountDTO) {
@@ -2416,6 +2419,14 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
if (CollectionUtil.isNotEmpty(accountExtends)) {
response.setAccountExtendList(accountExtends);
}
SubscriptionInfo subscriptionInfo = stripeService.getLatestSubscriptionInfoByAccountId(accountId);
if (!Objects.isNull(subscriptionInfo)) {
response.setSubscriptionId(subscriptionInfo.getSubscriptionId());
response.setSubscriptionType(subscriptionInfo.getType());
response.setStatus(subscriptionInfo.getStatus());
response.setExpireTime(String.valueOf(subscriptionInfo.getCurrentPeriodEnd()));
response.setAutoRenewal(subscriptionInfo.getStatus().equals("active"));
}
return response;
}

View File

@@ -5,13 +5,14 @@ import com.ai.da.common.constant.CommonConstant;
import com.ai.da.common.context.UserContext;
import com.ai.da.common.response.ResultEnum;
import com.ai.da.common.utils.CopyUtil;
import com.ai.da.common.utils.ObjectUtils;
import com.ai.da.common.utils.RedisUtil;
import com.ai.da.common.utils.SendEmailUtil;
import com.ai.da.mapper.primary.AffiliateMapper;
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.AccountService;
@@ -22,6 +23,7 @@ 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;
@@ -29,6 +31,7 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -45,6 +48,9 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
@Resource
private PaymentInfoService paymentInfoService;
@Resource
private SubscriptionInfoMapper subscriptionInfoMapper;
@Resource
private RedisUtil redisUtil;
@@ -60,6 +66,7 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
affiliate.setAccountId(userHolder.getId());
affiliate.setStatus("Pending");
affiliate.setCreateTime(LocalDateTime.now());
affiliate.setPromotionMethod(promotionMethod);
baseMapper.insert(affiliate);
// 邮件通知审批者
// String email = "kimwong@code-create.com.hk";
@@ -73,9 +80,9 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
public IPage<Affiliate> getAffiliateList(AffiliateQueryDTO affiliateQueryDTO){
QueryWrapper<Affiliate> qw = new QueryWrapper<>();
qw.eq(affiliateQueryDTO.getStatus() != null, "status", affiliateQueryDTO.getStatus());
qw.gt(affiliateQueryDTO.getStartTime() != null, "create_time", affiliateQueryDTO.getStartTime());
qw.lt(affiliateQueryDTO.getEndTime() != null, "create_time", affiliateQueryDTO.getEndTime());
qw.eq(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getStatus()), "status", affiliateQueryDTO.getStatus());
qw.gt(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getStartTime()), "create_time", affiliateQueryDTO.getStartTime());
qw.lt(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getEndTime()), "create_time", affiliateQueryDTO.getEndTime());
return baseMapper.selectPage(new Page<>(affiliateQueryDTO.getPage(), affiliateQueryDTO.getSize()), qw);
}
@@ -85,7 +92,7 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
qw.eq("account_id", accountId);
Affiliate affiliate = baseMapper.selectOne(qw);
AffiliateVO affiliateVO = CopyUtil.copyObject(affiliate, AffiliateVO.class);
affiliateVO.setLinkViewCount(getAffiliateLinkViewCount(accountId));
affiliateVO.setLinkViewCount(getAffiliateLinkViewCount(affiliate.getId()));
return affiliateVO;
}
@@ -104,6 +111,7 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
affiliate.setApproved(false);
}
affiliate.setUpdateTime(LocalDateTime.now());
baseMapper.updateById(affiliate);
// 2、将批准结果邮件通知用户
Account account = accountService.getById(affiliate.getAccountId());
@@ -171,17 +179,61 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
redisUtil.addToString(RedisUtil.PAYMENT_INFO_LAST_SCAN_TIME, currentTime);
}
public Boolean affiliateLinkViewsIncrease(Long id) {
redisUtil.increaseAffiliateLinkViewCount(id);
public Boolean affiliateLinkViewsIncrease(Long affiliateId) {
redisUtil.increaseAffiliateLinkViewCount(affiliateId);
return Boolean.TRUE;
}
private Long getAffiliateLinkViewCount(Long accountId) {
return redisUtil.getAffiliateLinkViewCount(accountId);
private Long getAffiliateLinkViewCount(Long affiliateId) {
return redisUtil.getAffiliateLinkViewCount(affiliateId);
}
// 查看每个affiliate带来收入的详情
@Override
public List<AffiliateInvitationDetailsVO> getEachAffiliateGeneratedRevenue(Long affiliateId, String startTime, String endTime) {
List<AffiliateInvitationDetailsVO> resp = new ArrayList<>() ;
// 1、从account表中找到所有关联了指定affiliateId的accountId
QueryWrapper<Account> qw = new QueryWrapper<>();
qw.eq("invitation_code", affiliateId);
List<Account> accountList = accountService.getBaseMapper().selectList(qw);
if (accountList.isEmpty()){
return null;
} else {
accountList.forEach(account -> {
// 2、分别找到各个accountId产生的第一笔订阅
Long accountId = account.getId();
QueryWrapper<SubscriptionInfo> subscriptionInfoQueryWrapper = new QueryWrapper<>();
subscriptionInfoQueryWrapper.eq("account_id", accountId)
.and(s -> s.eq("status", "active").or().eq("status", "canceled"))
.gt(!StringUtils.isNullOrEmpty(startTime) ,"create_time", startTime)
.lt(!StringUtils.isNullOrEmpty(endTime) ,"create_time", endTime).last("limit 1");
SubscriptionInfo subscriptionInfo = subscriptionInfoMapper.selectOne(subscriptionInfoQueryWrapper);
// 2、分别第一笔订阅的付款信息
if (!Objects.isNull(subscriptionInfo)){
PaymentInfo paymentInfo = paymentInfoService.getPaymentInfoByOrderNo(subscriptionInfo.getOrderNo(), "ASC").get(0);
AffiliateInvitationDetailsVO affiliateInvitationDetailsVO = new AffiliateInvitationDetailsVO();
affiliateInvitationDetailsVO.setAccountId(accountId);
affiliateInvitationDetailsVO.setUsername(account.getUserName());
affiliateInvitationDetailsVO.setFirstSubscriptionPaymentAmount(paymentInfo.getPayerTotal());
affiliateInvitationDetailsVO.setCommission(BigDecimal.valueOf(paymentInfo.getPayerTotal()).multiply(new BigDecimal("0.25")).floatValue());
affiliateInvitationDetailsVO.setTime(subscriptionInfo.getCreateTime());
resp.add(affiliateInvitationDetailsVO);
}
});
}
return resp;
}
// todo 每个月给kim发一封邮件统计本月的affiliate等的收入
public void commissionCalculation(){
// 1、总收入(近一个月通过affiliate产生的收入)
// 2、未支付的金额 affiliate表中unpaid的总和
// 3、邀请的新人 查询account表中本月新增并有invitation_id的数量
}

View File

@@ -1,11 +1,17 @@
package com.ai.da.service.impl;
import com.ai.da.common.context.UserContext;
import com.ai.da.common.enums.PayTypeEnum;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.mapper.primary.PaymentInfoMapper;
import com.ai.da.mapper.primary.entity.PaymentInfo;
import com.ai.da.model.dto.AlipayHKCallbackDTO;
import com.ai.da.model.dto.QueryPageByTimeDTO;
import com.ai.da.model.vo.OrderListVO;
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.google.gson.Gson;
import com.paypal.orders.Order;
@@ -15,15 +21,19 @@ import com.stripe.model.Charge;
import com.stripe.model.Invoice;
import com.stripe.model.Subscription;
import com.stripe.model.checkout.Session;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -313,11 +323,11 @@ public class PaymentInfoServiceImpl extends ServiceImpl<PaymentInfoMapper, Payme
@Override
public PaymentInfo getPaymentInfoByOrderId(String orderId){
public List<PaymentInfo> getPaymentInfoByOrderNo(String orderId, String order){
QueryWrapper<PaymentInfo> qw = new QueryWrapper<>();
qw.eq("order_no", orderId);
qw.eq("order_no", orderId).orderByDesc(order.equals("DESC"),"id");
return baseMapper.selectOne(qw);
return baseMapper.selectList(qw);
}
@Override
@@ -329,4 +339,36 @@ public class PaymentInfoServiceImpl extends ServiceImpl<PaymentInfoMapper, Payme
baseMapper.updateById(paymentInfo);
}
public PageBaseResponse<OrderListVO> getPaymentInfo(QueryPageByTimeDTO queryPageByTimeDTO){
Long accountId = UserContext.getUserHolder().getId();
String startTime = queryPageByTimeDTO.getStartTime();
String endTime = queryPageByTimeDTO.getEndTime();
if (StringUtil.isNullOrEmpty(startTime)) {
startTime = "2024-02-01 00:00:00";
}
if (StringUtil.isNullOrEmpty(endTime)) {
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
endTime = now.format(dateTimeFormatter);
}
int offset = (queryPageByTimeDTO.getPage() - 1) * queryPageByTimeDTO.getSize();
List<OrderListVO> orderListVOS = baseMapper.selectPageOrderList(accountId, startTime, endTime, offset, queryPageByTimeDTO.getSize());
if (CollectionUtils.isEmpty(orderListVOS)) {
return PageBaseResponse.success(new Page<>());
}else {
int totalCount = baseMapper.queryOrderListTotalCount(accountId, startTime, endTime);
IPage<OrderListVO> orderListVOIPage = new Page<>();
Integer size = queryPageByTimeDTO.getSize();
orderListVOIPage.setSize(size);
orderListVOIPage.setRecords(orderListVOS);
orderListVOIPage.setCurrent(queryPageByTimeDTO.getPage());
orderListVOIPage.setPages((long)Math.ceil((double) totalCount / size));
orderListVOIPage.setTotal(totalCount);
return PageBaseResponse.success(orderListVOIPage);
}
}
}

View File

@@ -4,6 +4,7 @@ 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.enums.*;
import com.ai.da.common.utils.DateUtil;
import com.ai.da.common.utils.SendEmailUtil;
import com.ai.da.mapper.primary.AccountMapper;
import com.ai.da.mapper.primary.PaymentInfoMapper;
@@ -37,11 +38,9 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
@SuppressWarnings("LoggingSimilarMessage")
@@ -440,11 +439,9 @@ public class StripeServiceImpl implements StripeService {
@Transactional(rollbackFor = Exception.class)
public SubscriptionInfo createSubscription(Subscription subscription){
// 确认当前subscription是否已经记录
QueryWrapper<SubscriptionInfo> qw = new QueryWrapper<>();
qw.eq("subscription_id", subscription.getId());
SubscriptionInfo subscriptionInfo = subscriptionInfoMapper.selectOne(qw);
if (Objects.isNull(subscriptionInfo)){
SubscriptionInfo subscriptionInfo = getSubscriptionInfoBySubId(subscription.getId());
// SubscriptionInfo subscriptionInfo = subscriptionInfoMapper.selectOne(qw);
if (Objects.isNull(subscriptionInfo)) {
String description = subscription.getDescription();
String orderNo = description.replace("AiDA - ", "");
OrderInfo orderInfo = orderInfoService.getOrderByOrderNo(orderNo);
@@ -459,7 +456,7 @@ public class StripeServiceImpl implements StripeService {
subscriptionInfo.setSubscriptionId(subscription.getId());
subscriptionInfo.setType(interval);
subscriptionInfo.setStatus(subscription.getStatus());
subscriptionInfo.setNextPayDate(changeTimeStampFormat(subscription.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy_EEEE));
subscriptionInfo.setNextPayDate(DateUtil.changeTimeStampFormat(subscription.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy_EEEE));
subscriptionInfo.setCurrentPeriodStart(subscription.getCurrentPeriodStart());
subscriptionInfo.setCurrentPeriodEnd(subscription.getCurrentPeriodEnd());
subscriptionInfo.setCreateTime(LocalDateTime.now());
@@ -471,21 +468,34 @@ public class StripeServiceImpl implements StripeService {
return subscriptionInfo;
}
public String changeTimeStampFormat(Long timeStamp, String type, String format){
// 将秒级时间戳转换为毫秒级
if (type.equals("seconds")){
timeStamp = timeStamp * 1000;
public SubscriptionInfo getSubscriptionInfoBySubId(String subId){
QueryWrapper<SubscriptionInfo> qw = new QueryWrapper<>();
qw.eq("subscription_id", subId);
List<SubscriptionInfo> subscriptionInfos = subscriptionInfoMapper.selectList(qw);
if (subscriptionInfos.size() == 1){
return subscriptionInfos.get(0);
}else if (subscriptionInfos.size() > 1) {
// 如果新建了多个订阅则筛选出状态为active的订单
Optional<SubscriptionInfo> activeSubscriptionInfo = subscriptionInfos.stream()
.filter(sub -> sub.getStatus().equals("active"))
.findFirst();
return activeSubscriptionInfo.orElseGet(() -> subscriptionInfos.get(0));
}else {
return null;
}
// 输出格式
SimpleDateFormat outputFormat = new SimpleDateFormat(format, Locale.ENGLISH);
// 创建Date对象
Date date = new Date(timeStamp);
// 格式化输出
return outputFormat.format(date);
}
public String changeTimeStampFormat(LocalDateTime localDate){
return localDate.format(DateTimeFormatter.ofPattern("MMM. dd, yyyy, EEEE", Locale.US));
public SubscriptionInfo getLatestSubscriptionInfoByAccountId(Long accountId){
QueryWrapper<SubscriptionInfo> qw = new QueryWrapper<>();
qw.eq("account_id", accountId);
List<SubscriptionInfo> subscriptionInfos = subscriptionInfoMapper.selectList(qw);
if (subscriptionInfos.isEmpty()){
return null;
}else {
return subscriptionInfos.get(0);
}
}
@Transactional(rollbackFor = Exception.class)
@@ -504,10 +514,10 @@ public class StripeServiceImpl implements StripeService {
}
if (!subscriptionInfo.getCurrentPeriodEnd().equals(subscription.getCurrentPeriodEnd())){
subscriptionInfo.setCurrentPeriodEnd(subscription.getCurrentPeriodEnd());
subscriptionInfo.setNextPayDate(changeTimeStampFormat(subscription.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy_EEEE));
subscriptionInfo.setNextPayDate(DateUtil.changeTimeStampFormat(subscription.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy_EEEE));
// 更新账号到期时间
updateAccountValidity(subscriptionInfo.getAccountId(), subscriptionInfo.getCurrentPeriodEnd());
log.info("更新 {} 账号到期时间为:{}", subscriptionInfo.getAccountId(), changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy_EEEE));
log.info("更新 {} 账号到期时间为:{}", subscriptionInfo.getAccountId(), DateUtil.changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy_EEEE));
flag = true;
}
if (flag){
@@ -526,7 +536,7 @@ public class StripeServiceImpl implements StripeService {
}
// 取消连续订阅 将订阅从pause状态转为cancel状态使用定时器定期检索DB中过期且不续订的订阅
public void cancelSubscription(String subscriptionId) {
public void cancelSubscription(String subscriptionId, String cancelReason) {
Stripe.apiKey = privateKey;
Long accountId = UserContext.getUserHolder().getId();
log.info("用户 {} 申请取消连续订阅 {}", accountId, subscriptionId);
@@ -538,6 +548,9 @@ public class StripeServiceImpl implements StripeService {
try {
Subscription cancel = subscription.cancel();
cancel.getStatus();
// 更新数据库
updateCancelReason(subscriptionId, cancelReason);
} catch (StripeException e) {
log.error("订阅 {} 取消失败, error message : {}", subscription.getId(), e.getMessage());
}
@@ -564,8 +577,9 @@ public class StripeServiceImpl implements StripeService {
try {
Stripe.apiKey = privateKey;
// todo transactionId不再是sessionId而是invoiceId所以这里需要更新
// 根据orderId找到对应的sessionId
String sessionId = paymentInfoService.getPaymentInfoByOrderId(orderNo).getTransactionId();
String sessionId = paymentInfoService.getPaymentInfoByOrderNo(orderNo, "DESC").get(0).getTransactionId();
if (StringUtils.isNotEmpty(sessionId)) { //根据会话编号退款
Session session = Session.retrieve(sessionId);
@@ -628,7 +642,8 @@ public class StripeServiceImpl implements StripeService {
public void checkOrderStatus(String orderNo) {
Stripe.apiKey = privateKey;
// 1、通过orderNo 查询sessionId
PaymentInfo paymentInfo = paymentInfoService.getPaymentInfoByOrderId(orderNo);
// todo transactionId不再是sessionId而是invoiceId所以这里需要更新
PaymentInfo paymentInfo = paymentInfoService.getPaymentInfoByOrderNo(orderNo, "DESC").get(0);
try {
Session session = Session.retrieve(paymentInfo.getTransactionId());
if (Objects.isNull(session)) {
@@ -822,8 +837,8 @@ public class StripeServiceImpl implements StripeService {
emailParamsDTO.setCreateDate(String.valueOf(paymentInfo.getCreateTime()).replace("T", " "));
emailParamsDTO.setQuantity(String.valueOf(1));
emailParamsDTO.setTotalFee(paymentInfo.getPayerTotal().toString());
emailParamsDTO.setLastOrderDate(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodStart(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
emailParamsDTO.setEndOfPrepaidTerm(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
emailParamsDTO.setLastOrderDate(DateUtil.changeTimeStampFormat(subscriptionInfo.getCurrentPeriodStart(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
emailParamsDTO.setEndOfPrepaidTerm(DateUtil.changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
setSubscriptionParams(paymentInfo, subscriptionInfo, orderByOrderNo, emailParamsDTO);
SendEmailUtil.subscriptionEmailReminder(type, emailParamsDTO, language, account.getUserEmail());
@@ -941,9 +956,9 @@ public class StripeServiceImpl implements StripeService {
emailParamsDTO.setSubscriptionId(subscriptionInfo.getId().toString());
emailParamsDTO.setFailMessage(orderByOrderNo.getNote());
emailParamsDTO.setSubscriptionType(subscriptionInfo.getType());
emailParamsDTO.setStartDate(changeTimeStampFormat(orderByOrderNo.getCreateTime()));
emailParamsDTO.setNextPayDate(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
emailParamsDTO.setRenewalTime(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
emailParamsDTO.setStartDate(DateUtil.changeTimeStampFormat(orderByOrderNo.getCreateTime()));
emailParamsDTO.setNextPayDate(DateUtil.changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
emailParamsDTO.setRenewalTime(DateUtil.changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
}
public void subscriptionReminder(){
@@ -1082,6 +1097,14 @@ public class StripeServiceImpl implements StripeService {
return null;
}
public void updateCancelReason(String subscriptionId, String reason){
QueryWrapper<SubscriptionInfo> qw = new QueryWrapper<>();
qw.eq("subscription_id", subscriptionId);
SubscriptionInfo subscriptionInfo = subscriptionInfoMapper.selectOne(qw);
subscriptionInfo.setCancelReason(reason);
subscriptionInfoMapper.updateById(subscriptionInfo);
}
}