package com.cf.imes.module.system.service.pay;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.cf.imes.framework.common.exception.ErrorData;
import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.common.util.json.JsonUtils;
import com.cf.imes.framework.common.util.validation.ValidationUtils;
import com.cf.imes.framework.id.core.util.SnowflakeSerialNoWorker3rd;
import com.cf.imes.framework.mybatis.core.dataobject.BaseDO;
import com.cf.imes.framework.pay.config.ChenfengAlipayConfig;
import com.cf.imes.framework.pay.config.ChenfengPayConfig;
import com.cf.imes.framework.pay.core.client.dto.order.PayOrderRespDTO;
import com.cf.imes.framework.redis.config.ChenfengCacheProperties;
import com.cf.imes.framework.redis.constants.RedisKeyConstants;
import com.cf.imes.framework.redis.util.RedisLockUtil;
import com.cf.imes.module.system.controller.admin.funds.delay.vo.PreviouProductDelayRespVO;
import com.cf.imes.module.system.controller.admin.pay.vo.PayOrderUnifiedReqDTO;
import com.cf.imes.framework.pay.core.enums.order.PayOrderStatusRespEnum;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductProcessorRespVO;
import com.cf.imes.module.system.dal.dataobject.funds.delay.ProductDelayRecordDO;
import com.cf.imes.module.system.dal.dataobject.funds.productpromotion.ProductPromotionDO;
import com.cf.imes.module.system.enums.pay.ProductDurationUnitEnum;
import com.cf.imes.module.system.service.funds.delay.ProductDelayService;
import com.cf.imes.module.system.service.funds.productpromotion.ProductPromotionService;
import com.cf.imes.module.system.service.funds.purchase.PurchaseService;
import com.cf.imes.module.system.controller.admin.pay.vo.PayProductOrderUnifiedReqVO;
import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDO;
import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDetailDO;
import com.cf.imes.module.system.dal.dataobject.funds.purchase.PurchaseRecordDO;
import com.cf.imes.module.system.dal.dataobject.incomeandexpense.IncomeExpenseDetailsDO;
import com.cf.imes.module.system.enums.pay.IncomeExpenseTypeEnum;
import com.cf.imes.module.system.enums.pay.PayChannelCodeEnum;
import com.cf.imes.module.system.enums.pay.TradeTypeEnum;
import com.cf.imes.module.system.framework.snowflake.config.MybatisIdProperties;
import com.cf.imes.module.system.service.funds.incomeandexpense.IncomeExpenseService;
import com.cf.imes.module.system.service.funds.products.ProductsDetailService;
import com.cf.imes.module.system.service.pay.factory.impl.AbstractPayHandler;
import com.cf.imes.module.system.service.pay.factory.PayHandlerFactory;
import com.cf.imes.module.system.dal.dataobject.pay.PayOrderDO;
import com.cf.imes.module.system.dal.mysql.pay.PayOrderMapper;
import com.cf.imes.module.system.enums.pay.PayOrderStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.validation.annotation.Validated;

import jakarta.annotation.Resource;
import jakarta.validation.Validator;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.cf.imes.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.ORG_PRODUCT_PURCHASE_LOCK_ERROR;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.ORG_PRODUCT_PURCHASE_WAIT_PAY_ERROR;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_NOTIFY_PASSBACK_TRADETYPE_NOT_SUPPORT_ERROR;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_ORDER_CHANNEL_QUERY_NOT_EXIST;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_ORDER_NOTIFY_DATA_PARSE_ERROR;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_ORDER_NOT_EXIST;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_ORDER_SUBMIT_CHANNEL_ERROR;

/**
 * 支付订单接口 实现类
 *
 * @author Gqr
 * @since 2025/4/17 17:29
 */
@Service
@Validated
@Slf4j
public class PayOrderServiceImpl implements PayOrderService {

    // 自动生成定价id正则
    private static final Pattern productMonthDetailIdPattern = Pattern.compile("^1759029544(\\d+)$");
    private static final Pattern productYearDetailIdPattern = Pattern.compile("^1759993429(\\d+)$");

    @Resource
    private PayHandlerFactory payHandlerFactory;

    @Resource
    private PayOrderMapper payOrderMapper;

    @Resource
    private Validator validator;

    @Resource
    private ChenfengAlipayConfig alipayConfig;

    @Resource
    private ChenfengPayConfig payConfig;

    @Resource
    private ProductsDetailService productsDetailService;

    @Resource
    private IncomeExpenseService incomeExpenseService;

    @Resource
    private PurchaseService purchaseService;

    @Resource
    private MybatisIdProperties mybatisIdProperties;

    @Resource
    private ProductDelayService productDelayService;

    @Resource
    private RedisLockUtil redisLockUtil;

    @Resource
    private ChenfengCacheProperties chenfengCacheProperties;

    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private SnowflakeSerialNoWorker3rd snowflakeSerialNoWorker3rd;

    @Resource
    private ProductPromotionService productPromotionService;

    @Override
    public PayOrderDO get(Long id) {
        return payOrderMapper.selectById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PayOrderRespDTO productPay(PayProductOrderUnifiedReqVO payProductOrderUnifiedReqVO) {
        Long organId = payProductOrderUnifiedReqVO.getOrganId();
        organId = ObjectUtil.isNull(organId) ? SecurityFrameworkUtils.getUserOrganId() : organId;

        // 主动校验一次入参
        ValidationUtils.validate(validator, payProductOrderUnifiedReqVO);

        // 校验产品和产品明细
        Long productId = payProductOrderUnifiedReqVO.getProductId();
        ProductsDO productsDO = productsDetailService.validateProductExist(productId);

        // 解析产品定价明细
        ProductsDetailDO productsDetailDO = analyzeProductDetail(productId, payProductOrderUnifiedReqVO.getProductDetailsId());

        // 检查机构产品购买锁
        String purchaseLockKey = String.format(RedisKeyConstants.PRODUCT_PURCHASE_LOCK_KEY, organId, productId);
        boolean lockResult = redisLockUtil.acquireLock(purchaseLockKey, String.valueOf(organId), chenfengCacheProperties.getLockTimeout());
        if (!lockResult) {
            throw new ServiceException(ORG_PRODUCT_PURCHASE_LOCK_ERROR);
        }

        PayOrderUnifiedReqDTO payOrderUnifiedReqDTO = null;
        // 获取渠道方处理器
        Integer channelCode = payProductOrderUnifiedReqVO.getProductChannelCode();
        try {
            // 检查机构产品是否有待支付的购买记录
            List<PurchaseRecordDO> pendingPurchaseList = purchaseService.getOrgProductPendingPurchaseList(organId, productId);
            if (CollUtil.isNotEmpty(pendingPurchaseList)) {
                // 取更新时间最新的那一条展示支付链接
                PurchaseRecordDO latestPendingPurchase = pendingPurchaseList.stream()
                        .max(Comparator.comparing(BaseDO::getUpdateTime))
                        .orElse(null);
                Object[] errorData = new Object[3];
                errorData[0] = latestPendingPurchase.getPayOrderId();
                errorData[1] = latestPendingPurchase.getPaymentMethod();
                errorData[2] = latestPendingPurchase.getDisplayContent();
                throw new ServiceException(ORG_PRODUCT_PURCHASE_WAIT_PAY_ERROR, new ErrorData().setErrorData(errorData), payConfig.getPayExpireTime().toMinutes());
            }

            // 查询组织产品的首次购买记录
            PurchaseRecordDO orgInitialPurchaseRecord = purchaseService.getOrgInitialPurchaseRecord(organId, productsDO.getId());

            // 查询组织有效产品购买记录下的延期记录
            PreviouProductDelayRespVO previouProductDelayRespVO = productDelayService.getActiveRecordsByProductId(productId, organId);

            // 查询可用的优惠活动
            List<ProductPromotionDO> availableProductPromotionList = productPromotionService.getAvailableProductPromotionList(productId);

            // 根据购买类型做价格处理
            PayProductProcessorNew payProductProcessorNew = new PayProductProcessorNew(productsDO, productsDetailDO, orgInitialPurchaseRecord, previouProductDelayRespVO, availableProductPromotionList, organId, payConfig, mybatisIdProperties);
            ProductProcessorRespVO productProcessorRespVO = payProductProcessorNew.processPayment(payProductOrderUnifiedReqVO.getProductChannelCode());

            PayOrderDO payOrder = productProcessorRespVO.getPayOrderDO();

            // 处理前置的购买记录
            processPreviosPurchaseRecord(previouProductDelayRespVO, payOrder);

            // 记录入库，如果有外部支付发起请求二维码
            PurchaseRecordDO purchaseRecordDO = productProcessorRespVO.getPurchaseRecordDO();


            // 发起渠道方支付
            createPayOrder(payOrder);
            AbstractPayHandler payHandler = payHandlerFactory.getPayHandler(channelCode);

            payOrderUnifiedReqDTO = createPayOrderUnifiedReqDTO(payOrder);
            PayOrderRespDTO payOrderRespDTO = payHandler.unifiedOrder(payOrderUnifiedReqDTO);

            // 购买记录入库，记录外部支付请求
            purchaseRecordDO.setDisplayContent(payOrderRespDTO.getDisplayContent());
            purchaseRecordDO.setPayOrderId(payOrder.getId());
            purchaseService.createRecord(purchaseRecordDO);
            return payOrderRespDTO;

        } catch (Throwable e) {
            // 系统异常，则包装成 PayException 异常抛出
            log.error("[unifiedOrder][客户端({}) request({}) 发起支付异常]",
                    channelCode, JSON.toJSONString(payOrderUnifiedReqDTO), e);
            if(e instanceof ServiceException) {
                throw (ServiceException) e;
            } else {
                throw new ServiceException(PAY_ORDER_SUBMIT_CHANNEL_ERROR);
            }
        } finally {
            // 软件购买锁解锁
            redisLockUtil.releaseLock(purchaseLockKey, String.valueOf(organId));
        }
    }

    @Override
    public String getProductPayExpireTime(PayProductOrderUnifiedReqVO payProductOrderUnifiedReqVO) {
        Long organId = payProductOrderUnifiedReqVO.getOrganId();
        organId = ObjectUtil.isNull(organId) ? SecurityFrameworkUtils.getUserOrganId() : organId;

        // 主动校验一次入参
        ValidationUtils.validate(validator, payProductOrderUnifiedReqVO);

        // 校验产品和产品明细
        Long productId = payProductOrderUnifiedReqVO.getProductId();
        ProductsDO productsDO = productsDetailService.validateProductExist(productId);

        // 解析产品定价明细
        ProductsDetailDO productsDetailDO = analyzeProductDetail(productId, payProductOrderUnifiedReqVO.getProductDetailsId());

        // 检查机构产品是否有待支付的购买记录
        List<PurchaseRecordDO> pendingPurchaseList = purchaseService.getOrgProductPendingPurchaseList(organId, productId);
        if (CollUtil.isNotEmpty(pendingPurchaseList)) {
            // 取更新时间最新的那一条展示支付链接
            PurchaseRecordDO latestPendingPurchase = pendingPurchaseList.stream()
                    .max(Comparator.comparing(BaseDO::getUpdateTime))
                    .orElse(null);
            Object[] errorData = new Object[3];
            errorData[0] = latestPendingPurchase.getPayOrderId();
            errorData[1] = latestPendingPurchase.getPaymentMethod();
            errorData[2] = latestPendingPurchase.getDisplayContent();
            throw new ServiceException(ORG_PRODUCT_PURCHASE_WAIT_PAY_ERROR, new ErrorData().setErrorData(errorData), payConfig.getPayExpireTime().toMinutes());
        }

        // 查询组织产品的首次购买记录
        PurchaseRecordDO orgInitialPurchaseRecord = purchaseService.getOrgInitialPurchaseRecord(organId, productsDO.getId());

        // 查询组织有效产品购买记录下的延期记录
        PreviouProductDelayRespVO previouProductDelayRespVO = productDelayService.getActiveRecordsByProductId(productId, organId);

        // 查询可用的优惠活动
        List<ProductPromotionDO> availableProductPromotionList = productPromotionService.getAvailableProductPromotionList(productId);

        // 根据购买类型做价格处理
        PayProductProcessorNew payProductProcessor = new PayProductProcessorNew(productsDO, productsDetailDO, orgInitialPurchaseRecord, previouProductDelayRespVO, availableProductPromotionList, organId, null, null);
        return LocalDateTimeUtil.format(payProductProcessor.getProductPayExpireTime(), FORMAT_YEAR_MONTH_DAY);
    }

    /**
     * 解析定价规则，年从数据库取，月就手动拼
     *
     * @param productDetailId
     * @return
     */
    private ProductsDetailDO analyzeProductDetail(Long productId, Long productDetailId) {
        // productDetailId符合规则month自动生成规则
        Matcher matcher = productMonthDetailIdPattern.matcher(String.valueOf(productDetailId));
        if (matcher.find()) {
            // 取规则后的数字，是订购月份
            String monthDuration = matcher.group(1);
            ProductsDetailDO monthDetail = new ProductsDetailDO();
            monthDetail.setId(productDetailId);
            monthDetail.setDuration(Integer.parseInt(monthDuration));
            monthDetail.setDurationUnit(ProductDurationUnitEnum.MONTH.getCode());
            // 获取产品下的1年的折扣
//            monthDetail.setDiscount(productsDetailService.getProductDetailOneYearDiscount(productId));
            return monthDetail;
        } else {
            // productDetailId符合规则year自动生成规则
            matcher = productYearDetailIdPattern.matcher(String.valueOf(productDetailId));
            if (matcher.find()) {
                // 负责规则的年默认是1年
                ProductsDetailDO yearDetail = new ProductsDetailDO();
                yearDetail.setId(productDetailId);
                yearDetail.setDuration(1);
                yearDetail.setDurationUnit(ProductDurationUnitEnum.YEAR.getCode());
                // 自动生产的没有折扣
                yearDetail.setDiscount(BigDecimal.valueOf(100));
                return yearDetail;
            } else {
                return productsDetailService.validateProductDetailsExists(productDetailId);
            }
        }
    }

    /**
     * 处理前序购买记录：
     * 1、需要第三方支付，当前购买记录状态为待支付，等第三方回调/超期轮训处理到的时候标记为成功还是失败
     * 2、不需要第三方支付，直接作废
     *
     * @param previouProductDelayRespVO
     * @param payOrder
     */
    private void processPreviosPurchaseRecord(PreviouProductDelayRespVO previouProductDelayRespVO, PayOrderDO payOrder) {
        // 前序购买记录
        PurchaseRecordDO previouPurchaseRecord = previouProductDelayRespVO.getPreviouPurchaseRecord();
        // 前序购买记录的延期记录
        List<ProductDelayRecordDO> productDelayRecordDOS = BeanUtil.copyToList(previouProductDelayRespVO.getDelayRespVOList(), ProductDelayRecordDO.class);
        if (ObjectUtil.isNotNull(previouPurchaseRecord)) {
            Long previousId = previouPurchaseRecord.getId();
            Long organId = previouPurchaseRecord.getOrganId();
            if (ObjectUtil.isNull(payOrder)) {
                // 作废历史的购买记录
                purchaseService.invalidHistoryOrganProductPurchaseRecord(organId, previousId);
                // 更新前置购买记录下的延期记录
                productDelayService.updatePurchaseDelayRecordsPaidAndRemark(productDelayRecordDOS, organId, true);
            } else {
                // 更新前置购买记录下的延期记录
                productDelayService.updatePurchaseDelayRecordsPaidAndRemark(productDelayRecordDOS, organId, null);
            }
        }
    }

    /**
     * 创建支付渠道方请求对象
     *
     * @param payOrderDO
     * @return
     */
    private PayOrderUnifiedReqDTO createPayOrderUnifiedReqDTO(PayOrderDO payOrderDO) {
        PayOrderUnifiedReqDTO payOrderUnifiedReqDTO = new PayOrderUnifiedReqDTO();

        String subject = payOrderDO.getSubject();
        payOrderUnifiedReqDTO.setSubject(subject);
        payOrderUnifiedReqDTO.setBody(subject);
        payOrderUnifiedReqDTO.setOutTradeNo(String.valueOf(payOrderDO.getId()));
        payOrderUnifiedReqDTO.setPrice(payOrderDO.getPrice());
        payOrderUnifiedReqDTO.setExpireTime(payOrderDO.getExpireTime());
        payOrderUnifiedReqDTO.setNotifyUrl(String.format(alipayConfig.getNotifyUrl(), payOrderDO.getChannelCode().getCode()));

        return payOrderUnifiedReqDTO;
    }

    @Override
    public void queryPayOrderStatus(String outTradeNo, int channelCode) {
        // 校验支付订单是否存在
        PayOrderDO payOrderDO = payOrderMapper.selectOne(new LambdaQueryWrapper<PayOrderDO>()
                .eq(PayOrderDO::getId, outTradeNo)
                .eq(PayOrderDO::getDeleted, false)
        );
        if (ObjectUtils.isNull(payOrderDO)) {
            throw new ServiceException(PAY_ORDER_NOT_EXIST, outTradeNo);
        }
        // 订单已被更新成功，直接支付成功了
        if (PayOrderStatusEnum.SUCCESS.equals(payOrderDO.getStatus())) {
            return;
        }

        // 支付订单未成功，查询渠道方
        // 获取渠道方处理器
        AbstractPayHandler payHandler = payHandlerFactory.getPayHandler(channelCode);
        try {
            PayOrderRespDTO payOrderRespDTO = payHandler.queryPayOrder(outTradeNo);

            boolean paySuccess = PayOrderStatusRespEnum.SUCCESS.getStatus().equals(payOrderRespDTO.getStatus());
            if (!paySuccess) {
                throw new ServiceException(PAY_ORDER_CHANNEL_QUERY_NOT_EXIST);
            }
        } catch (Throwable e) {
            log.error("渠道方查询支付订单异常", e);
            throw new ServiceException(PAY_ORDER_CHANNEL_QUERY_NOT_EXIST);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void doOrderNotify(Integer channelCode, Map<String, String> params, String body, Map<String, String> headers) {
        // 获取渠道方处理器
        AbstractPayHandler payHandler = payHandlerFactory.getPayHandler(channelCode);

        try {
            // 解析回调数据
            PayOrderRespDTO payOrderRespDTO = payHandler.doParseOrderNotify(params, body, headers);

            // 处理支付回调数据
            this.notifyOrder(payOrderRespDTO);
        } catch (Throwable e) {
            log.error("[parseOrderNotify][客户端({}) params({}) body({}) headers({}) 解析失败]", channelCode, params, body, headers, e);
            throw new ServiceException(PAY_ORDER_NOTIFY_DATA_PARSE_ERROR);
        }
    }

    /**
     * 处理回调数据成功/失败
     *
     * @param notify
     */
    public void notifyOrder(PayOrderRespDTO notify) {
        String outTradeNo = notify.getOutTradeNo();

        // 情况一：支付成功的回调
        if (PayOrderStatusEnum.isSuccess(notify.getStatus())) {
            notifyOrderSuccess(notify, outTradeNo);
            return;
        }
        // 情况二：支付失败的回调
        if (PayOrderStatusEnum.isClosed(notify.getStatus())) {
            notifyOrderClosed(notify, outTradeNo);
        }
    }

    /**
     * 支付成功回调处理
     *
     * @param notify
     */
    private void notifyOrderSuccess(PayOrderRespDTO notify, String outTradeNo) {
        // 1. 判断 PayOrderDO 是否处于待支付
        PayOrderDO order = payOrderMapper.selectOne(new LambdaQueryWrapper<PayOrderDO>()
                .eq(PayOrderDO::getId, outTradeNo));
        Long organId = order.getOrganId();
        if (ObjectUtil.isNull(order)) {
            throw new ServiceException(PAY_ORDER_NOT_EXIST, outTradeNo);
        }
        // 如果已经是成功，直接返回，不用重复更新
        if (PayOrderStatusEnum.isSuccess(order.getStatus().getStatus())) {
            log.debug("[notifyOrderSuccess][order({}) 已经是已支付，无需更新]", order.getId());
            return;
        }
        if (!PayOrderStatusEnum.WAITING.equals(order.getStatus())) { // 校验状态，必须是待支付
            throw new ServiceException(PAY_ORDER_STATUS_IS_NOT_WAITING);
        }

        // 2. 更新 PayOrderDO
        int updateCounts = payOrderMapper.update(new LambdaUpdateWrapper<PayOrderDO>()
                .eq(PayOrderDO::getId, order.getId())
                .eq(PayOrderDO::getStatus, PayOrderStatusEnum.WAITING)
                .eq(PayOrderDO::getOrganId, organId)
                .set(PayOrderDO::getStatus, PayOrderStatusEnum.SUCCESS.getStatus())
                .set(PayOrderDO::getSuccessTime, notify.getSuccessTime())
                .set(PayOrderDO::getNotifyTime, LocalDateTime.now())
                .set(PayOrderDO::getChannelNotifyData, JsonUtils.zipString(JSON.toJSONString(notify))));

        if (updateCounts == 0) { // 校验状态，必须是待支付
            throw new ServiceException(PAY_ORDER_STATUS_IS_NOT_WAITING);
        }
        log.debug("[notifyOrderSuccess][order({}) 更新为已支付]", order.getId());

        // 3. 根据交易类型做对应回调处理
        switch (TradeTypeEnum.fromCode(order.getTradeType())) {
            case PRODUCT:
                notifyProductPayOrderSuccess(order);
                break;
            default:
                throw new ServiceException(PAY_NOTIFY_PASSBACK_TRADETYPE_NOT_SUPPORT_ERROR, order.getTradeType());
        }
    }

    /**
     * 产品购买成功回调
     *
     * @param payOrderDO
     */
    private void notifyProductPayOrderSuccess(PayOrderDO payOrderDO) {
        if (ObjectUtil.isNotNull(payOrderDO) && ObjectUtil.isNotNull(payOrderDO.getPurchaseId())) {
            Long organId = payOrderDO.getOrganId();

            // 支付订单的金额
            BigDecimal price = payOrderDO.getPrice();

            // 1、更新购买记录的开票状态
            long purchaseId = payOrderDO.getPurchaseId();
            purchaseService.updateRecordInvoicable(purchaseId, organId);

            // 2、作废前置购买记录
            PurchaseRecordDO record = purchaseService.getRecord(purchaseId);
            Long previousId = record.getPreviousId();
            if (ObjectUtil.isNotNull(previousId)) {
                purchaseService.invalidHistoryOrganProductPurchaseRecord(organId, previousId);
                // 3、更新前置购买记录下的延期记录为已支付
                productDelayService.updatePurchaseDelayRecordsPaid(previousId, organId);
            }

            // 4、激活本次的购买记录
            purchaseService.activateHistoryOrganProductPurchaseRecord(organId, purchaseId);

            // 5.新增收支明细
            IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
                    .organId(organId)
                    .orderNo(String.valueOf(payOrderDO.getId()))
                    .purchaseId(purchaseId)
                    .businessNo(snowflakeSerialNoWorker3rd.nextSerialNo())
                    .tradeType(TradeTypeEnum.PRODUCT.getCode())
                    .incomeExpenseType(IncomeExpenseTypeEnum.EXPENSES.getCode())
                    .amount(price)
                    .build();
            incomeExpenseDetailsDO.setCreator(payOrderDO.getCreator());
            incomeExpenseDetailsDO.setUpdater(payOrderDO.getUpdater());
            incomeExpenseService.createDetail(incomeExpenseDetailsDO);
        }

    }

    /**
     * 支付失败回调处理
     *
     * @param notify
     */
    private void notifyOrderClosed(PayOrderRespDTO notify, String outTradeNo) {
        // 1. 查询 PayOrderDO
        PayOrderDO order = payOrderMapper.selectOne(new LambdaQueryWrapper<PayOrderDO>()
                .eq(PayOrderDO::getId, outTradeNo));
        Long organId = order.getOrganId();
        if (ObjectUtil.isNull(order)) {
            throw new ServiceException(PAY_ORDER_NOT_EXIST, outTradeNo);
        }

        Integer orderStatus = order.getStatus().getStatus();
        if (PayOrderStatusEnum.isClosed(orderStatus)) { // 如果已经是关闭，直接返回，不用重复更新
            log.debug("[notifyOrderClosed][order({}) 已经是支付关闭，无需更新]", order.getId());
            return;
        }
        // 一般出现先是支付成功，然后支付关闭，都是全部退款导致关闭的场景。这个情况，我们不更新支付拓展单，只通过退款流程，更新支付单
        if (PayOrderStatusEnum.isSuccess(orderStatus)) {
            log.debug("[notifyOrderClosed][order({}) 是已支付，无需更新为支付关闭]", order.getId());
            return;
        }
        if (ObjectUtil.notEqual(orderStatus, PayOrderStatusEnum.WAITING.getStatus())) { // 校验状态，必须是待支付
            throw new ServiceException(PAY_ORDER_STATUS_IS_NOT_WAITING);
        }

        // 2. 更新 PayOrderDO
        int updateCounts = payOrderMapper.update(new LambdaUpdateWrapper<PayOrderDO>()
                .eq(PayOrderDO::getId, order.getId())
                .eq(PayOrderDO::getStatus, PayOrderStatusEnum.WAITING)
                .eq(PayOrderDO::getOrganId, organId)
                .set(PayOrderDO::getStatus, PayOrderStatusEnum.CLOSED.getStatus())
                .set(PayOrderDO::getNotifyTime, LocalDateTime.now())
                .set(PayOrderDO::getChannelErrorCode, notify.getChannelErrorCode())
                .set(PayOrderDO::getChannelErrorMsg, notify.getChannelErrorMsg())
                .set(PayOrderDO::getChannelNotifyData, JsonUtils.zipString(JSON.toJSONString(notify))));

        if (updateCounts == 0) { // 校验状态，必须是待支付
            throw new ServiceException(PAY_ORDER_STATUS_IS_NOT_WAITING);
        }
        log.debug("[notifyOrderClosed][order({}) 更新为支付关闭]", order.getId());

        // 3. 根据交易类型做对应回调处理
        switch (TradeTypeEnum.fromCode(order.getTradeType())) {
            case PRODUCT:
                notifyProductPayOrderFail(order);
                break;
            default:
                throw new ServiceException(PAY_NOTIFY_PASSBACK_TRADETYPE_NOT_SUPPORT_ERROR, order.getTradeType());
        }
    }

    /**
     * 产品购买失败回调
     *
     * @param orderDO
     */
    private void notifyProductPayOrderFail(PayOrderDO orderDO) {
        if (ObjectUtil.isNotNull(orderDO) && ObjectUtil.isNotNull(orderDO.getPurchaseId())) {
            Long organId = orderDO.getOrganId();
            Long purchaseId = orderDO.getPurchaseId();
            PurchaseRecordDO record = purchaseService.getRecord(purchaseId);
            // 1、产品购买第三方支付失败 删除购买记录
            purchaseService.deleteRecord(purchaseId);

            // 2、重置前序产品购买记录下延期记录的备注
            Long previousId = record.getPreviousId();
            if (ObjectUtil.isNotNull(previousId)) {
                productDelayService.resetPurchaseDelayRecordsRemark(previousId, organId);
            }
        }
    }

    @Override
    public int expirePayOrder() {
        // 1、查询过期的待支付订单
        List<PayOrderDO> expireOrders = payOrderMapper.selectList(new LambdaQueryWrapper<PayOrderDO>()
                .eq(PayOrderDO::getStatus, PayOrderStatusEnum.WAITING.getStatus())
                .lt(PayOrderDO::getExpireTime, LocalDateTime.now()));

        // 2、遍历处理
        int count = 0;
        for (PayOrderDO order : expireOrders) {
            count += expireOrder(order) ? 1 : 0;
        }
        return count;
    }

    /**
     * 同步单个支付单
     *
     * @param order 支付单
     * @return 是否已过期
     */
    private boolean expireOrder(PayOrderDO order) {
        try {
            // 1、获取支付渠道
            PayChannelCodeEnum channelCode = order.getChannelCode();
            AbstractPayHandler payHandler = payHandlerFactory.getPayHandler(channelCode.getCode());

            // 2、调用三方接口，查询支付单状态，是不是已支付
            PayOrderRespDTO payOrderRespDTO = payHandler.queryPayOrder(String.valueOf(order.getId()));
            if (PayOrderStatusEnum.isSuccess(payOrderRespDTO.getStatus()) || PayOrderStatusEnum.isClosed(payOrderRespDTO.getStatus())) {
                notifyOrderWithTx(payOrderRespDTO);
                log.info("[expireOrder][order({}) 更新为支付关闭成功]", order.getId());
                return true;
            }

            // 3、系统内超期未支付状态是NOTPAY，主动关单后再次查询状态
            if (PayOrderStatusEnum.WAITING.getStatus().equals(payOrderRespDTO.getStatus())) {
                boolean closeOrderResult = payHandler.closeOrder(String.valueOf(order.getId()));
                if (closeOrderResult) {
                    payOrderRespDTO = payHandler.queryPayOrder(String.valueOf(order.getId()));
                    if (PayOrderStatusEnum.isSuccess(payOrderRespDTO.getStatus()) || PayOrderStatusEnum.isClosed(payOrderRespDTO.getStatus())) {
                        notifyOrderWithTx(payOrderRespDTO);
                        log.info("[expireOrder][order({}) 更新为支付关闭成功]", order.getId());
                        return true;
                    }
                }
            }

            return false;
        } catch (Throwable e) {
            log.error("[expireOrder][order({}) 过期订单异常]", order.getId(), e);
            return false;
        }
    }

    /**
     * 手动控制支付回调中更新数据库操作的事务
     *
     * @param notify
     */
    private void notifyOrderWithTx(PayOrderRespDTO notify) {
        transactionTemplate.executeWithoutResult(status -> {
            try {
                notifyOrder(notify);
            } catch (Exception e) {
                status.setRollbackOnly();
                throw e;
            }
        });
    }

    @Override
    public BigDecimal getRechargeTotalAmount() {
        return payOrderMapper.getRechargeTotalAmount();
    }

    @Override
    public void createPayOrder(PayOrderDO payOrderDO) {
        payOrderMapper.insert(payOrderDO);
    }
}
