添加Affiliate功能
This commit is contained in:
@@ -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=";
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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("临时 取消订阅")
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -25,4 +25,6 @@ public class Affiliate extends BaseEntity{
|
||||
private Boolean approved = false;
|
||||
|
||||
private String link;
|
||||
|
||||
private String promotionMethod;
|
||||
}
|
||||
|
||||
@@ -33,4 +33,7 @@ public class SubscriptionInfo extends BaseEntity{
|
||||
// 当前订阅订单有效期结束时间
|
||||
private Long currentPeriodEnd;
|
||||
|
||||
// 取消订阅原因
|
||||
private String cancelReason;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
29
src/main/java/com/ai/da/model/vo/OrderListVO.java
Normal file
29
src/main/java/com/ai/da/model/vo/OrderListVO.java
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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的数量
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ spring.security.jwtExpiration=8640000000
|
||||
spring.security.ignorePaths=/,/favicon.ico,/doc.html,/webjars/**,/swagger-resources,/v2/api-docs,\
|
||||
/api/account/**,/api/element/**,/api/python/**,/api/design/**,/api/history/**,/api/library/**,/api/third/party/**,/api/generate/**,/api/workspace/**,/api/classification/**,\
|
||||
/api/product/**,/api/ali-pay/**,/api/order-info/**,/api/paypal/**,/api/credits/**,/api/inquiry/**,/api/tasks/**,/api/python/prepareForSR,/api/alipay-hk/**,/api/portfolio/**,\
|
||||
/api/stripe/**,/api/message/**,/notification/**
|
||||
/api/stripe/**,/api/message/**,/notification/**,/api/affiliate/**
|
||||
spring.security.authApi=/auth/login
|
||||
|
||||
|
||||
|
||||
42
src/main/resources/mapper/primary/PaymentInfoMapper.xml
Normal file
42
src/main/resources/mapper/primary/PaymentInfoMapper.xml
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ai.da.mapper.primary.PaymentInfoMapper">
|
||||
<select id="selectPageOrderList" resultType="com.ai.da.model.vo.OrderListVO">
|
||||
SELECT
|
||||
p.id AS id,
|
||||
p.payer_total AS amount,
|
||||
p.payment_type AS paymentMethod,
|
||||
p.trade_state AS state,
|
||||
o.title AS orderType,
|
||||
p.hosted_invoice_url AS invoiceLink,
|
||||
p.create_time
|
||||
FROM
|
||||
`t_payment_info` p
|
||||
LEFT JOIN
|
||||
`t_order_info` o
|
||||
ON
|
||||
p.order_no = o.order_no
|
||||
WHERE
|
||||
o.account_id = #{accountId}
|
||||
AND p.create_time BETWEEN #{startTime} AND #{endTime}
|
||||
ORDER BY
|
||||
p.id DESC
|
||||
LIMIT #{pageSize} OFFSET #{offset};
|
||||
</select>
|
||||
|
||||
<select id="queryOrderListTotalCount" parameterType="map" resultType="int">
|
||||
SELECT
|
||||
COUNT(*)
|
||||
FROM
|
||||
`t_payment_info` p
|
||||
LEFT JOIN
|
||||
`t_order_info` o
|
||||
ON
|
||||
p.order_no = o.order_no
|
||||
WHERE
|
||||
o.account_id = #{accountId}
|
||||
AND p.create_time BETWEEN #{startTime} AND #{endTime};
|
||||
</select>
|
||||
|
||||
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user