Merge branch 'dev/dev_xp' into dev/dev

This commit is contained in:
2025-04-28 14:53:04 +08:00
16 changed files with 200 additions and 21 deletions

View File

@@ -18,7 +18,9 @@ public interface AffiliateService extends IService<Affiliate> {
double[] getPersonalMonthlyIncome(int year);
Boolean applicationApproval(Long id, Boolean isApproved);
Boolean applicationApproval(Long id, Boolean isApproved, Float commission);
void updateCommissionPercentage(Long id, Float commission);
void updateAffiliateInfoWithPayment();

View File

@@ -34,4 +34,6 @@ public interface PaymentInfoService extends IService<PaymentInfo> {
PageBaseResponse<OrderListVO> getPaymentInfo(QueryPageByTimeDTO queryPageByTimeDTO);
List<PaymentInfo> getPaymentInfoByPromCode(Long accountId, String promCode);
PaymentInfo updatePaymentRefundStatus(Charge charge);
}

View File

@@ -3,6 +3,8 @@ package com.ai.da.service;
import com.ai.da.mapper.primary.entity.RefundInfo;
import com.baomidou.mybatisplus.extension.service.IService;
import com.stripe.model.Charge;
import com.stripe.model.Refund;
import java.util.List;
@@ -19,4 +21,13 @@ public interface RefundInfoService extends IService<RefundInfo> {
void updateRefundForAliPay(String refundNo, String content, String refundStatus);
void updateRefundForPayPal(Long id, String refundId, String content, String refundStatus);
List<RefundInfo> getByChargeId(String chargeId);
RefundInfo createRefundForStripe(Refund refund);
RefundInfo updateRefundStatusForStripe(Refund refund);
RefundInfo updateRefundForStripe(Charge charge);
}

View File

@@ -70,4 +70,6 @@ public interface StripeService {
String retrievePromotionCode(String promotionCode);
IPage<ProductCoupons> getAllCoupons(QueryCouponsPageDTO queryCouponsPageDTO);
void deleteCoupon(Long id);
}

View File

@@ -147,7 +147,7 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
}
// 审批申请
public Boolean applicationApproval(Long id, Boolean isApproved){
public Boolean applicationApproval(Long id, Boolean isApproved, Float commission){
Affiliate affiliate = baseMapper.selectById(id);
// 1、更新db状态
@@ -156,6 +156,12 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
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);
@@ -175,6 +181,19 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
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
@@ -211,8 +230,8 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
Float payerTotal = paymentInfo.getPayerTotal();
if (payerTotal > 0){
// 分配新用户首次订阅所付费用的25%作为佣金
BigDecimal commission = BigDecimal.valueOf(payerTotal).multiply(new BigDecimal("0.25"));
// 分配新用户首次订阅所付费用 预设的佣金比例 作为佣金
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;

View File

@@ -106,7 +106,11 @@ public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, No
IPage<NotificationVO> convert = notificationPage.convert(o -> {
NotificationVO notificationVO = CopyUtil.copyObject(o, NotificationVO.class);
Account senderAccount = accountService.getById(notificationVO.getSenderId());
notificationVO.setUserName(senderAccount.getUserName());
if (Objects.nonNull(senderAccount)){
notificationVO.setUserName(senderAccount.getUserName());
}else {
notificationVO.setUserName("--");
}
// notificationVO.setSenderUserAvatar(StringUtils.isNullOrEmpty(senderAccount.getAvatar()) ? null : minioUtil.getPreSignedUrl(senderAccount.getAvatar(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
if (Objects.isNull(notificationVO.getPortfolioId())){
notificationVO.setPortfolioId(null);

View File

@@ -435,4 +435,28 @@ public class PaymentInfoServiceImpl extends ServiceImpl<PaymentInfoMapper, Payme
public List<PaymentInfo> getPaymentInfoByPromCode(Long accountId, String promCode){
return baseMapper.selectPaidPaymentsByAccountAndPromotion(accountId, promCode);
}
public PaymentInfo updatePaymentRefundStatus(Charge charge){
// 判断当前退款是部分退款还是全部退款
QueryWrapper<PaymentInfo> qw = new QueryWrapper<>();
qw.eq("transaction_id", charge.getInvoice());
PaymentInfo paymentInfo = baseMapper.selectOne(qw);
if (Objects.nonNull(paymentInfo)){
String status ;
if (Objects.equals(charge.getAmount(), charge.getAmountRefunded())){
status = "Refunded";
}else if (charge.getAmount() > charge.getAmountRefunded()){
status = "Partial refund";
}else {
status = "Refund Exception";
log.warn("{}, 退款金额高于付款金额, ChargeId为{}", status, charge.getId());
}
if (!paymentInfo.getTradeState().equals(status)){
paymentInfo.setTradeState(status);
paymentInfo.setUpdateTime(LocalDateTime.now());
baseMapper.updateById(paymentInfo);
}
}
return null;
}
}

View File

@@ -10,14 +10,16 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.stripe.model.Charge;
import com.stripe.model.Refund;
import io.netty.util.internal.StringUtil;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.time.LocalDateTime;
import java.util.*;
@Service
public class RefundInfoServiceImpl extends ServiceImpl<RefundInfoMapper, RefundInfo> implements RefundInfoService {
@@ -170,4 +172,63 @@ public class RefundInfoServiceImpl extends ServiceImpl<RefundInfoMapper, RefundI
// baseMapper.update(refundInfo, queryWrapper);
baseMapper.updateById(refundInfo);
}
public List<RefundInfo> getByChargeId(String chargeId){
List<RefundInfo> refundInfoList = baseMapper.selectList(new QueryWrapper<RefundInfo>().eq("charge_id", chargeId));
// 判断当前已退款的金额是否全部退完(针对多次部分退款的情况)
if (refundInfoList.isEmpty()){
return new ArrayList<>();
}else {
return refundInfoList;
}
}
public RefundInfo getByRefundId(String refundId){
return baseMapper.selectOne(new QueryWrapper<RefundInfo>().eq("refund_id", refundId));
}
public RefundInfo createRefundForStripe(Refund refund){
// 先确认这个回调是否已经被记录
RefundInfo refundInfo = getByRefundId(refund.getId());
if (Objects.isNull(refundInfo)){
refundInfo = new RefundInfo();
refundInfo.setRefundId(refund.getId());
refundInfo.setChargeId(refund.getCharge());
refundInfo.setRefund(refund.getAmount() / 100f);
refundInfo.setReason(refund.getReason());
refundInfo.setRefundStatus(refund.getStatus());
refundInfo.setCreateTime(LocalDateTime.now());
baseMapper.insert(refundInfo);
}
return refundInfo;
}
public RefundInfo updateRefundStatusForStripe(Refund refund){
RefundInfo refundInfo = getByRefundId(refund.getId());
if (Objects.isNull(refundInfo)){
return null;
}
if (!refundInfo.getRefundStatus().equals(refund.getStatus())){
refundInfo.setRefundStatus(refund.getStatus());
refundInfo.setUpdateTime(LocalDateTime.now());
baseMapper.updateById(refundInfo);
}
return refundInfo;
}
public RefundInfo updateRefundForStripe(Charge charge){
List<RefundInfo> refundInfoList = getByChargeId(charge.getId());
if (!refundInfoList.isEmpty()){
RefundInfo refundInfo = refundInfoList.get(refundInfoList.size() - 1);
if (StringUtil.isNullOrEmpty(refundInfo.getOrderNo())){
String orderNo = charge.getDescription().replace("AiDA - ", "");
refundInfo.setOrderNo(orderNo);
refundInfo.setTotalFee(charge.getAmount() / 100f);
refundInfo.setUpdateTime(LocalDateTime.now());
baseMapper.updateById(refundInfo);
return refundInfo;
}
}
return null;
}
}

View File

@@ -116,6 +116,16 @@ public class StripeServiceImpl implements StripeService {
default:
throw new BusinessException("unknown subscription type");
}
// 添加优惠券(只允许在订阅时使用优惠券)
String promotionCode = productPurchaseDTO.getPromotionCode();
if (!StringUtil.isNullOrEmpty(promotionCode)){
ProductCoupons productCoupon = checkProductCoupon(promotionCode);;
if (productCoupon != null){
sessionBuilder.addDiscount(SessionCreateParams.Discount.builder()
.setPromotionCode(productCoupon.getPromotionCodeId()).build());
}
}
// 只有订阅时才允许使用推广码优惠
// sessionBuilder.setAllowPromotionCodes(true);
break;
@@ -170,16 +180,6 @@ public class StripeServiceImpl implements StripeService {
.build());
sessionBuilder.putMetadata("orderId", orderId); //通过订单号关联用于检索支付信息(可选)
// 添加优惠券
String promotionCode = productPurchaseDTO.getPromotionCode();
if (!StringUtil.isNullOrEmpty(promotionCode)){
ProductCoupons productCoupon = checkProductCoupon(promotionCode);;
if (productCoupon != null){
sessionBuilder.addDiscount(SessionCreateParams.Discount.builder()
.setPromotionCode(productCoupon.getPromotionCodeId()).build());
}
}
Session session = Session.create(sessionBuilder.build());
List<String> paymentMethodTypes = session.getPaymentMethodTypes();
log.info("paymentMethodTypes: {}", paymentMethodTypes);
@@ -193,6 +193,9 @@ public class StripeServiceImpl implements StripeService {
return session.getUrl();
} catch (BusinessException e) {
throw e;
} catch (InvalidRequestException e) {
log.info("创建会话出现异常:", e);
throw new BusinessException(e.getMessage().substring(0, e.getMessage().indexOf(";")));
} catch (Exception e) {
log.error("创建支付会话出现异常:", e);
}
@@ -430,6 +433,26 @@ public class StripeServiceImpl implements StripeService {
orderInfo.setOrderStatus(OrderStatusEnum.SUCCESS.getType());
orderInfo.setNote("");
orderInfoService.updateById(orderInfo);
}else if (event.getType().equals("charge.refunded")){
// 更新退款信息
RefundInfo refundInfo = refundInfoService.updateRefundForStripe(charge);
// 更新 t_payment_info的支付状态
if (Objects.nonNull(refundInfo)){
paymentInfoService.updatePaymentRefundStatus(charge);
}
}
}else if (stripeObject instanceof Refund){
Refund refund = (Refund) stripeObject;
if (event.getType().equals("refund.created")){
// 新增退款信息
refundInfoService.createRefundForStripe(refund);
}else if (event.getType().equals("refund.updated")){
// 根据***id更新退款记录信息
RefundInfo refundInfo = refundInfoService.updateRefundStatusForStripe(refund);
if (Objects.isNull(refundInfo)){
// 等事件先创建,再更新。回调事件的顺序随机
response = false;
}
}
}
log.info("回调事件 {} 处理完成", event.getType());