package com.cyksj.service.mange.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateField; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.alipay.api.AlipayClient; import com.alipay.api.domain.AlipayTradeFastpayRefundQueryModel; import com.alipay.api.domain.AlipayTradeRefundModel; import com.alipay.api.request.AlipayTradeRefundRequest; import com.alipay.api.response.AlipayTradeRefundResponse; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.cyksj.common.constant.Constant; import com.cyksj.common.exception.BusinessRuntimeException; import com.cyksj.common.task.GlobalThreadPoolTaskExecutor; import com.cyksj.common.util.Codec; import com.cyksj.common.util.Jsons; import com.cyksj.common.util.StringUtil; import com.cyksj.common.util.Xmls; import com.cyksj.config.WeChatConfig; import com.cyksj.config.zfb.AliPayClientFactory; import com.cyksj.dto.RedisKey; import com.cyksj.mapper.*; import com.cyksj.mapper.channel.ShopConfigMapper; import com.cyksj.mapper.channel.UserPayEquipmentAvailableBenefitsMapper; import com.cyksj.mapper.channel.UserPayEquipmentReceiveBenefitsRecordMapper; import com.cyksj.mapper.claudecode.ClaudeCodeDeductOrderRelateMapper; import com.cyksj.mapper.claudecode.ClaudeCodeUserResetRecordMapper; import com.cyksj.mapper.manage.cms.CmsUserMapper; import com.cyksj.mapper.manage.coupon.CouponUserMapper; import com.cyksj.mapper.manage.distribute.DistributeWaitingSendPointsMapper; import com.cyksj.mapper.manage.distribute.UserDistributeBusinessOrderDonRecordMapper; import com.cyksj.mapper.manage.distribute.UserDistributeMapper; import com.cyksj.mapper.manage.distribute.equipment.EquipmentUserDistributeTransportBenefitsMapper; import com.cyksj.mapper.manage.distribute.sub.UserDistributeSubMapper; import com.cyksj.mapper.manage.distribute.sub.UserDistributeSubRewardsMapper; import com.cyksj.mapper.manage.distribute.sub.UserDistributeSubWaitingMapper; import com.cyksj.mapper.manage.sys.WorkOrderMapper; import com.cyksj.mapper.mirrorshop.UserMirrorShopDistributeWaitingSendPointsMapper; import com.cyksj.mapper.shop.YhShopUserDistributeBusinessOrderDonRecordMapper; import com.cyksj.mapper.stock.GoodsSkuStockRelateMapper; import com.cyksj.model.entity.*; import com.cyksj.model.excel.ExcelOrderDonInfo; import com.cyksj.model.manage.request.ReqSetLogisticsPost; import com.cyksj.model.manage.views.GroupsRelationView; import com.cyksj.model.request.ChangeRelationReq; import com.cyksj.model.request.ClickOutReq; import com.cyksj.model.request.OrderBalanceRefundReq; import com.cyksj.model.request.OrderRefundReq; import com.cyksj.redis.RedisService; import com.cyksj.service.chatgpt.GptUserRechargeRecordService; import com.cyksj.service.claude.ClaudeCodeService; import com.cyksj.service.coin.UserGalaxyCoinService; import com.cyksj.service.coupon.CouponFontService; import com.cyksj.service.distribute.equipment.EquipmentDistributeService; import com.cyksj.service.giftcard.GiftCardOrderUseRecordService; import com.cyksj.service.mange.CmsOrderDonService; import com.cyksj.service.mange.MirrorUserCleatService; import com.cyksj.service.mange.stock.GoodsDonStockService; import com.cyksj.service.market.MarketFrontService; import com.cyksj.service.order.OrderCommonService; import com.cyksj.service.order.OrderDonBusinessEventService; import com.cyksj.service.order.OrderDonTicketRecordService; import com.cyksj.service.order.MultiQuantityOrderService; import com.cyksj.service.order.OrderRefundService; import com.cyksj.service.order.UserRealOrderBenefitsService; import com.cyksj.service.relation.GroupRelationClearService; import com.cyksj.service.relation.GroupRelationFrontService; import com.cyksj.service.relation.GroupsRelationExpiryRecordService; import com.cyksj.service.relation.GroupsRelationNetflixService; import com.cyksj.service.shop.YhShopDistributeService; import com.cyksj.service.user.UserBenefitsService; import com.cyksj.service.user.UserBindRelationService; import com.cyksj.service.wechat.WeChatService; import com.ejlchina.searcher.BeanSearcher; import com.ejlchina.searcher.param.Operator; import com.ejlchina.searcher.util.MapUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionTemplate; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; /** * @author chan * @date 2021/10/25 下午10:14 */ @Service @Slf4j @RequiredArgsConstructor public class CmsOrderDonServiceImpl extends ServiceImpl implements CmsOrderDonService { private final OrderDonTransportMapper transportMapper; private final OrderDonWaybillMapper orderDonWaybillMapper; private final GoodsSkuStockRelateMapper skuStockRelateMapper; private final GoodsDonStockService goodsDonStockService; private final GoodsDonSkuMapper goodsDonSkuMapper; private final UserRealOrderBenefitsService userRealOrderBenefitsService; private final UserRelationKickOutRecordMapper kickOutRecordMapper; private final CmsUserMapper cmsUserMapper; private final OrderCommonService orderCommonService; private final GoodsDonMapper goodsDonMapper; private final RedisService redisService; private final UserBenefitsService userBenefitsService; private final DistributeWaitingSendPointsMapper distributeWaitingSendPointsMapper; private final RefundOrderDonMapper refundOrderDonMapper; private final UserDistributeMapper userDistributeMapper; private final OrderDonMapper orderDonMapper; private final MarketFrontService marketFrontService; private final EquipmentDistributeService equipmentDistributeService; private final CouponFontService couponFontService; private final WeChatService weChatService; private final WeChatConfig weChatConfig; private final ShopConfigMapper shopConfigMapper; private final GroupRelationClearService relationClearService; private final GroupsRelationMapper relationMapper; private final GroupsMapper groupsMapper; private final GoodsDonSkuMapper skuMapper; private final UserDistributeBusinessOrderDonRecordMapper businessOrderDonRecordMapper; private final WorkOrderMapper workOrderMapper; private final OrderRefundService orderRefundService; private final OrderDiscountPlusPurchaseSkuRelationMapper orderDiscountPlusPurchaseSkuRelationMapper; private final EquipmentUserDistributeTransportBenefitsMapper transportBenefitsMapper; private final BeanSearcher beanSearcher; private final UserPayEquipmentReceiveBenefitsRecordMapper receiveBenefitsRecordMapper; private final GroupsRelationExpiryRecordService groupsRelationExpiryRecordService; private final GiftCardOrderUseRecordService giftCardOrderUseRecordService; private final GiftCardUserRecordMapper giftCardUserRecordMapper; private final YhShopDistributeService yhShopDistributeService; private final YhShopUserDistributeBusinessOrderDonRecordMapper yhShopBusinessOrderDonRecordMapper; private final UserPayEquipmentAvailableBenefitsMapper availableBenefitsMapper; private final GroupRelationFrontService groupRelationFrontService; private final UserMapper userMapper; private final ChatgptUserMapper chatgptUserMapper; private final UserDistributeSubMapper userDistributeSubMapper; private final UserDistributeSubWaitingMapper subWaitingMapper; private final UserDistributeSubRewardsMapper userDistributeSubRewardsMapper; private final UpgradeOrderDonMapper upgradeOrderDonMapper; private final UpgradePackageMapper upgradePackageMapper; private final MidjourneyUserMapper midjourneyUserMapper; private final GroupsRelationMapper groupsRelationMapper; private final UserBindRelationService userBindRelationService; private final GroupsRelationNetflixService groupsRelationNetflixService; private final AccountMapper accountMapper; private final MirrorUserCleatService mirrorUserCleatService; private final OrderDonRenewRecordMapper orderDonRenewRecordMapper; private final LumaUserMapper lumaUserMapper; private final UserBenefitsComboRelationRecordMapper userBenefitsComboRelationRecordMapper; private final UserMirrorShopDistributeWaitingSendPointsMapper userMirrorShopDistributeWaitingSendPointsMapper; private final ClaudeUserMapper claudeUserMapper; private final UserTicketClearedRecordMapper userTicketClearedRecordMapper; private final NetflixUserAccountSubmitRecoverRecordMapper netflixUserAccountSubmitRecoverRecordMapper; private final NetflixUserChangeOtherTicketRecordMapper netflixUserChangeOtherTicketRecordMapper; private final CouponUserMapper couponUserMapper; private final UserGalaxyCoinService userGalaxyCoinService; private final GptUserRechargeRecordService gptUserRechargeRecordService; private final ClaudeCodeUserResetRecordMapper claudeCodeUserResetRecordMapper; private final ClaudeCodeService claudeCodeService; private final ClaudeCodeDeductOrderRelateMapper claudeCodeDeductOrderRelateMapper; private final NanoUserQuotaMapper nanoUserQuotaMapper; private final AccountNetflixFrozenTicketReceivedRecordMapper receivedRecordMapper; private final OrderDonTicketRecordService orderDonTicketRecordService; private final MultiQuantityOrderService multiQuantityOrderService; private final OrderDonBusinessEventService orderDonBusinessEventService; private final OrderDonGalaxyCoinRecordMapper orderDonGalaxyCoinRecordMapper; private final UserBalanceSourceRecordMapper userBalanceSourceRecordMapper; private final TransactionTemplate transactionTemplate; private static final GlobalThreadPoolTaskExecutor TASK_POOL = GlobalThreadPoolTaskExecutor.getInstance(); @Override public List exportOrderInfo(Long promotionId, String start, String end) { return this.getBaseMapper().exportOrderInfo(promotionId,start,end); } @Override public OrderDonTransport getTransport(Long orderId) { return transportMapper.selectOne(Wrappers.lambdaQuery(OrderDonTransport.class).eq(OrderDonTransport::getOrderId, orderId)); } @Transactional(rollbackFor = Throwable.class) @Override public OrderDonWaybill setWaybill(ReqSetLogisticsPost reqSetLogisticsPost) { OrderDonWaybill orderDonWaybill = Optional.ofNullable(orderDonWaybillMapper.selectOne(Wrappers.lambdaQuery(OrderDonWaybill.class).eq(OrderDonWaybill::getOrderId, reqSetLogisticsPost.getOrderId()))).orElse(new OrderDonWaybill()); orderDonWaybill.setCodeCn(reqSetLogisticsPost.getCodeCn()); orderDonWaybill.setTrackingNumber(reqSetLogisticsPost.getTrackingNumber()); orderDonWaybill.setOrderId(reqSetLogisticsPost.getOrderId()); orderDonWaybill.setRemark(reqSetLogisticsPost.getRemark()); OrderDon orderDon = getById(orderDonWaybill.getOrderId()); Integer type = Optional.ofNullable(reqSetLogisticsPost.getType()).orElse(0); //实物发货 if (type == 0 || type == 1) { if (orderDonWaybill.getId() == null) { try { orderDonWaybillMapper.insert(orderDonWaybill); } catch (DuplicateKeyException e) { return orderDonWaybill; } orderDon.setStatus(OrderDon.Status.transport); updateById(orderDon); //库存扣减逻辑 List relateList = skuStockRelateMapper.selectList(Wrappers.lambdaQuery(GoodsSkuStockRelate.class).eq(GoodsSkuStockRelate::getSkuId, orderDon.getSkuId())); if (!relateList.isEmpty()) { relateList.forEach((relate) -> { goodsDonStockService.upStockRelate(relate.getStockId(), orderDon.getId(), "transport"); }); } } else { if (orderDon.getStatus() == OrderDon.Status.hasPayment) { orderDon.setStatus(OrderDon.Status.transport); updateById(orderDon); } orderDonWaybillMapper.updateById(orderDonWaybill); } //实物分销订单是否存在 DistributeWaitingSendPoints waitingSendPoints = distributeWaitingSendPointsMapper.selectOne(Wrappers.lambdaQuery(DistributeWaitingSendPoints.class) .eq(DistributeWaitingSendPoints::getOrderId, orderDon.getId()) .eq(DistributeWaitingSendPoints::getIsDelivery, false) .last("limit 1")); if (waitingSendPoints != null) { //实物分销订单发货后再 进行提成结算期 waitingSendPoints.setIsDelivery(true); distributeWaitingSendPointsMapper.updateById(waitingSendPoints); } //分销有礼 分销实物是否有发货后用户权益 EquipmentUserDistributeTransportBenefits transportBenefits = transportBenefitsMapper.selectOne(Wrappers.lambdaQuery(EquipmentUserDistributeTransportBenefits.class) .eq(EquipmentUserDistributeTransportBenefits::getOrderId, orderDon.getId()) .eq(EquipmentUserDistributeTransportBenefits::getStatus, EquipmentUserDistributeTransportBenefits.Status.hasPayment).last("limit 1")); if (transportBenefits != null) { if (transportBenefits.getBalance().compareTo(BigDecimal.ZERO) > 0) { //7天后余额到账 transportBenefitsMapper.update(null, Wrappers.lambdaUpdate(EquipmentUserDistributeTransportBenefits.class) .set(EquipmentUserDistributeTransportBenefits::getStatus, EquipmentUserDistributeTransportBenefits.Status.transport) .set(EquipmentUserDistributeTransportBenefits::getSendTime, DateUtil.offsetDay(DateTime.now(), 7)) .eq(EquipmentUserDistributeTransportBenefits::getOrderId, orderDon.getId()) .eq(EquipmentUserDistributeTransportBenefits::getStatus, EquipmentUserDistributeTransportBenefits.Status.hasPayment)); } } } //权益发货 if (type == 0 || type == 2) { GoodsDonSku sku = goodsDonSkuMapper.selectById(orderDon.getSkuId()); if (sku != null && sku.getIsBenefits()) { //是否实物权益已发放 Integer count = receiveBenefitsRecordMapper.selectCount(Wrappers.lambdaQuery(UserPayEquipmentReceiveBenefitsRecord.class) .eq(UserPayEquipmentReceiveBenefitsRecord::getRealOrderId, orderDon.getId())); if (count == 0) { count = availableBenefitsMapper.selectCount(Wrappers.lambdaQuery(UserPayEquipmentAvailableBenefits.class) .eq(UserPayEquipmentAvailableBenefits::getRealOrderId, orderDon.getId())); } if (count == 0) { //实物权益未发 String benefitsKey = RedisService.key.REAL_GOODS_BENEFITS_KEY.getNameFormat(orderDon.getId()); if (redisService.setNx(benefitsKey, orderDon.getId(), RedisService.key.REAL_GOODS_BENEFITS_KEY.getTimeout())) { //发放实物购买权益 if (StrUtil.isNotEmpty(sku.getBenefitsList())) { try { List goodsDonSkus = Jsons.parseList(sku.getBenefitsList(), GoodsDonSku.class); //自购实物 获取权益 userRealOrderBenefitsService.getPayRealGoodsBenefits(orderDon.getUserId(), orderDon.getId(), orderDon.getYhsId(), goodsDonSkus); //清除多余的车票 clearExtraRelation(orderDon); } catch (Exception e) { redisService.del(benefitsKey); } } } } } } return orderDonWaybill; } @Override public void kickOutRelationId(long adminId, ClickOutReq req) { Long orderId = req.getOrderId(); OrderDon orderDon = getById(orderId); if (orderDon != null && orderDonTicketRecordService.exists(orderId)) { throw BusinessRuntimeException.getInstance("Multi-quantity orders cannot clear a single ticket"); } if (orderDon != null && !orderDon.getIsKickOut()) { try { UserRelationKickOutRecord kickOutRecord = new UserRelationKickOutRecord(); kickOutRecord.setUserId(orderDon.getUserId()); kickOutRecord.setRelationId(orderDon.getRelationId()); kickOutRecord.setGoodsId(orderDon.getGoodsId()); kickOutRecord.setSkuId(orderDon.getSkuId()); kickOutRecord.setReason(req.getReason()); kickOutRecord.setImg(req.getImg()); kickOutRecord.setRemark(req.getRemark()); kickOutRecord.setOrderId(orderDon.getId()); kickOutRecord.setOrderNo(orderDon.getOrderNo()); Optional.ofNullable(cmsUserMapper.selectById(adminId)).ifPresent(cmsUser -> kickOutRecord.setOperator(cmsUser.getNickname())); kickOutRecordMapper.insert(kickOutRecord); orderDon.setIsKickOut(true); updateById(orderDon); GoodsDon goodsDon = goodsDonMapper.selectById(orderDon.getGoodsId()); GoodsDonSku goodsDonSku = goodsDonSkuMapper.selectById(orderDon.getSkuId()); //清除车票 orderCommonService.clearUnrealGoodsDetail(orderDon, goodsDon.getType(), goodsDonSku); } catch (DuplicateKeyException e) { } } } @Override public void unifiedRefund(OrderRefundReq refundReq) throws Exception { OrderDon orderDon = orderDonMapper.selectById(refundReq.getOrderId()); boolean multiQuantityOrder = orderDon != null && orderDonTicketRecordService.exists(orderDon.getId()); if (multiQuantityOrder) { refundMultiQuantityOrder(refundReq, orderDon); return; } GoodsDonSku sku = null; if (orderDon.getSkuId() != null) { sku = goodsDonSkuMapper.selectById(orderDon.getSkuId()); } GoodsDon goodsDon = goodsDonMapper.selectById(orderDon.getGoodsId()); if (StrUtil.isEmpty(refundReq.getRefundType())) { refundReq.setRefundType("money"); } String refundType = refundReq.getRefundType(); //校验订单是否符合退款 checkOrderRefund(orderDon, goodsDon, sku, refundReq); //空订单关闭 if (orderDon.getMoney().compareTo(BigDecimal.ZERO) == 0 && orderDon.getBalance().compareTo(BigDecimal.ZERO) == 0 && orderDon.getRelationId() == 0) { orderDon.setStatus(OrderDon.Status.refund); orderDonMapper.updateById(orderDon); if (goodsDon.getType() == 2) { //扣除用户自身实物权益 userRealOrderBenefitsService.deductUserRealGoodsBenefits(orderDon.getId()); } return; } //调用第三方接口退款 String outNo = StrUtil.EMPTY; if (refundReq.getRefundMoney() != null && refundReq.getRefundMoney().compareTo(BigDecimal.ZERO) > 0) { outNo = orderRefundService.centerRefund(refundReq, orderDon); } orderDon.setRefundDesc(refundReq.getRefundDesc()); //记录退款信息 commonRefund(orderDon, refundReq.getRefundMoney(), refundReq.getOperator(), goodsDon, sku, outNo, refundType, refundReq.getIsOnly(), refundReq.getRefundBalance(), refundReq.getChatImg(), refundReq.getDeductBalance(), refundReq.getDpRelationIds(), multiQuantityOrder); //修改订单状态 orderDon.setStatus(OrderDon.Status.refund); orderDonMapper.updateById(orderDon); //续费升级订单 if (orderDon.getOrderType() == 2 && (orderDon.getGoodsId() == Constant.CLAUDE_CODE_GOODS_ID || sku.getIsMirror())) { mirrorUserCleatService.handleRenewUpgradeOrders(orderDon, sku); } //提交过工单的订单 //将用户异常订单关闭 workOrderMapper.update(null, Wrappers.lambdaUpdate(WorkOrder.class) .set(WorkOrder::getStatus, WorkOrder.Status.close) .eq(WorkOrder::getStatus, WorkOrder.Status.waiting) .eq(WorkOrder::getUserId, orderDon.getUserId()) .eq(WorkOrder::getRelationId, orderDon.getRelationId())); //将GPT规格的车队 下次可停车时间置空 groupRelationFrontService.setGroupsTripsParkingTimeEmpty(sku, orderDon.getRelationId()); } /** * A multi-quantity order is one payment backed by independent ticket slots. * Ticket reclaim, provider refund and order-level side effects are deliberately split so * a provider call never participates in a database transaction. */ private void refundMultiQuantityOrder(OrderRefundReq refundReq, OrderDon orderDon) throws Exception { refundMultiQuantityOrder(refundReq, orderDon, 5); } private void refundMultiQuantityOrder(OrderRefundReq refundReq, OrderDon orderDon, int leaseMinutes) throws Exception { Long orderId = orderDon.getId(); OrderDonBusinessEvent existing = orderDonBusinessEventService.get(orderId, OrderDonBusinessEvent.Type.refund); if (orderDon.getStatus() == OrderDon.Status.refund && existing != null && existing.getStatus() == OrderDonBusinessEvent.Status.success) { return; } validateMultiQuantityWholeRefund(refundReq, orderDon); if (requiresManualMultiQuantityRefund(orderDon)) { throw BusinessRuntimeException.getInstance("PayPal/Stripe批量订单请人工核对后退款"); } multiQuantityOrderService.prepareRefund(orderId); OrderDonBusinessEvent refundEvent = orderDonBusinessEventService.get( orderId, OrderDonBusinessEvent.Type.refund); if (refundEvent != null && refundEvent.getStatus() == OrderDonBusinessEvent.Status.processing) { tryFinalizeProcessingRefund(orderDon, refundReq, refundEvent); return; } String workerToken = UUID.randomUUID().toString().replace("-", ""); orderDonTicketRecordService.startRefund(orderId, workerToken); GoodsDon goodsDon = goodsDonMapper.selectById(orderDon.getGoodsId()); GoodsDonSku sku = orderDon.getSkuId() == null ? null : goodsDonSkuMapper.selectById(orderDon.getSkuId()); String refundType = getMultiQuantityRefundType(orderDon); String outRefundNo = buildMultiQuantityRefundNo(orderId); if (requiresExternalRefund(orderDon)) { try { orderRefundService.centerRefund(refundReq, orderDon, outRefundNo); } catch (Exception e) { log.error("Multi-quantity refund submission failed, orderId:{}", orderId, e); boolean restored = false; try { ProviderRefundStatus status = queryProviderRefundStatus(orderDon, outRefundNo); if (status == ProviderRefundStatus.FAILED) { orderDonTicketRecordService.restoreRefund(orderId, workerToken, "provider refund submission failed"); restored = true; } } catch (Exception queryError) { log.warn("Unable to confirm refund submission result, orderId:{}", orderId, queryError); } if (restored) { throw BusinessRuntimeException.getInstance("退款申请失败,原车票已恢复"); } throw BusinessRuntimeException.getInstance("退款申请状态确认中,请稍后查询"); } if (orderDon.getType() == OrderDon.Type.WeChat) { // WeChat's refund API only acknowledges the request. Finalize on its SUCCESS // notification or a later query; tickets remain frozen meanwhile. return; } } final String finalWorkerToken = workerToken; transactionTemplate.executeWithoutResult(status -> finishMultiQuantityRefund( refundReq, orderDon, goodsDon, sku, outRefundNo, refundType, finalWorkerToken)); } private void tryFinalizeProcessingRefund(OrderDon order, OrderRefundReq request, OrderDonBusinessEvent event) throws Exception { if (StrUtil.isBlank(event.getWorkerToken())) { throw BusinessRuntimeException.getInstance("订单退款状态异常"); } String outRefundNo = buildMultiQuantityRefundNo(order.getId()); ProviderRefundStatus status = queryProviderRefundStatus(order, outRefundNo); if (status == ProviderRefundStatus.SUCCESS) { GoodsDon goods = goodsDonMapper.selectById(order.getGoodsId()); GoodsDonSku sku = order.getSkuId() == null ? null : goodsDonSkuMapper.selectById(order.getSkuId()); transactionTemplate.executeWithoutResult(tx -> finishMultiQuantityRefund( request, order, goods, sku, outRefundNo, getMultiQuantityRefundType(order), event.getWorkerToken())); return; } if (status == ProviderRefundStatus.FAILED) { orderDonTicketRecordService.restoreRefund(order.getId(), event.getWorkerToken(), "provider refund failed or closed"); throw BusinessRuntimeException.getInstance("退款失败,原车票已恢复"); } throw BusinessRuntimeException.getInstance("订单退款正在处理中,请稍后查询退款状态"); } @Override public int recoverMultiQuantityRefunds(int limit, int leaseMinutes) { int boundedLimit = Math.max(1, Math.min(limit, 500)); int boundedLeaseMinutes = Math.max(1, Math.min(leaseMinutes, 120)); int processed = 0; for (Long orderId : orderDonTicketRecordService.expiredRefundOrderIds(boundedLeaseMinutes, boundedLimit)) { OrderDon order = orderDonMapper.selectById(orderId); if (order == null || order.getStatus() == OrderDon.Status.refund) { continue; } try { recoverMultiQuantityRefund(order, boundedLeaseMinutes); } catch (Exception e) { log.error("Multi-quantity refund recovery failed, orderId:{}", orderId, e); } processed++; } return processed; } private void recoverMultiQuantityRefund(OrderDon order, int leaseMinutes) throws Exception { OrderDonBusinessEvent refundEvent = orderDonBusinessEventService.get( order.getId(), OrderDonBusinessEvent.Type.refund); if (refundEvent == null || refundEvent.getStatus() != OrderDonBusinessEvent.Status.processing) { return; } String outRefundNo = buildMultiQuantityRefundNo(order.getId()); ProviderRefundStatus providerStatus = queryProviderRefundStatus(order, outRefundNo); if (providerStatus == ProviderRefundStatus.PROCESSING || providerStatus == ProviderRefundStatus.UNKNOWN) { if (StrUtil.isNotBlank(refundEvent.getWorkerToken())) { orderDonTicketRecordService.deferRefundCheck(order.getId(), refundEvent.getWorkerToken(), "provider refund is not final"); } return; } if (!requiresRefundLeaseReclaim(providerStatus)) { return; } String workerToken = orderDonTicketRecordService.reclaimExpiredRefund( order.getId(), leaseMinutes); if (workerToken == null) { return; } if (providerStatus == ProviderRefundStatus.FAILED) { orderDonTicketRecordService.restoreRefund(order.getId(), workerToken, "provider refund failed or closed"); log.error("Multi-quantity provider refund failed, orderId:{}, outRefundNo:{}", order.getId(), outRefundNo); return; } GoodsDon goodsDon = goodsDonMapper.selectById(order.getGoodsId()); GoodsDonSku sku = order.getSkuId() == null ? null : goodsDonSkuMapper.selectById(order.getSkuId()); OrderRefundReq request = buildRecoveryRefundRequest(order); transactionTemplate.executeWithoutResult(status -> finishMultiQuantityRefund( request, order, goodsDon, sku, outRefundNo, getMultiQuantityRefundType(order), workerToken)); } private OrderRefundReq buildRecoveryRefundRequest(OrderDon order) { OrderRefundReq request = new OrderRefundReq(); request.setOrderId(order.getId()); request.setRefundMoney(Optional.ofNullable(order.getMoney()).orElse(BigDecimal.ZERO)); request.setRefundBalance(Optional.ofNullable(order.getBalance()).orElse(BigDecimal.ZERO)); request.setRefundDesc(StrUtil.blankToDefault(order.getRefundDesc(), "multi-quantity whole-order refund recovery")); request.setOperator("system-recovery"); request.setIsDiffer(false); request.setRefundType(getMultiQuantityRefundType(order)); return request; } private boolean requiresExternalRefund(OrderDon order) { return Optional.ofNullable(order.getMoney()).orElse(BigDecimal.ZERO).compareTo(BigDecimal.ZERO) > 0 && order.getType() != OrderDon.Type.BALANCE; } private String refundShopId(OrderDon order) { return StrUtil.blankToDefault(order.getShopId(), order.getType() == OrderDon.Type.WeChat ? ShopConfig.WX_DEFAULT_APP_ID : ShopConfig.ZFB_DEFAULT_APP_ID); } private ProviderRefundStatus queryProviderRefundStatus(OrderDon order, String outRefundNo) throws Exception { if (!requiresExternalRefund(order)) { return ProviderRefundStatus.SUCCESS; } if (order.getType() == OrderDon.Type.WeChat) { Map result = weChatService.getWxRefundDetail( weChatConfig.getAppid(), refundShopId(order), outRefundNo); return parseWxRefundStatus(result, order, outRefundNo); } if (order.getType() == OrderDon.Type.ALI_PAY) { com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse response = queryAliRefund(order, outRefundNo); if (response == null) { return ProviderRefundStatus.UNKNOWN; } if (!StrUtil.equals(order.getOrderNo(), response.getOutTradeNo()) || !StrUtil.equals(outRefundNo, response.getOutRequestNo()) || !StrUtil.equals(order.getTransactionId(), response.getTradeNo())) { throw BusinessRuntimeException.getInstance("支付宝退款查询订单信息不匹配"); } BigDecimal expected = Optional.ofNullable(order.getMoney()).orElse(BigDecimal.ZERO); BigDecimal actual = yuanToCent(response.getRefundAmount()); return actual.compareTo(expected) == 0 ? ProviderRefundStatus.SUCCESS : ProviderRefundStatus.UNKNOWN; } return ProviderRefundStatus.UNKNOWN; } private com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse queryAliRefund( OrderDon order, String outRefundNo) throws Exception { AlipayClient alipayClient = AliPayClientFactory.getAlipayClient( refundShopId(order)); com.alipay.api.request.AlipayTradeFastpayRefundQueryRequest request = new com.alipay.api.request.AlipayTradeFastpayRefundQueryRequest(); AlipayTradeFastpayRefundQueryModel model = new AlipayTradeFastpayRefundQueryModel(); model.setTradeNo(order.getTransactionId()); model.setOutRequestNo(outRefundNo); request.setBizModel(model); ShopConfig shopConfig = shopConfigMapper.selectOne(Wrappers.lambdaQuery(ShopConfig.class) .eq(ShopConfig::getAppId, order.getShopId()).last("limit 1")); com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse response = shopConfig != null && Boolean.TRUE.equals(shopConfig.getIsCert()) ? alipayClient.certificateExecute(request) : alipayClient.execute(request); return response != null && response.isSuccess() ? response : null; } private ProviderRefundStatus parseWxRefundStatus(Map result, OrderDon order, String outRefundNo) { if (result == null || !"SUCCESS".equals(result.get("result_code"))) { return ProviderRefundStatus.UNKNOWN; } int index = findWxRefundIndex(result, outRefundNo); if (index < 0) { return ProviderRefundStatus.UNKNOWN; } if (!StrUtil.equals(order.getTransactionId(), result.get("transaction_id")) || !StrUtil.equals(order.getOrderNo(), result.get("out_trade_no")) || !StrUtil.equals(refundShopId(order), result.get("mch_id")) || parseCent(result.get("total_fee")) != Optional.ofNullable(order.getMoney()).orElse(BigDecimal.ZERO).longValueExact() || parseCent(result.get("refund_fee_" + index)) != Optional.ofNullable(order.getMoney()).orElse(BigDecimal.ZERO).longValueExact()) { throw BusinessRuntimeException.getInstance("微信退款查询订单信息不匹配"); } return mapWxRefundStatus(result.get("refund_status_" + index)); } private int findWxRefundIndex(Map result, String outRefundNo) { int count; try { count = Integer.parseInt(result.getOrDefault("refund_count", "0")); } catch (NumberFormatException e) { return -1; } for (int index = 0; index < count; index++) { if (StrUtil.equals(outRefundNo, result.get("out_refund_no_" + index))) { return index; } } return -1; } private ProviderRefundStatus mapWxRefundStatus(String status) { if ("SUCCESS".equals(status)) { return ProviderRefundStatus.SUCCESS; } if ("PROCESSING".equals(status)) { return ProviderRefundStatus.PROCESSING; } if ("CHANGE".equals(status) || "REFUNDCLOSE".equals(status)) { return ProviderRefundStatus.FAILED; } return ProviderRefundStatus.UNKNOWN; } private long parseCent(String value) { try { return Long.parseLong(value); } catch (Exception e) { throw BusinessRuntimeException.getInstance("退款金额格式异常"); } } private BigDecimal yuanToCent(String value) { try { return new BigDecimal(value).multiply(BigDecimal.valueOf(100)) .setScale(0, RoundingMode.UNNECESSARY); } catch (Exception e) { throw BusinessRuntimeException.getInstance("退款金额格式异常"); } } static boolean requiresManualMultiQuantityRefund(OrderDon order) { return order != null && (order.getType() == OrderDon.Type.PAYPAL || order.getType() == OrderDon.Type.STRIPE); } static boolean requiresRefundLeaseReclaim(ProviderRefundStatus status) { return status == ProviderRefundStatus.SUCCESS || status == ProviderRefundStatus.FAILED; } enum ProviderRefundStatus { SUCCESS, PROCESSING, FAILED, UNKNOWN } private void validateMultiQuantityWholeRefund(OrderRefundReq refundReq, OrderDon orderDon) { if (orderDon.getStatus() == OrderDon.Status.noPayment || orderDon.getStatus() == OrderDon.Status.close) { throw BusinessRuntimeException.getInstance("订单{0}状态不支持退款", orderDon.getStatus()); } if (orderDon.getStatus() == OrderDon.Status.refund) { throw BusinessRuntimeException.getInstance("该订单已退款"); } if (orderDon.getCouponUserId() != null && orderDon.getCouponUserId() != 0) { throw BusinessRuntimeException.getInstance("批量订单不支持优惠券退款"); } BigDecimal money = Optional.ofNullable(orderDon.getMoney()).orElse(BigDecimal.ZERO); BigDecimal balance = Optional.ofNullable(orderDon.getBalance()).orElse(BigDecimal.ZERO); BigDecimal refundMoney = Optional.ofNullable(refundReq.getRefundMoney()).orElse(BigDecimal.ZERO); BigDecimal requestedBalance = refundReq.getRefundBalance(); if (refundMoney.compareTo(money) != 0 || (requestedBalance != null && requestedBalance.compareTo(balance) != 0) || Boolean.TRUE.equals(refundReq.getIsOnly()) || Boolean.TRUE.equals(refundReq.getIsDiffer())) { throw BusinessRuntimeException.getInstance("批量订单仅支持整单退款,现金和余额必须全部退回"); } refundReq.setRefundMoney(money); refundReq.setRefundBalance(balance); refundReq.setRefundType(getMultiQuantityRefundType(orderDon)); } private void reclaimMultiQuantityTickets(OrderDon orderDon, String workerToken) { List records = orderDonTicketRecordService.listByOrderId(orderDon.getId()); int expected = Optional.ofNullable(orderDon.getNum()).orElse(records.size()); if (records.size() != expected) { throw BusinessRuntimeException.getInstance("订单车票数量不匹配"); } for (OrderDonTicketRecord record : records) { if (isTicketAlreadyReclaimed(record)) { continue; } if (record.getStatus() != OrderDonTicketRecord.Status.refunding) { throw BusinessRuntimeException.getInstance("订单车票状态异常,序号: {0}", record.getItemIndex()); } if (currentTicketRelationId(record) != null) { clearMultiQuantityTicket(orderDon, record); } if (!orderDonTicketRecordService.markRefunded(record.getId(), workerToken)) { throw BusinessRuntimeException.getInstance("车票退款状态更新冲突,序号: {0}", record.getItemIndex()); } } if (!areAllMultiQuantityTicketsReclaimed(orderDon, orderDonTicketRecordService.listByOrderId(orderDon.getId()))) { throw BusinessRuntimeException.getInstance("订单车票未全部回收"); } } static boolean areAllMultiQuantityTicketsReclaimed(OrderDon orderDon, List records) { int expected = Optional.ofNullable(orderDon.getNum()).orElse(records.size()); if (records.size() != expected) { return false; } return records.stream().allMatch(CmsOrderDonServiceImpl::isTicketAlreadyReclaimed); } static boolean isTicketAlreadyReclaimed(OrderDonTicketRecord record) { return record.getStatus() == OrderDonTicketRecord.Status.refunded || record.getStatus() == OrderDonTicketRecord.Status.cleared; } private void clearMultiQuantityTicket(OrderDon orderDon, OrderDonTicketRecord record) { Long relationId = currentTicketRelationId(record); OrderDonTicketRecord activeRecord = orderDonTicketRecordService.getActiveByRelationIdForUpdate( relationId); if (activeRecord == null || !activeRecord.getId().equals(record.getId()) || activeRecord.getStatus() != OrderDonTicketRecord.Status.refunding || !orderDon.getId().equals(activeRecord.getOrderId())) { throw BusinessRuntimeException.getInstance("车票分配记录已变更,relationId: {0}", relationId); } GroupsRelation relation = relationMapper.selectByIdForUpdate(relationId); if (relation == null) { throw BusinessRuntimeException.getInstance("退款车票不存在,relationId: {0}", relationId); } if (relation.getUserId() == null || relation.getUserId() == 0) { return; } if (!isMultiQuantityTicketOwner(orderDon, relation)) { throw BusinessRuntimeException.getInstance("车票归属异常,relationId: {0}", relation.getId()); } relationClearService.clearRefundTicket(relation, activeRecord); } private Long currentTicketRelationId(OrderDonTicketRecord record) { return record.getRelationId(); } static boolean isMultiQuantityTicketOwner(OrderDon orderDon, GroupsRelation relation) { return orderDon != null && relation != null && orderDon.getUserId() != null && orderDon.getUserId().equals(relation.getUserId()); } private void finishMultiQuantityRefund(OrderRefundReq refundReq, OrderDon orderDon, GoodsDon goodsDon, GoodsDonSku sku, String outRefundNo, String refundType, String workerToken) { OrderDon latest = orderDonMapper.selectByIdForUpdate(orderDon.getId()); if (latest == null) { throw BusinessRuntimeException.getInstance("订单不存在"); } OrderDonBusinessEvent refundEvent = orderDonBusinessEventService.get( latest.getId(), OrderDonBusinessEvent.Type.refund); if (refundEvent != null && refundEvent.getStatus() == OrderDonBusinessEvent.Status.success) { return; } if (latest.getStatus() == OrderDon.Status.refund) { throw BusinessRuntimeException.getInstance("订单退款状态与幂等事件不一致"); } if (refundEvent == null || refundEvent.getStatus() != OrderDonBusinessEvent.Status.processing || !StrUtil.equals(workerToken, refundEvent.getWorkerToken())) { throw BusinessRuntimeException.getInstance("订单退款执行权已失效"); } reclaimMultiQuantityTickets(latest, workerToken); latest.setRefundDesc(refundReq.getRefundDesc()); latest.setRefundMoney(latest.getMoney()); latest.setRefundBalance(Optional.ofNullable(latest.getBalance()).orElse(BigDecimal.ZERO)); Integer returnedBalance = userBalanceSourceRecordMapper.selectCount( Wrappers.lambdaQuery(UserBalanceSourceRecord.class) .eq(UserBalanceSourceRecord::getOrderId, latest.getId()) .eq(UserBalanceSourceRecord::getSource, UserBalanceSourceRecord.Source.refund)); if ((OrderRefundReq.RefundType.balance.name().equals(refundType) || OrderRefundReq.RefundType.bxj.name().equals(refundType)) && latest.getRefundBalance().compareTo(BigDecimal.ZERO) > 0 && returnedBalance != null && returnedBalance > 0) { throw BusinessRuntimeException.getInstance("订单余额已退回,退款幂等状态异常"); } commonRefund(latest, latest.getRefundMoney(), refundReq.getOperator(), goodsDon, sku, outRefundNo, refundType, null, latest.getRefundBalance(), refundReq.getChatImg(), refundReq.getDeductBalance(), null, true); refundUsedGalaxyCoin(latest); latest.setStatus(OrderDon.Status.refund); if (orderDonMapper.update(latest, Wrappers.lambdaUpdate(OrderDon.class) .ne(OrderDon::getStatus, OrderDon.Status.refund) .eq(OrderDon::getId, latest.getId())) != 1) { throw BusinessRuntimeException.getInstance("订单退款状态更新冲突"); } if (!orderDonBusinessEventService.markSuccess(latest.getId(), OrderDonBusinessEvent.Type.refund, workerToken)) { throw BusinessRuntimeException.getInstance("订单退款幂等状态更新冲突"); } } private void refundUsedGalaxyCoin(OrderDon orderDon) { OrderDonGalaxyCoinRecord record = orderDonGalaxyCoinRecordMapper.selectOne( Wrappers.lambdaQuery(OrderDonGalaxyCoinRecord.class) .eq(OrderDonGalaxyCoinRecord::getOrderId, orderDon.getId()) .eq(OrderDonGalaxyCoinRecord::getSource, OrderDonGalaxyCoinRecord.Source.ordinary.ordinal()) .last("limit 1")); if (record == null || record.getGalaxyCoin() == null || record.getGalaxyCoin().compareTo(BigDecimal.ZERO) <= 0) { return; } userGalaxyCoinService.recordOrderRefundGalaxyCoinIfValid(orderDon.getId(), record.getUserId(), record.getGalaxyCoin(), OrderDonGalaxyCoinRecord.Source.ordinary); } private String getMultiQuantityRefundType(OrderDon orderDon) { BigDecimal money = Optional.ofNullable(orderDon.getMoney()).orElse(BigDecimal.ZERO); BigDecimal balance = Optional.ofNullable(orderDon.getBalance()).orElse(BigDecimal.ZERO); if (money.compareTo(BigDecimal.ZERO) > 0 && balance.compareTo(BigDecimal.ZERO) > 0) { return OrderRefundReq.RefundType.bxj.name(); } if (money.compareTo(BigDecimal.ZERO) == 0 && balance.compareTo(BigDecimal.ZERO) > 0) { return OrderRefundReq.RefundType.balance.name(); } return OrderRefundReq.RefundType.money.name(); } private String buildMultiQuantityRefundNo(Long orderId) throws Exception { return Codec.DoDigest.custom() .setAlgorithm(Codec.DoDigest.Algorithm.MD5) .setStringData("multi-order-refund:" + orderId) .toHexString(); } @Override @Transactional(rollbackFor = Throwable.class) public void unifiedUpgradeRefund(OrderRefundReq refundReq) throws Exception { UpgradeOrderDon orderDon = upgradeOrderDonMapper.selectById(refundReq.getOrderId()); UpgradePackage upgradePackage = upgradePackageMapper.selectById(orderDon.getPackageId()); if (StrUtil.isEmpty(refundReq.getRefundType())) { refundReq.setRefundType("money"); } String refundType = refundReq.getRefundType(); //校验订单是否符合退款 checkUpgradeOrderRefund(orderDon, refundReq); //空订单关闭 if (orderDon.getMoney().compareTo(BigDecimal.ZERO) == 0 && orderDon.getBalance().compareTo(BigDecimal.ZERO) == 0) { orderDon.setStatus(UpgradeOrderDon.Status.refund); upgradeOrderDonMapper.updateById(orderDon); return; } Long userId = orderDon.getUserId(); List userIdList = null; //检验gpt套餐 if (upgradePackage.getGoodsId() == 18) { userIdList = userBindRelationService.getRelationUserIdList(userId, null); //是否有更高级的套餐 UpgradeOrderDon more = upgradeOrderDonMapper.selectOne(Wrappers.lambdaQuery(UpgradeOrderDon.class) .in(UpgradeOrderDon::getUserId, userIdList) .eq(UpgradeOrderDon::getRelationId, orderDon.getRelationId()) .gt(UpgradeOrderDon::getNum, orderDon.getNum()) .eq(UpgradeOrderDon::getStatus, UpgradeOrderDon.Status.complete) .last("limit 1")); if (more != null) { throw BusinessRuntimeException.getInstance("请先退掉其更高级套餐"); } } //调用第三方接口退款 String outNo = orderRefundService.centerUpgradeRefund(refundReq, orderDon); orderDon.setRefundDesc(refundReq.getRefundDesc()); //记录退款信息 commonUpgradeRefund(orderDon, refundReq.getRefundMoney(), refundReq.getOperator(), upgradePackage, outNo, refundType, refundReq.getIsOnly(), refundReq.getRefundBalance(), refundReq.getChatImg()); //修改订单状态 orderDon.setStatus(UpgradeOrderDon.Status.refund); upgradeOrderDonMapper.updateById(orderDon); //扣除套餐次数 if (upgradePackage.getGoodsId() == 26) { MidjourneyUser midjourneyUser = midjourneyUserMapper.selectOne(Wrappers.lambdaQuery(MidjourneyUser.class).eq(MidjourneyUser::getRelationId, orderDon.getRelationId())); if (midjourneyUser != null) { int update = midjourneyUserMapper.update(null, Wrappers.lambdaUpdate(MidjourneyUser.class) .set(MidjourneyUser::getMjUpgradeNum, midjourneyUser.getMjUpgradeNum() < upgradePackage.getNum() ? 0 : midjourneyUser.getMjUpgradeNum() - upgradePackage.getNum()) .eq(MidjourneyUser::getId, midjourneyUser.getId())); if (update == 0) { log.info("midjourneyUser 套餐包增值失败 id:{},orderId:{}", midjourneyUser.getId(), orderDon.getId()); } Long decr = redisService.decr(RedisService.key.MIDJOURNEY_UPGRADE_LIMIT.getName() + midjourneyUser.getId(), upgradePackage.getNum().longValue()); if (decr < 0) { redisService.set(RedisService.key.MIDJOURNEY_UPGRADE_LIMIT.getName() + midjourneyUser.getId(), 0L); } } } //扣除nano套餐次数 if (upgradePackage.getGoodsId() == Constant.NANO_GOODS_ID) { NanoUserQuota nanoUserQuota = nanoUserQuotaMapper.selectOne(Wrappers.lambdaQuery(NanoUserQuota.class).eq(NanoUserQuota::getRelationId, orderDon.getRelationId()).last("limit 1")); if (nanoUserQuota != null) { int update = nanoUserQuotaMapper.update(null, Wrappers.lambdaUpdate(NanoUserQuota.class) .set(NanoUserQuota::getRemainingQuota, nanoUserQuota.getRemainingQuota() < upgradePackage.getNum() ? 0 : nanoUserQuota.getRemainingQuota() - upgradePackage.getNum()) .set(NanoUserQuota::getTotalQuota, nanoUserQuota.getTotalQuota() <= upgradePackage.getNum() ? nanoUserQuota.getTotalQuota() : nanoUserQuota.getTotalQuota() - upgradePackage.getNum()) .eq(NanoUserQuota::getId, nanoUserQuota.getId())); if (update == 0) { log.info("nano user 套餐包增值失败 id:{},orderId:{}", nanoUserQuota.getId(), orderDon.getId()); } } } //扣除gpt套餐升级 if (upgradePackage.getGoodsId() == 18) { ChatgptUser chatgptUser = chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class).eq(ChatgptUser::getRelationId, orderDon.getRelationId())); if (chatgptUser != null) { GroupsRelation relation = groupsRelationMapper.selectById(chatgptUser.getRelationId()); GroupsTrips groupsTrips = groupsMapper.selectById(relation.getGroupsId()); GoodsDonSku sku = skuMapper.selectById(groupsTrips.getSkuId()); //原先sku的套餐 Long gptLimitTime = sku.getGptLimitTime(); Integer gptLimitNum = sku.getGptLimitNum(); //次等套餐的规格 UpgradeOrderDon secondUpgrade = upgradeOrderDonMapper.selectOne(Wrappers.lambdaQuery(UpgradeOrderDon.class) .in(UpgradeOrderDon::getUserId, userIdList) .eq(UpgradeOrderDon::getRelationId, orderDon.getRelationId()) .lt(UpgradeOrderDon::getNum, orderDon.getNum()) .eq(UpgradeOrderDon::getStatus, UpgradeOrderDon.Status.complete) .last("limit 1")); Long renewSkuId = chatgptUser.getRenewSkuId(); //续费升级 GoodsDonSku renewSku = null; if (renewSkuId != null) { renewSku = goodsDonSkuMapper.selectById(renewSkuId); gptLimitTime = renewSku.getGptLimitTime(); gptLimitNum = renewSku.getGptLimitNum(); } if (secondUpgrade != null) { gptLimitTime = secondUpgrade.getTime(); gptLimitNum = secondUpgrade.getNum(); if (renewSku != null) { //低级套餐 GoodsDonSku secondSkuUpGrade = goodsDonSkuMapper.selectById(secondUpgrade.getSkuId()); //续费升级套餐更高 if (renewSku.getMonths() >= secondSkuUpGrade.getMonths() && renewSku.getGptLimitNum() >= secondSkuUpGrade.getGptLimitNum() && renewSku.getGptLimitTime() <= secondSkuUpGrade.getGptLimitTime()) { gptLimitTime = renewSku.getGptLimitTime(); gptLimitNum = renewSku.getGptLimitNum(); } } } Long packageId = 0l; //降级套餐 if (secondUpgrade != null) { packageId = secondUpgrade.getPackageId(); } int update = chatgptUserMapper.update(null, Wrappers.lambdaUpdate(ChatgptUser.class) .set(ChatgptUser::getLimitTime, gptLimitTime) .set(ChatgptUser::getLimitNum, gptLimitNum) .set(ChatgptUser::getPackageId, packageId) //无次等套餐 不需要清除套餐时间 .set(secondUpgrade == null, ChatgptUser::getClearPackageTime, null) .eq(ChatgptUser::getId, chatgptUser.getId())); if (update == 0) { log.info("chatgptUser 升级套餐包增值失败 id:{},orderId:{}", chatgptUser.getId(), orderDon.getId()); } } } //扣除luma套餐次数 if (upgradePackage.getGoodsId() == Constant.LUMA_GOODS_ID) { LumaUser lumaUser = lumaUserMapper.selectOne(Wrappers.lambdaQuery(LumaUser.class).eq(LumaUser::getRelationId, orderDon.getRelationId())); if (lumaUser != null) { int update = lumaUserMapper.update(null, Wrappers.lambdaUpdate(LumaUser.class) .set(LumaUser::getNum, lumaUser.getNum() < upgradePackage.getNum() ? 0 : lumaUser.getNum() - upgradePackage.getNum()) .eq(LumaUser::getId, lumaUser.getId())); if (update == 0) { log.info("lumaUser 套餐包增值失败 id:{},orderId:{}", lumaUser.getId(), orderDon.getId()); } Long decr = redisService.decr(RedisService.key.LUMA_NUM_LIMIT.getName() + lumaUser.getId(), upgradePackage.getNum().longValue()); if (decr < 0) { redisService.set(RedisService.key.LUMA_NUM_LIMIT.getName() + lumaUser.getId(), 0L); } } } } /** * 退款后 处理关联 */ public void commonUpgradeRefund(UpgradeOrderDon orderDon, BigDecimal refundMoney, String operator, UpgradePackage upgradePackage, String outNo, String refundType, Boolean isOnly, BigDecimal refundBalance, String chatImg) { TASK_POOL.execute(() -> { orderRefundService.refundUpgradeRecord(orderDon, refundMoney, refundBalance, upgradePackage, operator, outNo, refundType, chatImg); //退等比例 if (isOnly != null && isOnly) { userBenefitsService.addUserBalance(orderDon.getUserId(), refundBalance, UserBalanceSourceRecord.Source.refund, 0L, orderDon.getId(), upgradePackage.getName()+ UserBalanceSourceRecord.Source.refund.getDesc(), true); BigDecimal orderDonRefundBalance = Optional.ofNullable(orderDon.getRefundBalance()).orElse(BigDecimal.ZERO); orderDon.setRefundBalance(orderDonRefundBalance.add(refundBalance)); } // //若存在余额支付,退还余额 // if (isOnly == null && orderDon.getBalance().compareTo(BigDecimal.ZERO) > 0) { // userBenefitsService.addUserBalance(orderDon.getUserId(), orderDon.getBalance(), UserBalanceSourceRecord.Source.refund, 0L, orderDon.getId(), upgradePackage.getName() + UserBalanceSourceRecord.Source.refund.getDesc(), true); // } //是否全额退款 // BigDecimal orderDonRefundMoney = Optional.ofNullable(orderDon.getRefundMoney()).orElse(BigDecimal.ZERO); // BigDecimal orderDonRefundBalance = Optional.ofNullable(orderDon.getRefundBalance()).orElse(BigDecimal.ZERO); // if (orderDonRefundMoney.add(orderDonRefundBalance).compareTo(orderDon.getMoney().add(Optional.ofNullable(orderDon.getBalance()).orElse(BigDecimal.ZERO))) == 0) { // //是否使用银河币支付 // OrderDonGalaxyCoinRecord orderDonGalaxyCoinRecord = orderDonGalaxyCoinRecordMapper.selectOne(Wrappers.lambdaQuery(OrderDonGalaxyCoinRecord.class) // .eq(OrderDonGalaxyCoinRecord::getOrderId, orderDon.getId()) // .eq(OrderDonGalaxyCoinRecord::getSource, OrderDonGalaxyCoinRecord.Source.upgrade.ordinal()) // .last("limit 1")); // if (orderDonGalaxyCoinRecord != null) { // //有效时,返还银河币 并记录 // userGalaxyCoinService.recordOrderRefundGalaxyCoinIfValid(orderDon.getId(), orderDonGalaxyCoinRecord.getUserId(), orderDonGalaxyCoinRecord.getGalaxyCoin().abs(), OrderDonGalaxyCoinRecord.Source.upgrade); // } // } //扣除 该笔订单产生的返现银河币 if (orderDon.getMoney().compareTo(BigDecimal.ZERO) > 0) { userGalaxyCoinService.subOrderGenerateGalaxyCoin(orderDon.getId(), OrderDonGalaxyCoinRecord.Source.upgrade); } //余额现金混合退款 if (OrderRefundReq.RefundType.bxj.name().equals(refundType)) { userBenefitsService.addUserBalance(orderDon.getUserId(), refundBalance, UserBalanceSourceRecord.Source.refund, 0L, orderDon.getId(), upgradePackage.getName() + UserBalanceSourceRecord.Source.refund.getDesc(), true); } if (UpgradeOrderDon.Type.BALANCE.getType().equals(refundType)) { if (isOnly == null && orderDon.getMoney().compareTo(BigDecimal.ZERO) > 0) { userBenefitsService.addUserBalance(orderDon.getUserId(), refundMoney, UserBalanceSourceRecord.Source.refund, 0L, orderDon.getId(), upgradePackage.getName() + UserBalanceSourceRecord.Source.refund.getDesc(), true); } } }); } /** * 校验订单是否符合退款操作 */ private void checkUpgradeOrderRefund(UpgradeOrderDon orderDon, OrderRefundReq refundReq) { if (orderDon == null) { throw BusinessRuntimeException.getInstance("订单不存在"); } BigDecimal orderDonRefundMoney = Optional.ofNullable(orderDon.getRefundMoney()).orElse(BigDecimal.ZERO); BigDecimal orderDonRefundBalance = Optional.ofNullable(orderDon.getRefundBalance()).orElse(BigDecimal.ZERO); String refundType = refundReq.getRefundType(); // if (orderDon.getType() == UpgradeOrderDon.Type.BALANCE && !UpgradeOrderDon.Type.BALANCE.getType().equals(refundType)) { // throw BusinessRuntimeException.getInstance("余额支付的订单不支持金额退款"); // } if (UpgradeOrderDon.Status.refund == orderDon.getStatus()) { throw BusinessRuntimeException.getInstance("该订单已退款"); } if (UpgradeOrderDon.Status.noPayment == orderDon.getStatus() || UpgradeOrderDon.Status.close == orderDon.getStatus()) { throw BusinessRuntimeException.getInstance("订单{0}状态不支持退款", orderDon.getStatus()); } BigDecimal refundMoney = refundReq.getRefundMoney(); BigDecimal money = orderDon.getMoney(); BigDecimal balance = orderDon.getBalance(); BigDecimal refundBalance = refundReq.getRefundBalance(); //余额退款 if (UpgradeOrderDon.Type.BALANCE.getType().equals(refundType)) { refundMoney = orderDonRefundBalance.add(refundMoney); if (UpgradeOrderDon.Type.BALANCE != orderDon.getType() && refundMoney.compareTo(money) > 0) { throw BusinessRuntimeException.getInstance("退款余额大于订单支付金额"); } orderDon.setRefundBalance(refundMoney); orderDon.setRefundMoney(BigDecimal.ZERO); } else if (OrderRefundReq.RefundType.bxj.name().equals(refundType)) { balance = balance.add(money); if (refundBalance.compareTo(balance) > 0) { throw BusinessRuntimeException.getInstance("退款余额大于订单支付余额"); } refundBalance = refundBalance.add(orderDonRefundBalance); orderDon.setRefundBalance(refundBalance); refundMoney = orderDonRefundMoney.add(refundMoney); if (refundMoney.compareTo(money) > 0) { throw BusinessRuntimeException.getInstance("退款金额大于订单支付金额"); } orderDon.setRefundMoney(refundMoney); } else { //退金额 refundMoney = orderDonRefundMoney.add(refundMoney); if (refundMoney.compareTo(money) > 0) { throw BusinessRuntimeException.getInstance("退款金额大于订单支付金额"); } orderDon.setRefundMoney(refundMoney); } if (!redisService.setNx(String.format("%s", refundReq.getOrderId()), refundReq.getOrderId(), 10l)) { throw BusinessRuntimeException.getInstance("请勿重复点击,正在退款中.."); } } public String wxRefund(OrderRefundReq refundReq, OrderDon orderDon) throws Exception { if (orderDon.getMoney().compareTo(BigDecimal.ZERO) <=0){ return StrUtil.EMPTY; } long time = System.currentTimeMillis(); //退款订单号 String outRefundNo = Codec.DoDigest.custom() .setAlgorithm(Codec.DoDigest.Algorithm.MD5) .setStringData(time + String.valueOf(orderDon.getId())) .toHexString(); weChatService.refundWxOrder(weChatConfig.getAppid(), Optional.ofNullable(orderDon.getShopId()).orElse(ShopConfig.WX_DEFAULT_APP_ID), orderDon.getTransactionId(), outRefundNo, orderDon.getMoney(), refundReq.getRefundMoney(), weChatConfig.getNotifyDomain() + "manage/order/wx/refund/notify"); //记录退款信息 return outRefundNo; } @Override public void wxRefundNotify(Map map) throws Exception { ShopConfig callbackShop = shopConfigMapper.getByAppId(map.get("mch_id")); if (callbackShop == null || StrUtil.isBlank(callbackShop.getSecret())) { throw BusinessRuntimeException.getInstance("退款回调商户不存在"); } String md5Key = Codec.DoDigest.custom().setAlgorithm(Codec.DoDigest.Algorithm.MD5) .setStringData(callbackShop.getSecret()).toHexString(); String reqInfoXml = Codec.DoCipher.custom() .setAlgorithm(Codec.DoCipher.Trans.AES_ECB_PKCS7Padding) .setBase64Data(map.get("req_info")).setStringKey(md5Key).ofDecrypt().toText(); log.info("=== 退款数据解密: \n {}", reqInfoXml); Map refund = Xmls.toMap(reqInfoXml); String transactionId = refund.get("transaction_id"); OrderDon order = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class) .eq(OrderDon::getTransactionId, transactionId).last("limit 1")); if (order == null) { throw BusinessRuntimeException.getInstance("退款回调订单不存在"); } String expectedRefundNo = buildMultiQuantityRefundNo(order.getId()); boolean multiQuantity = orderDonTicketRecordService.exists(order.getId()); if (!multiQuantity) { log.info("微信普通订单退款回调, transactionId:{}, outRefundNo:{}", transactionId, refund.get("out_refund_no")); return; } if (order.getType() != OrderDon.Type.WeChat) { throw BusinessRuntimeException.getInstance("退款回调支付方式不匹配"); } validateWxRefundCallback(refund, order, callbackShop, expectedRefundNo); String status = refund.get("refund_status"); if ("SUCCESS".equals(status)) { OrderDonBusinessEvent event = orderDonBusinessEventService.get( order.getId(), OrderDonBusinessEvent.Type.refund); if (event != null && event.getStatus() == OrderDonBusinessEvent.Status.success) { return; } if (event == null) { log.error("WeChat refund succeeded without a local refund event, orderId:{}", order.getId()); throw BusinessRuntimeException.getInstance("退款回调缺少本地退款记录"); } if (event.getStatus() == OrderDonBusinessEvent.Status.failed && StrUtil.isBlank(event.getWorkerToken())) { String callbackToken = UUID.randomUUID().toString().replace("-", ""); orderDonTicketRecordService.startRefund(order.getId(), callbackToken); event = orderDonBusinessEventService.get(order.getId(), OrderDonBusinessEvent.Type.refund); } if (event.getStatus() != OrderDonBusinessEvent.Status.processing || StrUtil.isBlank(event.getWorkerToken())) { throw BusinessRuntimeException.getInstance("退款回调状态异常"); } GoodsDon goods = goodsDonMapper.selectById(order.getGoodsId()); GoodsDonSku sku = order.getSkuId() == null ? null : goodsDonSkuMapper.selectById(order.getSkuId()); OrderRefundReq request = buildRecoveryRefundRequest(order); String workerToken = event.getWorkerToken(); transactionTemplate.executeWithoutResult(tx -> finishMultiQuantityRefund( request, order, goods, sku, expectedRefundNo, getMultiQuantityRefundType(order), workerToken)); return; } if ("CHANGE".equals(status) || "REFUNDCLOSE".equals(status)) { OrderDonBusinessEvent event = orderDonBusinessEventService.get( order.getId(), OrderDonBusinessEvent.Type.refund); if (event != null && event.getStatus() == OrderDonBusinessEvent.Status.processing && StrUtil.isNotBlank(event.getWorkerToken())) { orderDonTicketRecordService.restoreRefund(order.getId(), event.getWorkerToken(), "WeChat refund " + status); } log.error("微信批量订单退款失败, orderId:{}, status:{}", order.getId(), status); } } private void validateWxRefundCallback(Map refund, OrderDon order, ShopConfig callbackShop, String expectedRefundNo) { long expectedMoney = Optional.ofNullable(order.getMoney()).orElse(BigDecimal.ZERO).longValueExact(); if (!StrUtil.equals(callbackShop.getAppId(), refundShopId(order)) || !StrUtil.equals(callbackShop.getAppId(), refund.get("mch_id")) || !StrUtil.equals(weChatConfig.getAppid(), refund.get("appid")) || !StrUtil.equals(expectedRefundNo, refund.get("out_refund_no")) || !StrUtil.equals(order.getTransactionId(), refund.get("transaction_id")) || !StrUtil.equals(order.getOrderNo(), refund.get("out_trade_no")) || parseCent(refund.get("total_fee")) != expectedMoney || parseCent(refund.get("refund_fee")) != expectedMoney) { throw BusinessRuntimeException.getInstance("微信退款回调订单信息不匹配"); } } @Override @Transactional(rollbackFor = Throwable.class) public void changeRelation(ChangeRelationReq req) throws Exception { Long relationId = req.getRelationId(); Long groupId = req.getGroupsId(); UserTicketClearedRecord.Source source = req.getSource(); if (source == null) source = UserTicketClearedRecord.Source.change_ticket; GroupsRelation relation = relationMapper.selectById(relationId); Long yhsId = relation == null ? null : relation.getYhsId(); Assert.notNull(relation, "车票已不存在"); if (relation.getRechargeStatus() != null) { throw BusinessRuntimeException.getInstance("代充账号车票无法更换车票"); } //冻结中 不允许更换车票 if (relation.getIsFrozen()) { throw BusinessRuntimeException.getInstance("用户车票冻结中,无法更换车票"); } Integer reservedDays = relation.getReservedDays(); String key = RedisService.key.NETFLIX_CHANGE_TICKET_KEY.getName() + relationId; if (!redisService.setNx(key, relationId, RedisService.key.NETFLIX_CHANGE_TICKET_KEY.getTimeout())) { throw BusinessRuntimeException.getInstance("正在更换车票中.."); } Long userId = relation.getUserId(); List userIdList = userBindRelationService.getRelationUserIdList(userId, null); OrderDonTicketRecord multiTicketRecord = orderDonTicketRecordService.getActiveByRelationIdForUpdate(relationId); if (multiTicketRecord != null && multiTicketRecord.getStatus() == OrderDonTicketRecord.Status.refunding) { throw BusinessRuntimeException.getInstance("订单退款处理中,暂不支持更换车票"); } try { log.info("用户userId:{}更换车票relationId:{}", userId, relationId); OrderDon orderDon = multiTicketRecord == null ? orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class) .in(OrderDon::getUserId, userIdList) .eq(OrderDon::getRelationId, relationId) .notIn(OrderDon::getStatus, Constant.noOrderStatus) .orderByDesc(OrderDon::getId) .last("limit 1")) : orderDonMapper.selectById(multiTicketRecord.getOrderId()); OrderDiscountPlusPurchaseSkuRelation plusPurchaseSkuRelation = null; List plus_relationList = null; if (orderDon == null) { List plusPurchaseSkuRelations = orderDiscountPlusPurchaseSkuRelationMapper.selectPlusRelationByIdAndUserId(relation.getId(), relation.getUserId()); for (OrderDiscountPlusPurchaseSkuRelation data : plusPurchaseSkuRelations) { String relationIds = data.getRelationIds(); if (StrUtil.isEmpty(relationIds)) { return; } List relationList = Jsons.parseList(relationIds, Long.class); if (relationList.contains(relationId)) { plusPurchaseSkuRelation = data; break; } } if (plusPurchaseSkuRelation != null) { String relationIds = plusPurchaseSkuRelation.getRelationIds(); if (StrUtil.isNotEmpty(relationIds)) { plus_relationList = Jsons.parseList(relationIds, Long.class); if (plus_relationList.contains(relationId)) { orderDon = new OrderDon(); GroupsRelationView groupsRelationView = beanSearcher.searchFirst(GroupsRelationView.class, MapUtils.builder().field(GroupsRelationView::getId, relationId).build()); Assert.notNull(groupsRelationView, "车票已不存在"); orderDon.setUserId(groupsRelationView.getUserId()); orderDon.setSkuId(groupsRelationView.getSkuId()); orderDon.setGoodsId(groupsRelationView.getGoodsId()); orderDon.setStatus(OrderDon.Status.complete); orderDon.setOrderType(1); } } } } Assert.notNull(orderDon, "订单不存在"); // if (orderDon.getStatus() == OrderDon.Status.refund) { // throw BusinessRuntimeException.getInstance("订单已退款"); // } //获取新车队 LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(GroupsTrips.class) .eq(GroupsTrips::getSkuId, orderDon.getSkuId()) .eq(GroupsTrips::getId, groupId) .ne(GroupsTrips::getId, relation.getGroupsId()) .ne(GroupsTrips::getStatus, GroupsTrips.Status.down) .last("limit 1"); GroupsTrips groups = groupsMapper.selectOne(queryWrapper); if (groups == null) throw BusinessRuntimeException.getInstance("车队不存在或已下架"); if (!Constant.AI_goodsIds.contains(orderDon.getGoodsId()) && groups.getAvailableNum() <= 0) { throw BusinessRuntimeException.getInstance("该新车位人员已满"); } Integer relationNum = groups.getNum(); //获取新车队车位 GroupsRelation newRelation; Boolean isOutside = req.getIsOutside(); if (Constant.AI_goodsIds.contains(orderDon.getGoodsId()) && ((isOutside != null && isOutside) || groups.getAvailableNum() == 0) && groups.getNum() > 1) { //Ai车队 额外增加车位 并不让上车 newRelation = getChatGPTOutsideRelation(orderDon.getUserId(), groupId, relationNum, 1); } else { newRelation = getRelationThisTrips(orderDon.getUserId(), groupId, 1); } //校验座位 int update = groupsRelationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class) .set(GroupsRelation::getUserId, userId) .eq(GroupsRelation::getUserId, 0) .eq(GroupsRelation::getId, newRelation.getId())); if (update == 0) { throw BusinessRuntimeException.getInstance("系统繁忙,请重新操作"); } newRelation.setUserId(userId); newRelation.setStartTime(relation.getStartTime()); newRelation.setExpiryTime(relation.getExpiryTime()); newRelation.setYhsId(yhsId); newRelation.setCmt(relation.getCmt()); newRelation.setCustomerService(relation.getCustomerService()); if (newRelation.getStatus() != GroupsRelation.Status.outside) { newRelation.setStatus(GroupsRelation.Status.validity); } //代充信息 newRelation.setRechargeStatus(relation.getRechargeStatus()); newRelation.setRechargeNum(relation.getRechargeNum()); newRelation.setRechargeRemainNum(relation.getRechargeRemainNum()); newRelation.setSubmitTime(relation.getSubmitTime()); newRelation.setSendTime(relation.getSendTime()); newRelation.setAccount(relation.getAccount()); newRelation.setPassword(relation.getPassword()); newRelation.setGptTaskId(relation.getGptTaskId()); newRelation.setCardKey(relation.getCardKey()); newRelation.setGptToken(relation.getGptToken()); //补偿时长 newRelation.setCompensationDays(relation.getCompensationDays()); //清除原先车票 relation.setNewRelationId(newRelation.getId()); relationClearService.clearTicket(relation, source); //兜底处理 多余车票 if (multiTicketRecord == null) { clearOtherChangeTicket(userId, relationId, newRelation, orderDon.getSkuId()); } //奈飞车票 if (orderDon.getGoodsId() == 1) { User user = userMapper.selectById(newRelation.getUserId()); GroupsTrips groupsTrips = groupsMapper.selectById(newRelation.getGroupsId()); Account account = accountMapper.selectById(groupsTrips.getAccountId()); AtomicInteger start = new AtomicInteger(1); groupsRelationNetflixService.timingSetNetflix(newRelation.getNum(), user.getNickname(), account.getAccount(), account.getPassword(), newRelation.getExpiryTime(), start); } if (plusPurchaseSkuRelation != null && plus_relationList != null) { plus_relationList.remove(relationId); plus_relationList.add(newRelation.getId()); plusPurchaseSkuRelation.setRelationIds(plus_relationList.toString()); orderDiscountPlusPurchaseSkuRelationMapper.updateById(plusPurchaseSkuRelation); } if (source != null && source == UserTicketClearedRecord.Source.abnormal) { //将用户异常订单关闭 workOrderMapper.update(null, Wrappers.lambdaUpdate(WorkOrder.class) .set(WorkOrder::getStatus, WorkOrder.Status.close) .eq(WorkOrder::getStatus, WorkOrder.Status.waiting) .eq(WorkOrder::getUserId, userId) .eq(WorkOrder::getRelationId, relationId)); } if (orderDon.getMoney() != null && orderDon.getMoney().compareTo(BigDecimal.ZERO) == 0) { //是否是实物权益车票 UserPayEquipmentReceiveBenefitsRecord receiveBenefitsRecord = receiveBenefitsRecordMapper.selectOne(Wrappers.lambdaQuery(UserPayEquipmentReceiveBenefitsRecord.class) .eq(UserPayEquipmentReceiveBenefitsRecord::getTicketOrderId, orderDon.getId()) .eq(UserPayEquipmentReceiveBenefitsRecord::getRelationId, relationId).last("limit 1")); if (receiveBenefitsRecord != null) { receiveBenefitsRecord.setRelationId(newRelation.getId()); receiveBenefitsRecordMapper.updateById(receiveBenefitsRecord); } } if ((newRelation.getStartTime() == null || newRelation.getExpiryTime() == null)) { if (multiTicketRecord != null) { relationMapper.updateById(newRelation); return; } List orderDonList = orderDonMapper.selectList(Wrappers.lambdaQuery(OrderDon.class).eq(OrderDon::getUserId, userId).eq(OrderDon::getRelationId, relationId).eq(OrderDon::getStatus, OrderDon.Status.hasPayment)); if (orderDonList.isEmpty()) { orderDonList = orderDonMapper.selectList(Wrappers.lambdaQuery(OrderDon.class) .eq(OrderDon::getUserId, userId) .eq(OrderDon::getRelationId, relationId) .eq(OrderDon::getStatus, OrderDon.Status.complete) .orderByDesc(OrderDon::getId) .last("limit 1")); } if (groups.getStatus() == GroupsTrips.Status.waiting) { orderDonList.forEach(hasPaymentOrder -> { hasPaymentOrder.setRelationId(newRelation.getId()); orderDonMapper.updateById(hasPaymentOrder); }); relationMapper.updateById(newRelation); return; } orderDonList.forEach((hasPaymentOrder) -> { //如果续费增加时间,如果首次则设置当前时间为初始时间 Date expiryTime = Optional.ofNullable(newRelation.getExpiryTime()).orElse(new Date()); GoodsDonSku donSku = skuMapper.selectById(hasPaymentOrder.getSkuId()); Date newDate; if (donSku.getDays() != null && donSku.getDays() > 0) { newDate = DateUtil.offsetDay(expiryTime, donSku.getDays() * hasPaymentOrder.getNum()); } else { newDate = DateUtil.offset(expiryTime, DateField.MONTH, donSku.getMonths() * hasPaymentOrder.getNum()); } newRelation.setExpiryTime(newDate); newRelation.setStartTime(newRelation.getStartTime() == null ? new Date() : null); relationMapper.updateById(newRelation); hasPaymentOrder.setStatus(OrderDon.Status.complete); hasPaymentOrder.setRelationId(newRelation.getId()); orderDonMapper.updateById(hasPaymentOrder); }); return; } List orderDonList = multiTicketRecord == null ? orderDonMapper.selectList(Wrappers.lambdaQuery(OrderDon.class).eq(OrderDon::getUserId, userId).eq(OrderDon::getRelationId, relationId).eq(OrderDon::getStatus, OrderDon.Status.complete)) : java.util.Collections.emptyList(); orderDonList.forEach(completeOrder->{ completeOrder.setRelationId(newRelation.getId()); orderDonMapper.updateById(completeOrder); }); log.info("userId:{}更换车票,原座位号relationId:{}======>新座位号newRelationId:{}", newRelation.getUserId(), relationId, newRelation.getId()); relationMapper.updateById(newRelation); //更换车票 调整GPT相关规格新车队 上车数 GoodsDonSku sku = goodsDonSkuMapper.selectById(groups.getSkuId()); groupRelationFrontService.adjustGroupsParkingTime(newRelation.getId(), sku); //关闭正在恢复的车票 if (orderDon.getGoodsId() == Constant.NETFLIX_GID) { NetflixUserAccountSubmitRecoverRecord recoverRecord = netflixUserAccountSubmitRecoverRecordMapper.selectOne(Wrappers.lambdaQuery(NetflixUserAccountSubmitRecoverRecord.class) .eq(NetflixUserAccountSubmitRecoverRecord::getRelationId, relationId) .in(NetflixUserAccountSubmitRecoverRecord::getUserId, userIdList) .eq(NetflixUserAccountSubmitRecoverRecord::getStatus, 0) .last("limit 1")); if (recoverRecord != null) { recoverRecord.setStatus(3); netflixUserAccountSubmitRecoverRecordMapper.updateById(recoverRecord); newRelation.setExpiryTime(DateUtil.offsetDay(DateTime.now(), recoverRecord.getRemainDays().intValue())); relationMapper.updateById(newRelation); } else { if (reservedDays != null) { newRelation.setExpiryTime(DateUtil.offsetDay(DateTime.now(), reservedDays)); relationMapper.updateById(newRelation); } } } } finally { if (redisService.hasKey(key)) { redisService.del(key); } } } public String zfbRefund(OrderRefundReq refundReq, OrderDon orderDon) throws Exception { if (orderDon.getMoney().compareTo(BigDecimal.ZERO) <=0){ return StrUtil.EMPTY; } AlipayTradeRefundResponse response; //支付宝退款 AlipayClient alipayClient = AliPayClientFactory.getAlipayClient(Optional.ofNullable(orderDon.getShopId()).orElse(ShopConfig.ZFB_DEFAULT_APP_ID)); AlipayTradeRefundRequest request = new AlipayTradeRefundRequest(); AlipayTradeRefundModel model = new AlipayTradeRefundModel(); //支付宝交易号 model.setTradeNo(orderDon.getTransactionId()); model.setRefundAmount(refundReq.getRefundMoney().divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_EVEN).toString()); model.setQueryOptions(List.of("gmt_refund_pay")); long time = System.currentTimeMillis(); //退款订单号 String outRefundNo = Codec.DoDigest.custom() .setAlgorithm(Codec.DoDigest.Algorithm.MD5) .setStringData(time + String.valueOf(orderDon.getId())) .toHexString(); //退款请求号 model.setOutRequestNo(outRefundNo); request.setBizModel(model); String shopId = orderDon.getShopId(); ShopConfig shopConfig = shopConfigMapper.selectOne(Wrappers.lambdaQuery(ShopConfig.class).eq(ShopConfig::getAppId, orderDon.getShopId())); if (shopId != null && shopConfig != null && shopConfig.getIsCert()) { response = alipayClient.certificateExecute(request); } else { response = alipayClient.execute(request); } if (!response.isSuccess()) throw BusinessRuntimeException.getInstance("退款失败,e:{0}", response.getSubMsg()); // AliPayRefundRep aliPayRefundRep = Jsons.parseObject(response.getBody(), AliPayRefundRep.class); // AliPayRefundRep.AlipayTradeRefundResponse res = aliPayRefundRep.getResponse(); // orderDon.setRefundMoney(res.getRefundFee().multiply(BigDecimal.valueOf(100))); return outRefundNo; } /** * 校验订单是否符合退款操作 */ private void checkOrderRefund(OrderDon orderDon, GoodsDon goodsDon, GoodsDonSku sku, OrderRefundReq refundReq) { if (orderDon == null) { throw BusinessRuntimeException.getInstance("订单不存在"); } validateOrdinaryRefundRequest(orderDon, refundReq); BigDecimal orderDonRefundMoney = Optional.ofNullable(orderDon.getRefundMoney()).orElse(BigDecimal.ZERO); BigDecimal orderDonRefundBalance = Optional.ofNullable(orderDon.getRefundBalance()).orElse(BigDecimal.ZERO); String refundType = refundReq.getRefundType(); // if (orderDon.getType() == OrderDon.Type.BALANCE && !OrderDon.Type.BALANCE.getType().equals(refundType)) { // throw BusinessRuntimeException.getInstance("余额支付的订单不支持金额退款"); // } if (OrderDon.Status.refund == orderDon.getStatus()) { throw BusinessRuntimeException.getInstance("该订单已退款"); } if (OrderDon.Status.noPayment == orderDon.getStatus() || OrderDon.Status.close == orderDon.getStatus()) { throw BusinessRuntimeException.getInstance("订单{0}状态不支持退款", orderDon.getStatus()); } if (goodsDon.getId() == Constant.NETFLIX_GID) { Integer selectCount = netflixUserChangeOtherTicketRecordMapper.selectCount(Wrappers.lambdaQuery(NetflixUserChangeOtherTicketRecord.class) .eq(NetflixUserChangeOtherTicketRecord::getOrderId, orderDon.getId())); if (selectCount > 0) { throw BusinessRuntimeException.getInstance("该奈飞车票已替换其他车票,无法退款"); } } if (goodsDon.getType() == 1 && sku != null) { if (goodsDon.getId() == Constant.CLAUDE_CODE_GOODS_ID) { //车票是否存在 Long relationId = orderDon.getRelationId(); List userIdList = userBindRelationService.getRelationUserIdList(orderDon.getUserId(), null); Integer selectCount1 = userTicketClearedRecordMapper.selectCount(Wrappers.lambdaQuery(UserTicketClearedRecord.class) .eq(UserTicketClearedRecord::getRelationId, relationId) .in(UserTicketClearedRecord::getUserId, userIdList) .eq(UserTicketClearedRecord::getSource, UserTicketClearedRecord.Source.cc_deduct) .gt(UserTicketClearedRecord::getCreatedTime, orderDon.getCreatedTime())); if (selectCount1 > 0) { //抵扣车票对应的 新车票是否已经退款 Integer selectCount = claudeCodeDeductOrderRelateMapper.selectDeductUpgradeRelation(relationId, userIdList); if (selectCount == 0) { throw BusinessRuntimeException.getInstance("车票已被抵扣升级 无法退款"); } } } //虚拟原订单是否已过期 Integer months = sku.getMonths(); Integer days = sku.getDays(); Integer num = orderDon.getNum(); DateTime now = DateTime.now(); DateTime endTime; if (days != null && days > 0) { endTime = DateUtil.offsetDay(orderDon.getCreatedTime(), days * num); } else { endTime = DateUtil.offsetMonth(orderDon.getCreatedTime(), months * num); } // if (now.isAfter(endTime)) { // throw BusinessRuntimeException.getInstance("该订单车票已过期"); // } if (orderDon.getOrderType() == 1) { //是否有其他付款订单是否过期 OrderDon otherOrder = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class) .eq(OrderDon::getRelationId, orderDon.getRelationId()) .eq(OrderDon::getUserId, orderDon.getUserId()) .ne(OrderDon::getId, orderDon.getId()) .notIn(OrderDon::getStatus, Constant.noOrderAllStatus) .eq(OrderDon::getOrderType, 2) .orderByDesc(OrderDon::getId) .last("limit 1")); if (otherOrder != null) { throw BusinessRuntimeException.getInstance("该笔订单存在续费订单,请先退掉续费订单"); } } } BigDecimal refundMoney = refundReq.getRefundMoney(); BigDecimal refundBalance = refundReq.getRefundBalance(); BigDecimal money = orderDon.getMoney(); BigDecimal balance = orderDon.getBalance(); //余额退款 if (OrderDon.Type.BALANCE.getType().equals(refundType)) { refundMoney = orderDonRefundBalance.add(refundMoney); if (OrderDon.Type.BALANCE != orderDon.getType() && refundMoney.compareTo(money) > 0) { throw BusinessRuntimeException.getInstance("退款余额大于订单支付金额"); } orderDon.setRefundBalance(refundMoney); } else if (OrderRefundReq.RefundType.bxj.name().equals(refundType)) { balance = balance.add(money); if (orderDon.getGoodsId() == Constant.CLAUDE_CODE_GOODS_ID) { ClaudeCodeDeductOrderRelate claudeCodeDeductOrderRelate = claudeCodeDeductOrderRelateMapper.selectOne(Wrappers.lambdaQuery(ClaudeCodeDeductOrderRelate.class).eq(ClaudeCodeDeductOrderRelate::getOrderId, orderDon.getId()).last("limit 1")); //特殊处理 cc抵扣升级订单 余额退款 if (claudeCodeDeductOrderRelate != null && claudeCodeDeductOrderRelate.getDeductMoney() != null) { balance = balance.add(claudeCodeDeductOrderRelate.getDeductMoney()); } } if (refundBalance != null) { if (refundBalance.compareTo(balance) > 0) { throw BusinessRuntimeException.getInstance("退款余额大于订单总金额"); } refundBalance = refundBalance.add(orderDonRefundBalance); orderDon.setRefundBalance(refundBalance); } refundMoney = orderDonRefundMoney.add(refundMoney); if (refundMoney.compareTo(money) > 0) { throw BusinessRuntimeException.getInstance("退款金额大于订单支付金额"); } orderDon.setRefundMoney(refundMoney); } else { //退金额 refundMoney = orderDonRefundMoney.add(refundMoney); if (refundMoney.compareTo(money) > 0) { throw BusinessRuntimeException.getInstance("退款金额大于订单支付金额"); } orderDon.setRefundMoney(refundMoney); } //礼品卡是否已使用 GiftCardUserRecord giftCardUserRecord = null; if (goodsDon.getSpecialType() != null && goodsDon.getSpecialType() == GoodsDon.SpecialType.giftCard) { Integer count = giftCardUserRecordMapper.selectCount(Wrappers.lambdaQuery(GiftCardUserRecord.class) .eq(GiftCardUserRecord::getIsUsed, 1) .eq(GiftCardUserRecord::getOrderId, orderDon.getId())); if (count > 0) { throw BusinessRuntimeException.getInstance("该礼品卡已使用无法退款"); } } if (!redisService.setNx(String.format("%s", refundReq.getOrderId()), refundReq.getOrderId(), 10l)) { throw BusinessRuntimeException.getInstance("请勿重复点击,正在退款中.."); } //礼品卡购买状态 if (giftCardUserRecord != null) { giftCardUserRecord.setIsPay(false); giftCardUserRecordMapper.updateById(giftCardUserRecord); } //校验续费升级规格 // checkMirrorRenewSku(orderDon); } /** * 普通订单退款仍由管理端选择退款方式,但金额字段必须与退款方式一致。 * 否则错误地把纯余额订单按 money 提交时,会清票并关闭订单,却不会退回余额。 */ private void validateOrdinaryRefundRequest(OrderDon orderDon, OrderRefundReq refundReq) { String refundType = refundReq.getRefundType(); boolean supportedType = OrderRefundReq.RefundType.money.name().equals(refundType) || OrderRefundReq.RefundType.balance.name().equals(refundType) || OrderRefundReq.RefundType.bxj.name().equals(refundType); if (!supportedType) { throw BusinessRuntimeException.getInstance("退款类型不正确"); } BigDecimal refundMoney = Optional.ofNullable(refundReq.getRefundMoney()).orElse(BigDecimal.ZERO); BigDecimal refundBalance = Optional.ofNullable(refundReq.getRefundBalance()).orElse(BigDecimal.ZERO); if (refundMoney.signum() < 0 || refundBalance.signum() < 0) { throw BusinessRuntimeException.getInstance("退款金额不能小于0"); } // Existing user-side expired-ticket refunds use money + refundBalance with // isOnly=true to return the balance-funded portion while refunding cash. // Reject only the malformed management request that carries a balance // amount without opting into that established mixed-refund path. if (OrderRefundReq.RefundType.money.name().equals(refundType) && !Boolean.TRUE.equals(refundReq.getIsOnly()) && (refundBalance.signum() > 0 || orderDon.getType() == OrderDon.Type.BALANCE)) { throw BusinessRuntimeException.getInstance("退款类型与余额退款金额不匹配"); } if (OrderRefundReq.RefundType.balance.name().equals(refundType) && !Boolean.TRUE.equals(refundReq.getIsOnly()) && refundMoney.signum() <= 0 && refundBalance.signum() > 0) { throw BusinessRuntimeException.getInstance("退至余额的金额字段不正确"); } } /** * 退款后 处理关联 */ public void commonRefund(OrderDon orderDon, BigDecimal refundMoney, String operator, GoodsDon goodsDon, GoodsDonSku sku, String outNo, String refundType, Boolean isOnly, BigDecimal refundBalance, String chatImg, BigDecimal deductBalance, List dpRelationIds, boolean multiQuantityOrder) { orderRefundService.refundRecord(orderDon, refundMoney, refundBalance, operator, goodsDon, sku, outNo, refundType, chatImg); if (shouldRestoreStock(goodsDon, multiQuantityOrder)) { goodsDonStockService.upStockRelate(null, orderDon.getId(), "refund"); } try { if (!multiQuantityOrder && !Boolean.TRUE.equals(orderDon.getIsDp())) { GroupsRelation relation = relationMapper.selectById(orderDon.getRelationId()); //冻结车票 清除 if (relation != null && Boolean.TRUE.equals(relation.getIsFrozen())) { Optional.ofNullable(receivedRecordMapper.selectOne(Wrappers.lambdaQuery(AccountNetflixFrozenTicketReceivedRecord.class).eq(AccountNetflixFrozenTicketReceivedRecord::getRelationId, relation.getId()).last("limit 1"))) .ifPresent(rr -> { Long sendRelationId = rr.getSendRelationId(); List userIdList = userBindRelationService.getRelationUserIdList(orderDon.getUserId(), null); GroupsRelation sendRelation = groupsRelationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class).eq(GroupsRelation::getId, sendRelationId).in(GroupsRelation::getUserId, userIdList).last("limit 1")); if (sendRelation != null) { relationClearService.clearTicket(sendRelation, UserTicketClearedRecord.Source.manual); } }); } //虚物订单退款,清出车队,车票变为已过期 orderCommonService.clearUnrealGoodsDetail(orderDon, goodsDon.getType(), sku); } } catch (Exception e) { log.error("清除车票错误:{}", StringUtil.getErrorText(e)); } //若存在优惠券使用,退还优惠券 if (orderDon.getCouponUserId() != null && orderDon.getCouponUserId() != 0L) { couponFontService.updateOrderDonCouponStatus(orderDon); } //退等比例 if (isOnly != null && isOnly) { userBenefitsService.addUserBalance(orderDon.getUserId(), refundBalance, UserBalanceSourceRecord.Source.refund, orderDon.getYhsId(), orderDon.getId(), goodsDon.getTitle() + UserBalanceSourceRecord.Source.refund.getDesc(), true); BigDecimal orderDonRefundBalance = Optional.ofNullable(orderDon.getRefundBalance()).orElse(BigDecimal.ZERO); orderDon.setRefundBalance(orderDonRefundBalance.add(refundBalance)); } //若存在余额支付,退还余额 // if (isOnly == null && orderDon.getBalance().compareTo(BigDecimal.ZERO) > 0) { // BigDecimal addBalance = orderDon.getBalance(); // //需额外扣除余额 // if (deductBalance != null && deductBalance.compareTo(BigDecimal.ZERO) > 0) { // addBalance = addBalance.subtract(deductBalance); // } // userBenefitsService.addUserBalance(orderDon.getUserId(), addBalance, UserBalanceSourceRecord.Source.refund, orderDon.getYhsId(), orderDon.getId(), goodsDon.getTitle() + UserBalanceSourceRecord.Source.refund.getDesc(), true); // } //是否全额退款 // BigDecimal orderDonRefundMoney = Optional.ofNullable(orderDon.getRefundMoney()).orElse(BigDecimal.ZERO); // BigDecimal orderDonRefundBalance = Optional.ofNullable(orderDon.getRefundBalance()).orElse(BigDecimal.ZERO); // if (orderDonRefundMoney.add(orderDonRefundBalance).compareTo(orderDon.getMoney().add(Optional.ofNullable(orderDon.getBalance()).orElse(BigDecimal.ZERO))) == 0) { // //是否使用银河币支付 // OrderDonGalaxyCoinRecord orderDonGalaxyCoinRecord = orderDonGalaxyCoinRecordMapper.selectOne(Wrappers.lambdaQuery(OrderDonGalaxyCoinRecord.class) // .eq(OrderDonGalaxyCoinRecord::getSource, OrderDonGalaxyCoinRecord.Source.ordinary.ordinal()) // .eq(OrderDonGalaxyCoinRecord::getOrderId, orderDon.getId()) // .last("limit 1")); // if (orderDonGalaxyCoinRecord != null) { // //有效时,返还银河币 并记录 // userGalaxyCoinService.recordOrderRefundGalaxyCoinIfValid(orderDon.getId(), orderDonGalaxyCoinRecord.getUserId(), orderDonGalaxyCoinRecord.getGalaxyCoin().abs(), OrderDonGalaxyCoinRecord.Source.ordinary); // } // } //扣除 该笔订单产生的返现银河币 if (Optional.ofNullable(orderDon.getMoney()).orElse(BigDecimal.ZERO) .compareTo(BigDecimal.ZERO) > 0) { userGalaxyCoinService.subOrderGenerateGalaxyCoin(orderDon.getId(), OrderDonGalaxyCoinRecord.Source.ordinary); } //余额现金混合退款 if (OrderRefundReq.RefundType.bxj.name().equals(refundType)) { userBenefitsService.addUserBalance(orderDon.getUserId(), refundBalance, UserBalanceSourceRecord.Source.refund, null, orderDon.getId(), orderDon.getPayTitle() + UserBalanceSourceRecord.Source.refund.getDesc(), true); } try { marketFrontService.subSpecificChanceNum(orderDon.getUserId(), orderDon.getId(), orderDon.getGoodsId(), orderDon.getUpdateTime()); if (goodsDon != null && Objects.equals(goodsDon.getType(), 2) && !Boolean.TRUE.equals(orderDon.getIsDistribute())) { equipmentDistributeService.subDistributeBenefitsChance(orderDon.getUserId(), orderDon.getId()); //扣除用户自身实物权益 userRealOrderBenefitsService.deductUserRealGoodsBenefits(orderDon.getId()); } } catch (Exception e) { log.error("wx减少抽奖机会错误,orderId:{},error:{}", orderDon.getId(), e); } if (OrderRefundReq.RefundType.balance.name().equals(refundType)) { if (!Boolean.TRUE.equals(isOnly)) { // 普通订单选择“退还至余额”时,退款数额放在 refundMoney; // 多件整单退款的余额部分由服务端固定写入 refundBalance。 BigDecimal balanceAmount = multiQuantityOrder ? Optional.ofNullable(refundBalance).orElse(BigDecimal.ZERO) : Optional.ofNullable(refundMoney).orElse(BigDecimal.ZERO); if (balanceAmount.compareTo(BigDecimal.ZERO) > 0) { userBenefitsService.addUserBalance(orderDon.getUserId(), balanceAmount, UserBalanceSourceRecord.Source.refund, orderDon.getYhsId(), orderDon.getId(), goodsDon.getTitle() + UserBalanceSourceRecord.Source.refund.getDesc(), true); } } } else { //余额退款不需要扣除积分 //分销提成积分扣除 deductDsPoints(orderDon); //店铺 分销提成金额扣除 yhShopDistributeService.deductDsMoney(orderDon); //镜像店铺 分销提成金额扣除 deductUserMirrorShopMoney(orderDon); } if (Boolean.TRUE.equals(orderDon.getIsFixedDistribute())) { //商务提成 因订单退款去掉 UserDistributeBusinessOrderDonRecord record = businessOrderDonRecordMapper.selectOne(Wrappers.lambdaQuery(UserDistributeBusinessOrderDonRecord.class) .eq(UserDistributeBusinessOrderDonRecord::getOrderId, orderDon.getId()) .eq(UserDistributeBusinessOrderDonRecord::getStatus, UserDistributeBusinessOrderDonRecord.Status.success) .last("limit 1")); if (record != null) { record.setStatus(UserDistributeBusinessOrderDonRecord.Status.refund); businessOrderDonRecordMapper.updateById(record); } } //店铺商务提成 if (orderDon.getYhsId() != null && orderDon.getYhsId() > 0) { YhShopUserDistributeBusinessOrderDonRecord shop_record = yhShopBusinessOrderDonRecordMapper.selectOne(Wrappers.lambdaQuery(YhShopUserDistributeBusinessOrderDonRecord.class) .eq(YhShopUserDistributeBusinessOrderDonRecord::getOrderId, orderDon.getId()) .eq(YhShopUserDistributeBusinessOrderDonRecord::getStatus, YhShopUserDistributeBusinessOrderDonRecord.Status.success) .last("limit 1")); if (shop_record != null) { shop_record.setStatus(YhShopUserDistributeBusinessOrderDonRecord.Status.refund); yhShopBusinessOrderDonRecordMapper.updateById(shop_record); } } //是否是优惠加购关联订单 if (Boolean.TRUE.equals(orderDon.getIsDp())) { OrderDiscountPlusPurchaseSkuRelation discountPlusPurchaseSkuRelation = orderDiscountPlusPurchaseSkuRelationMapper.selectOne(Wrappers.lambdaQuery(OrderDiscountPlusPurchaseSkuRelation.class) .eq(OrderDiscountPlusPurchaseSkuRelation::getOrderId, orderDon.getId()).last("limit 1")); if (discountPlusPurchaseSkuRelation != null) { //选择清除加购车票 if (CollUtil.isNotEmpty(dpRelationIds)) { try { List groupsRelations = relationMapper.selectList(Wrappers.lambdaQuery(GroupsRelation.class) .eq(GroupsRelation::getUserId, orderDon.getUserId()) .in(GroupsRelation::getId, dpRelationIds)); log.info("优惠加购订单orderId:{}退款,清除相关车票relation_ids:{}", orderDon.getId(), dpRelationIds); groupsRelations.forEach(relation -> relationClearService.clearTicket(relation, UserTicketClearedRecord.Source.dp)); } catch (Exception e) { } } String plusOrderIds = discountPlusPurchaseSkuRelation.getPlusOrderIds(); if (StrUtil.isNotBlank(plusOrderIds)) { try { log.info("关闭优惠加购orderId:{}关联实物订单", orderDon.getId()); List plusOrderIdList = Jsons.parseList(plusOrderIds, Long.class); List relate_orderDons = orderDonMapper.selectList(Wrappers.lambdaQuery(OrderDon.class) .in(OrderDon::getId, plusOrderIdList) .notIn(OrderDon::getStatus, Constant.noOrderStatus)); relate_orderDons.forEach(relate_o -> { //扣除用户自身实物权益 userRealOrderBenefitsService.deductUserRealGoodsBenefits(relate_o.getId()); }); orderDonMapper.update(null, Wrappers.lambdaUpdate(OrderDon.class) .set(OrderDon::getStatus, OrderDon.Status.refund) .in(OrderDon::getId, plusOrderIdList) .ne(OrderDon::getStatus, OrderDon.Status.refund)); } catch (Exception e) { } } } } //同步车票退款 重购记录 Runnable syncRebuyRecords = () -> { GroupsRelationExpiryRecord groupsRelationExpiryRecord = groupsRelationExpiryRecordService.getOne(Wrappers.lambdaQuery(GroupsRelationExpiryRecord.class) .in(GroupsRelationExpiryRecord::getReBuyOrderId, orderDon.getId()) .eq(GroupsRelationExpiryRecord::getRelationId, orderDon.getRelationId()).last("limit 1")); if (groupsRelationExpiryRecord == null) { log.info("订单orderId:{}退款,同步是否有重购记录不是直接续费车票", orderDon.getId()); List groupsRelationExpiryRecords = groupsRelationExpiryRecordService.list(Wrappers.lambdaQuery(GroupsRelationExpiryRecord.class) .in(GroupsRelationExpiryRecord::getReBuyOrderId, orderDon.getId())); log.info("订单orderId:{}退款,重购记录不是直接续费车票大小:{}", orderDon.getId(), groupsRelationExpiryRecords.size()); if (CollUtil.isNotEmpty(groupsRelationExpiryRecords)) { groupsRelationExpiryRecords.forEach(data -> data.setIsReBuy(false)); groupsRelationExpiryRecordService.saveOrUpdateBatch(groupsRelationExpiryRecords); } } else { groupsRelationExpiryRecord.setIsReBuy(false); groupsRelationExpiryRecordService.saveOrUpdate(groupsRelationExpiryRecord); } }; if (TransactionSynchronizationManager.isActualTransactionActive()) { TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { @Override public void afterCommit() { TASK_POOL.execute(syncRebuyRecords); } }); } else { TASK_POOL.execute(syncRebuyRecords); } //是否是礼品卡 现金支付订单 BigDecimal gcCash = orderDon.getGcCash(); if (gcCash != null && gcCash.compareTo(BigDecimal.ZERO) > 0) { giftCardOrderUseRecordService.returnGiftCardCash(orderDon.getId()); } //清除礼品卡 if (goodsDon.getSpecialType() != null && goodsDon.getSpecialType() == GoodsDon.SpecialType.giftCard) { giftCardUserRecordMapper.update(null, Wrappers.lambdaUpdate(GiftCardUserRecord.class) .set(GiftCardUserRecord::getIsPay, 0) .eq(GiftCardUserRecord::getOrderId, orderDon.getId())); } //是否是分销订单 //处理下级渠道分销提成 handleSubRewards(orderDon); //是否是用户套餐优惠权益订单 if (Objects.equals(orderDon.getGoodsId(), 15L)) { UserBenefitsComboRelationRecord userBenefitsComboRelationRecord = userBenefitsComboRelationRecordMapper.selectOne(Wrappers.lambdaQuery(UserBenefitsComboRelationRecord.class) .eq(UserBenefitsComboRelationRecord::getOrderId, orderDon.getId()).last("limit 1")); if (userBenefitsComboRelationRecord != null) { try { String relationIds = userBenefitsComboRelationRecord.getRelationIds(); List relationIdList = Jsons.parseList(relationIds, Long.class); //清空车票或减少车票时间 relationIdList.forEach(relationId -> { orderCommonService.clearUserTicketTime(relationId); }); } catch (Exception e) { } } } //HBO GO退款,车队上架 setUpIfEmpty(orderDon.getGoodsId(), orderDon.getRelationId()); if (Objects.equals(orderDon.getGoodsId(), Constant.NETFLIX_GID)) { List userIdList = userBindRelationService.getRelationUserIdList(orderDon.getUserId(), null); NetflixUserAccountSubmitRecoverRecord recoverRecord = netflixUserAccountSubmitRecoverRecordMapper.selectOne(Wrappers.lambdaQuery(NetflixUserAccountSubmitRecoverRecord.class) .eq(NetflixUserAccountSubmitRecoverRecord::getRelationId, orderDon.getRelationId()) .in(NetflixUserAccountSubmitRecoverRecord::getUserId, userIdList) .eq(NetflixUserAccountSubmitRecoverRecord::getStatus, 0) .last("limit 1")); if (recoverRecord != null) { recoverRecord.setStatus(3); netflixUserAccountSubmitRecoverRecordMapper.updateById(recoverRecord); } } //GPT独立会员 if (Objects.equals(orderDon.getGoodsId(), Constant.GPT_PLUS_GID) && sku != null && Objects.equals(sku.getNum(), 1)) { subRechargeNumOrder(orderDon.getUserId(), orderDon.getRelationId(), sku.getMonths(), operator); } //GPT独立会员退款 或代充退款 退还平台发券 if ((Objects.equals(orderDon.getGoodsId(), Constant.GPT_PLUS_GID) && sku != null && Objects.equals(sku.getNum(), 1)) || Objects.equals(orderDon.getGoodsId(), Constant.GPT_RECHARGE_GOODS_ID)) { //若不存在对应的车票 则清除优惠券 List skuIds = goodsDonSkuMapper.selectIdpGptAndRechargeSkuIds(Constant.GPT_PLUS_GID, Constant.GPT_RECHARGE_GOODS_ID); List userIdList = userBindRelationService.getRelationUserIdList(orderDon.getUserId(), null); Number number = beanSearcher.searchCount(GroupsRelationView.class, MapUtils.builder() .field(GroupsRelationView::getUserId, userIdList).op(Operator.InList) .field(GroupsRelationView::getSkuId, skuIds).op(Operator.InList) .field(GroupsRelationView::getStatus, GroupsRelation.Status.validity.name()) .build()); if (number.intValue() == 0) { List couponUsers = couponUserMapper.selectList(Wrappers.lambdaQuery(CouponUser.class) .eq(CouponUser::getUserId, orderDon.getUserId()) .eq(CouponUser::getChannel, CouponUser.Channel.ptfq) .eq(CouponUser::getStatus, CouponUser.Status.unused)); if (CollUtil.isNotEmpty(couponUsers)) { couponUserMapper.deleteBatchIds(couponUsers.stream().map(CouponUser::getId).collect(Collectors.toList())); } } } //claude code 退款 if (Objects.equals(orderDon.getGoodsId(), Constant.CLAUDE_CODE_GOODS_ID)) { DistributeWaitingSendPoints distributeWaitingSendPoints = distributeWaitingSendPointsMapper.selectOne(Wrappers.lambdaQuery(DistributeWaitingSendPoints.class).eq(DistributeWaitingSendPoints::getOrderId, orderDon.getId()).last("limit 1")); if (distributeWaitingSendPoints != null) { if (distributeWaitingSendPoints.getSendStatus() == DistributeWaitingSendPoints.Status.close) { //是否是普通cc推广 Long orderId = distributeWaitingSendPoints.getOrderId(); ClaudeCodeUserResetRecord claudeCodeUserResetRecord = claudeCodeUserResetRecordMapper.selectOne(Wrappers.lambdaQuery(ClaudeCodeUserResetRecord.class).eq(ClaudeCodeUserResetRecord::getOrderId, orderId).last("limit 1")); if (claudeCodeUserResetRecord != null) { claudeCodeService.adjUserRestNum(orderDon.getId(), distributeWaitingSendPoints.getSharedId(), -1, "新用户订单退款"); } } } } } //处理下级渠道分销提成 static boolean shouldRestoreStock(GoodsDon goodsDon, boolean multiQuantityOrder) { return !multiQuantityOrder || goodsDon == null || !Objects.equals(goodsDon.getType(), 1); } public void handleSubRewards(OrderDon orderDon) { //是否是分销订单 //处理下级渠道分销提成 if (orderDon != null && Boolean.TRUE.equals(orderDon.getIsDistribute())) { UserDistributeSub subRelate = userDistributeSubMapper.getSubRelate(orderDon.getUserId()); if (subRelate != null) { List subWaitings = subWaitingMapper.selectList(Wrappers.lambdaQuery(UserDistributeSubWaiting.class) .eq(UserDistributeSubWaiting::getSubUserId, subRelate.getSubUserId()) .eq(UserDistributeSubWaiting::getSendStatus, UserDistributeSubWaiting.Status.waiting)); if (CollUtil.isNotEmpty(subWaitings)) { List userDistributeSubRewards = userDistributeSubRewardsMapper.selectList(null); Map> rewardLevelMap = userDistributeSubRewards.stream().collect(Collectors.groupingBy(UserDistributeSubRewards::getLevel)); subWaitings.forEach(wait -> { List thisLevelList = rewardLevelMap.get(wait.getLevel()); if (thisLevelList == null) { return; } UserDistributeSubRewards thisLevel = thisLevelList.get(0); Integer months = thisLevel.getMonths(); BigDecimal subOrderMoney = Optional.ofNullable(orderDonMapper.selectSubChannelOrderMoney(wait.getSubUserId(), subRelate.getCreatedTime(), DateUtil.offsetMonth(subRelate.getCreatedTime(), months))).orElse(BigDecimal.ZERO); if (subOrderMoney.compareTo(thisLevel.getTargetAmount()) < 0) { subWaitingMapper.update(null, Wrappers.lambdaUpdate(UserDistributeSubWaiting.class) .set(UserDistributeSubWaiting::getSendStatus, UserDistributeSubWaiting.Status.close) .eq(UserDistributeSubWaiting::getSendStatus, UserDistributeSubWaiting.Status.waiting) .eq(UserDistributeSubWaiting::getId, wait.getId())); } }); } } } } /** * 退款 分销提成积分扣除 */ public void deductDsPoints(OrderDon orderDon) { if (orderDon != null && Boolean.TRUE.equals(orderDon.getIsDistribute())) { DistributeWaitingSendPoints distributeWaitingSendPoints = distributeWaitingSendPointsMapper.selectOne(Wrappers.lambdaQuery(DistributeWaitingSendPoints.class).eq(DistributeWaitingSendPoints::getOrderId, orderDon.getId()).eq(DistributeWaitingSendPoints::getIsVip,Boolean.FALSE).last("limit 1")); if (distributeWaitingSendPoints != null) { BigDecimal money = orderDon.getMoney(); BigDecimal refundMoney = orderDon.getRefundMoney(); //已发送 发送中 部分 全额退款 DistributeWaitingSendPoints.Status sendStatus = distributeWaitingSendPoints.getSendStatus(); BigDecimal points = distributeWaitingSendPoints.getPoints(); if (sendStatus == DistributeWaitingSendPoints.Status.waiting) { //实物设备退款就清0 if (refundMoney.compareTo(money) == 0 || Objects.equals(orderDon.getGoodsId(), 15L)) { //待发送状态 全额退款 distributeWaitingSendPoints.setSendStatus(DistributeWaitingSendPoints.Status.close); distributeWaitingSendPoints.setRefundPoints(points); distributeWaitingSendPoints.setPoints(BigDecimal.ZERO); distributeWaitingSendPointsMapper.updateById(distributeWaitingSendPoints); return; } Integer thisGoodsRate = distributeWaitingSendPoints.getRate(); if (thisGoodsRate == null || thisGoodsRate == 0) { thisGoodsRate = Optional.ofNullable(userDistributeMapper.getDistributeGoodsRateByUidAndGid(orderDon.getUserId(), orderDon.getGoodsId())).orElse(0); } if (thisGoodsRate > 0) { BigDecimal refundRate = refundMoney.divide(money, 2, RoundingMode.DOWN); //退掉比例的提成 BigDecimal needSubPoints = points.multiply(refundRate); distributeWaitingSendPoints.setRefundPoints(needSubPoints); //剩余积分 BigDecimal remainPoints = points.subtract(needSubPoints); distributeWaitingSendPoints.setPoints(remainPoints); //原先获取积分也扣除 BigDecimal oriPoints = distributeWaitingSendPoints.getOriPoints(); if (oriPoints != null && oriPoints.compareTo(BigDecimal.ZERO) > 0) { BigDecimal oriNeedSubPoints = oriPoints.multiply(refundRate); distributeWaitingSendPoints.setOriPoints(oriPoints.subtract(oriNeedSubPoints)); } distributeWaitingSendPointsMapper.updateById(distributeWaitingSendPoints); } } else if (sendStatus == DistributeWaitingSendPoints.Status.success) { //已发送的提成 部分退款 全额退款 需要扣除用户积分 //扣除固定推广者积分 //实物设备退款就清0 if (refundMoney.compareTo(money) == 0 || Objects.equals(orderDon.getGoodsId(), 15L)) { userBenefitsService.deductDistributePoints(orderDon.getId(), distributeWaitingSendPoints.getSharedId(), points); distributeWaitingSendPoints.setRefundPoints(points); distributeWaitingSendPoints.setPoints(BigDecimal.ZERO); distributeWaitingSendPoints.setSendStatus(DistributeWaitingSendPoints.Status.close); distributeWaitingSendPointsMapper.updateById(distributeWaitingSendPoints); return; } Integer thisGoodsRate = distributeWaitingSendPoints.getRate(); if (thisGoodsRate == null || thisGoodsRate == 0) { thisGoodsRate = Optional.ofNullable(userDistributeMapper.getDistributeGoodsRateByUidAndGid(orderDon.getUserId(), orderDon.getGoodsId())).orElse(0); } if (thisGoodsRate > 0) { BigDecimal refundRate = refundMoney.divide(money, 2, RoundingMode.DOWN); //退掉比例的提成 BigDecimal needSubPoints = points.multiply(refundRate); userBenefitsService.deductDistributePoints(orderDon.getId(), distributeWaitingSendPoints.getSharedId(), needSubPoints); //部分退款 distributeWaitingSendPoints.setRefundPoints(needSubPoints); distributeWaitingSendPoints.setPoints(points.subtract(needSubPoints)); //原先获取积分也扣除 BigDecimal oriPoints = distributeWaitingSendPoints.getOriPoints(); if (oriPoints != null && oriPoints.compareTo(BigDecimal.ZERO) > 0) { BigDecimal oriNeedSubPoints = oriPoints.multiply(refundRate); distributeWaitingSendPoints.setOriPoints(oriPoints.subtract(oriNeedSubPoints)); } distributeWaitingSendPointsMapper.updateById(distributeWaitingSendPoints); } } } } } @Override public void refundPriceDiffer(OrderRefundReq orderRefundReq) throws Exception { Long orderId = orderRefundReq.getOrderId(); OrderDon orderDon = orderDonMapper.selectById(orderId); if (orderDon == null) { throw BusinessRuntimeException.getInstance("订单不存在"); } if (orderDonTicketRecordService.exists(orderId)) { throw BusinessRuntimeException.getInstance("Multi-quantity orders only support whole-order refunds"); } if (orderDon.getMoney().compareTo(BigDecimal.ZERO) == 0) { throw BusinessRuntimeException.getInstance("该订单是0元订单,无法补差价"); } if (Constant.noOrderAllStatus.contains(orderDon.getStatus().name())) { throw BusinessRuntimeException.getInstance("订单不是已支付状态"); } //默认补差价余额 String refundType = Optional.ofNullable(orderRefundReq.getRefundType()).orElse("balance"); BigDecimal refundMoney = orderRefundReq.getRefundMoney(); BigDecimal orderRefundMoney = Optional.ofNullable(orderDon.getRefundMoney()).orElse(BigDecimal.ZERO); BigDecimal orderDonRefundBalance = Optional.ofNullable(orderDon.getRefundBalance()).orElse(BigDecimal.ZERO); if (refundMoney.add(orderRefundMoney).add(orderDonRefundBalance).compareTo(orderDon.getRealMoney()) >= 0) { throw BusinessRuntimeException.getInstance("补差价已达到该订单付款金额"); } GoodsDon goodsDon = goodsDonMapper.selectById(orderDon.getGoodsId()); GoodsDonSku sku = null; if (orderDon.getSkuId() != null) { sku = skuMapper.selectById(orderDon.getSkuId()); } orderDon.setRefundDesc("补差价"); String outNo = StrUtil.EMPTY; if ("money".equals(refundType)) { if (refundMoney == null || refundMoney.compareTo(BigDecimal.ZERO) <= 0) { throw BusinessRuntimeException.getInstance("请输入正确的差价金额"); } if (refundMoney.compareTo(orderDon.getMoney()) >= 0) { throw BusinessRuntimeException.getInstance("补差价金额应不大于订单金额"); } //退款 orderRefundReq.setIsDiffer(true); outNo = orderRefundService.centerRefund(orderRefundReq, orderDon); orderDon.setRefundMoney(orderRefundMoney.add(refundMoney)); orderDonMapper.updateById(orderDon); //补差价金额 扣除对应分销提成金额 orderDon.setRefundMoney(refundMoney); deductDsPoints(orderDon); //店铺分销提成金额扣除 yhShopDistributeService.deductDsMoney(orderDon); } else { BigDecimal refundBalance = refundMoney; if (refundBalance == null || refundBalance.compareTo(BigDecimal.ZERO) <= 0 || refundBalance.compareTo(orderDon.getMoney()) > 0) { throw BusinessRuntimeException.getInstance("请输入正确的差价金额"); } userBenefitsService.addUserBalance(orderDon.getUserId(), refundBalance, UserBalanceSourceRecord.Source.price_differ, orderDon.getYhsId(), orderDon.getId(), goodsDon.getTitle() + UserBalanceSourceRecord.Source.price_differ.getDesc(), true); orderDon.setRefundBalance(orderDonRefundBalance.add(refundBalance)); orderDonMapper.updateById(orderDon); } orderRefundService.refundRecord(orderDon, refundMoney, orderRefundReq.getRefundBalance(), orderRefundReq.getOperator(), goodsDon, sku, outNo, refundType, null); } @Override @Transactional(rollbackFor = Throwable.class) public void clearBalanceToRefundMoney(OrderBalanceRefundReq refundReq) throws Exception { Long refundOrderId = refundReq.getRefundOrderId(); BigDecimal refundMoney = refundReq.getRefundMoney(); RefundOrderDon refundOrderDon = refundOrderDonMapper.selectById(refundOrderId); if (!"balance".equals(refundOrderDon.getRefundMethod())) { throw BusinessRuntimeException.getInstance("该记录不是余额退款类型"); } Long orderId = refundOrderDon.getOrderId(); OrderDon orderDon = orderDonMapper.selectById(orderId); if (orderDon == null) { throw BusinessRuntimeException.getInstance("Order does not exist"); } if (orderDonTicketRecordService.exists(orderId)) { throw BusinessRuntimeException.getInstance("Multi-quantity orders only support whole-order refunds"); } if (orderDon.getMoney().compareTo(BigDecimal.ZERO) == 0) { throw BusinessRuntimeException.getInstance("该记录对应的订单无实际支付金额"); } if (refundOrderDon.getIsClearBalance()) { throw BusinessRuntimeException.getInstance("该笔退款余额记录已退款"); } BigDecimal refundFee = refundOrderDon.getRefundFee(); //默认退全部余额 if (refundMoney != null) { refundFee = refundMoney; } BigDecimal money = orderDon.getMoney(); //若退款余额 大于退款金额 将退款余额至为 退款金额 if (refundFee.compareTo(money) > 0) { refundFee = money; } User user = userMapper.selectById(orderDon.getUserId()); if (user.getBalance().compareTo(refundFee) < 0) { throw BusinessRuntimeException.getInstance("用户余额不足,无法扣除对应退款余额"); } int update = userMapper.update(null, Wrappers.lambdaUpdate(User.class) .set(User::getBalance, user.getBalance().subtract(refundFee)) .eq(User::getId, user.getId()) .eq(User::getBalance, user.getBalance())); if (update == 0) { throw BusinessRuntimeException.getInstance("扣除用户退款余额失败,请重试..."); } OrderRefundReq orderRefundReq = new OrderRefundReq(); orderRefundReq.setOrderId(orderId); orderRefundReq.setRefundMoney(refundFee); orderRefundReq.setRefundType("money"); String outNo = orderRefundService.centerRefund(orderRefundReq, orderDon); orderDon.setRefundBalance(orderDon.getRefundBalance().subtract(refundFee)); BigDecimal orderRefundMoney = Optional.ofNullable(orderDon.getRefundMoney()).orElse(BigDecimal.ZERO); orderDon.setRefundMoney(orderRefundMoney.add(refundFee)); orderDonMapper.updateById(orderDon); refundOrderDon.setOutRefundNo(outNo); refundOrderDon.setIsClearBalance(true); refundOrderDonMapper.updateById(refundOrderDon); } /** * 寻找该车队空余车位 */ public GroupsRelation getRelationThisTrips(Long userId, Long groupsId, Integer retryNum) { //寻找快满编车队进行占座 GroupsRelation relation = relationMapper.selectRandomOneRelationByGroupsId(groupsId); if (relation == null) { throw BusinessRuntimeException.getInstance("该车队人员已满,请选择另一车队"); } Long relationId = relation.getId(); if (!redisService.setNx(RedisKey.GROUPS_RELATION_NUM_KEY + relationId, userId, 60 * 5L)) { if (retryNum == 7) throw BusinessRuntimeException.getInstance("该车队人员已满,请选择另一车队"); retryNum++; return getRelationThisTrips(userId, groupsId, retryNum); } int count = groupsMapper.decrAvailableNum(relation.getGroupsId()); if (count == 0) { log.error("车位异常 groupId:{}", groupsId); groupsMapper.updateAvailableNum(groupsId); throw new BusinessRuntimeException("车位异常"); } return relation; } private GroupsRelation getChatGPTOutsideRelation(Long userId, Long groupsId, Integer relationNum, Integer retryNum) { //寻找快满编车队额外座位 GroupsRelation relation = relationMapper.selectOutsideRelationByGroupsId(groupsId, relationNum); if (relation == null) { relation = new GroupsRelation(); relation.setStatus(GroupsRelation.Status.outside); relation.setGroupsId(groupsId); GroupsRelation maxNumRelation = relationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class).eq(GroupsRelation::getGroupsId, groupsId).orderByDesc(GroupsRelation::getNum).last("limit 1")); relation.setNum(maxNumRelation.getNum() + 1); relationMapper.insert(relation); return relation; } Long relationId = relation.getId(); if (!redisService.setNx(RedisKey.GROUPS_RELATION_NUM_KEY + relationId, userId, 60 * 5L)) { if (retryNum == 7) throw BusinessRuntimeException.getInstance("该车队人员已满,请选择另一车队"); retryNum++; return getChatGPTOutsideRelation(userId, groupsId, relationNum, retryNum); } return relation; } private void checkMirrorRenewSku(OrderDon orderDon) { Long skuId = orderDon.getSkuId(); Long orderId = orderDon.getId(); OrderDonRenewRecord orderDonRenewRecord = orderDonRenewRecordMapper.selectOne(Wrappers.lambdaQuery(OrderDonRenewRecord.class) .eq(OrderDonRenewRecord::getOrderId, orderId) .last("limit 1")); if (orderDonRenewRecord != null) { //gpt if (orderDon.getGoodsId() == 18) { ChatgptUser chatgptUser = chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class) .eq(ChatgptUser::getRelationId, orderDon.getRelationId()) .last("limit 1")); Long presentRenewSkuId = chatgptUser.getRenewSkuId(); if (presentRenewSkuId != null) { //存在不同升级套餐 GoodsDonSku presentRenewSku = skuMapper.selectById(presentRenewSkuId); if (!ObjectUtil.equal(presentRenewSkuId, skuId)) { throw BusinessRuntimeException.getInstance("请先退掉用户目前续费升级的{0}规格", presentRenewSku.getSpecVal()); } } } //claude pro if (orderDon.getGoodsId() == 75) { ClaudeUser claudeUser = claudeUserMapper.selectOne(Wrappers.lambdaQuery(ClaudeUser.class) .eq(ClaudeUser::getRelationId, orderDon.getRelationId()) .last("limit 1")); Long presentRenewSkuId = claudeUser.getRenewSkuId(); if (presentRenewSkuId != null) { //存在不同升级套餐 GoodsDonSku presentRenewSku = skuMapper.selectById(presentRenewSkuId); if (!ObjectUtil.equal(presentRenewSkuId, skuId)) { throw BusinessRuntimeException.getInstance("请先退掉用户目前续费升级的{0}规格", presentRenewSku.getSpecVal()); } } } } } private void clearExtraRelation(OrderDon orderDon) { List userPayEquipmentReceiveBenefitsRecords = receiveBenefitsRecordMapper.selectList(Wrappers.lambdaQuery(UserPayEquipmentReceiveBenefitsRecord.class) .eq(UserPayEquipmentReceiveBenefitsRecord::getRealOrderId, orderDon.getId())); userPayEquipmentReceiveBenefitsRecords.forEach(record -> { Long skuId = record.getSkuId(); List relations = relationMapper.selectExtraRelation(orderDon.getUserId(), skuId, record.getRelationId()); relations.forEach(relation -> { //不是优惠加购 Integer count = orderDiscountPlusPurchaseSkuRelationMapper.selectCount(Wrappers.lambdaQuery(OrderDiscountPlusPurchaseSkuRelation.class) .eq(OrderDiscountPlusPurchaseSkuRelation::getUserId, relation.getUserId()) .apply("JSON_CONTAINS(relation_ids,'" + relation.getId() + "')")); if (count == 0) { log.info("非优惠加购订单,直接清除userId:{}车票:{}", relation.getUserId(), relation.getId()); relationClearService.clearTicket(relation, UserTicketClearedRecord.Source.manual); } }); }); } /** * 扣除用户镜像店铺提成 */ private void deductUserMirrorShopMoney(OrderDon orderDon) { UserMirrorShopDistributeWaitingSendPoints distributeWaitingSendPoints = userMirrorShopDistributeWaitingSendPointsMapper.selectOne(Wrappers.lambdaQuery(UserMirrorShopDistributeWaitingSendPoints.class) .eq(UserMirrorShopDistributeWaitingSendPoints::getOrderId, orderDon.getId()) .last("limit 1")); if (distributeWaitingSendPoints != null) { BigDecimal money = orderDon.getMoney(); BigDecimal refundMoney = orderDon.getRefundMoney(); //已发送 发送中 部分 全额退款 UserMirrorShopDistributeWaitingSendPoints.Status sendStatus = distributeWaitingSendPoints.getSendStatus(); BigDecimal take_money = distributeWaitingSendPoints.getMoney(); if (sendStatus == UserMirrorShopDistributeWaitingSendPoints.Status.waiting) { if (refundMoney.compareTo(money) == 0) { //待发送状态 全额退款 distributeWaitingSendPoints.setSendStatus(UserMirrorShopDistributeWaitingSendPoints.Status.close); distributeWaitingSendPoints.setRefundMoney(take_money); distributeWaitingSendPoints.setMoney(BigDecimal.ZERO); userMirrorShopDistributeWaitingSendPointsMapper.updateById(distributeWaitingSendPoints); return; } Integer rate = distributeWaitingSendPoints.getRate(); if (rate > 0) { BigDecimal needDeductMoney = refundMoney.multiply(BigDecimal.valueOf(rate).divide(BigDecimal.valueOf(100))); distributeWaitingSendPoints.setRefundMoney(needDeductMoney); //剩余积分 distributeWaitingSendPoints.setMoney(take_money.subtract(needDeductMoney)); userMirrorShopDistributeWaitingSendPointsMapper.updateById(distributeWaitingSendPoints); } } else if (sendStatus == UserMirrorShopDistributeWaitingSendPoints.Status.success) { //已发送的提成 部分退款 全额退款 需要扣除用户积分 //扣除固定推广者积分 if (refundMoney.compareTo(money) == 0) { //扣除店铺主 已有的提成金额 userBenefitsService.deductUserMirrorShopTakeMoney(orderDon.getId(), distributeWaitingSendPoints.getShopUserId(), take_money); distributeWaitingSendPoints.setRefundMoney(take_money); distributeWaitingSendPoints.setMoney(BigDecimal.ZERO); distributeWaitingSendPoints.setSendStatus(UserMirrorShopDistributeWaitingSendPoints.Status.close); userMirrorShopDistributeWaitingSendPointsMapper.updateById(distributeWaitingSendPoints); return; } Integer rate = distributeWaitingSendPoints.getRate(); if (rate > 0) { BigDecimal needDeductMoney = refundMoney.multiply(BigDecimal.valueOf(rate).divide(BigDecimal.valueOf(100))); //扣除店铺主 已有的提成金额 userBenefitsService.deductUserMirrorShopTakeMoney(orderDon.getId(), distributeWaitingSendPoints.getShopUserId(), needDeductMoney); //部分退款 distributeWaitingSendPoints.setRefundMoney(needDeductMoney); distributeWaitingSendPoints.setMoney(take_money.subtract(needDeductMoney)); userMirrorShopDistributeWaitingSendPointsMapper.updateById(distributeWaitingSendPoints); } } } } /** * 清除多余车票 */ private void clearOtherChangeTicket(Long userId, Long relationId, GroupsRelation newRelation, Long skuId) { UserTicketClearedRecord userTicketClearedRecord = userTicketClearedRecordMapper.selectOne(Wrappers.lambdaQuery(UserTicketClearedRecord.class) .eq(UserTicketClearedRecord::getUserId, userId) .eq(UserTicketClearedRecord::getRelationId, relationId) .eq(UserTicketClearedRecord::getSource, UserTicketClearedRecord.Source.change_ticket) .orderByDesc(UserTicketClearedRecord::getId) .last("limit 1")); if (userTicketClearedRecord != null) { Long newRelationId = userTicketClearedRecord.getNewRelationId(); List others = relationMapper.selectList(Wrappers.lambdaQuery(GroupsRelation.class) .ne(GroupsRelation::getId, newRelationId) .eq(GroupsRelation::getUserId, userId) .eq(GroupsRelation::getStartTime, newRelation.getStartTime()) .eq(GroupsRelation::getExpiryTime, newRelation.getExpiryTime())); others.forEach(r -> { Long groupsId = r.getGroupsId(); GroupsTrips groupsTrips = groupsMapper.selectById(groupsId); //排除 非同规格车票 if (!skuId.equals(groupsTrips.getSkuId())) { return; } Long rid = r.getId(); Integer selectCount = orderDonMapper.selectCount(Wrappers.lambdaQuery(OrderDon.class) .eq(OrderDon::getUserId, userId) .eq(OrderDon::getRelationId, rid) .notIn(OrderDon::getStatus, Constant.noOrderAllStatus)); if (selectCount == 0) { //不是优惠加购 Integer count = orderDiscountPlusPurchaseSkuRelationMapper.selectCount(Wrappers.lambdaQuery(OrderDiscountPlusPurchaseSkuRelation.class) .eq(OrderDiscountPlusPurchaseSkuRelation::getUserId, userId) .apply("JSON_CONTAINS(relation_ids,'" + rid + "')")); if (count == 0) { log.info("清除用户:{}多余车票", userId); relationClearService.clearTicket(r, UserTicketClearedRecord.Source.manual); } } }); } } /** * 车队退款 自动上架 */ private void setUpIfEmpty(Long goodsId, Long relationId) { if (goodsId == Constant.HBO_GO_GOODS_ID) { GroupsRelation relation = relationMapper.selectById(relationId); if (relation == null) { return; } GroupsTrips groupsTrips = groupsMapper.selectById(relation.getGroupsId()); if (groupsTrips == null) { return; } if (groupsTrips.getStatus() == GroupsTrips.Status.down) { log.info("HBO GO车队:{}存在退款,自动上架", groupsTrips.getId()); groupsMapper.update(null, Wrappers.lambdaUpdate(GroupsTrips.class) .set(GroupsTrips::getStatus, GroupsTrips.Status.validity) .eq(GroupsTrips::getId, groupsTrips.getId())); } } } /** * 退款扣除代充次数 */ private void subRechargeNumOrder(Long userId, Long relationId, Integer subNum, String operator) { List userIdList = userBindRelationService.getRelationUserIdList(userId, null); GroupsRelation relation = relationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class).eq(GroupsRelation::getId, relationId).in(GroupsRelation::getUserId, userIdList).gt(GroupsRelation::getExpiryTime, DateTime.now())); if (relation != null && relation.getRechargeStatus() != null) { if (relation.getRechargeRemainNum() == 0) { return; } int update = 0; while (update == 0) { Integer rechargeRemainNum = relation.getRechargeRemainNum(); if (subNum > rechargeRemainNum) { subNum = rechargeRemainNum; } update = groupsRelationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class) .set(GroupsRelation::getRechargeRemainNum, rechargeRemainNum - subNum) .eq(GroupsRelation::getId, relationId) .eq(GroupsRelation::getRechargeRemainNum, rechargeRemainNum)); if (update == 1) { //记录gpt代充记录 gptUserRechargeRecordService.recordGptUserRechargeNum(null, relation.getUserId(), relation.getId(), relation.getAccount(), -subNum, operator, "订单退款"); break; } relation = relationMapper.selectById(relationId); } } } }