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

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
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.ServiceException;
import com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.cf.imes.framework.common.exception.util.ServiceExceptionUtil;
import com.cf.imes.framework.common.util.Assert.AssertUtils;
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.SnowflakeIdWorker3rd;
import com.cf.imes.framework.organ.core.context.OrganContextHolder;
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.module.system.controller.admin.pay.vo.PayOrderUnifiedReqDTO;
import com.cf.imes.framework.common.exception.PayException;
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.organamount.vo.OrganRechargeAmountRespVO;
import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductProcessorRespVO;
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.controller.admin.pay.vo.PayRechargeOrderReqVO;
import com.cf.imes.module.system.dal.dataobject.funds.organamount.OrganAmountDO;
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.funds.recharge.GiftMoneyDetailsDO;
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.PayConstants;
import com.cf.imes.module.system.enums.pay.PayProductChannelCodeEnum;
import com.cf.imes.module.system.enums.pay.TradeTypeEnum;
import com.cf.imes.module.system.enums.recharge.RechargeGiftTypeEnum;
import com.cf.imes.module.system.enums.recharge.RechargeTypeEnum;
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.organamount.OrganAmountService;
import com.cf.imes.module.system.service.funds.products.ProductsDetailService;
import com.cf.imes.module.system.service.funds.recharge.RechargeActiveService;
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.validation.annotation.Validated;

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

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.HashMap;
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.exception.util.ServiceExceptionUtil.exception;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_NOTIFY_PASSBACK_NOT_EXISTS_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;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_RECHARGE_ACTIVITY_CHANNEL_MISMATCH_ERROR;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_RECHARGE_ACTIVITY_EXPIRE_ERROR;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_RECHARGE_ORDER_ORGANID_CHECK_ERROR;

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

    // 外部payNo正则
    private static final Pattern pattern = Pattern.compile("P(\\d+)O");

    @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 OrganAmountService organAmountService;

    @Resource
    private IncomeExpenseService incomeExpenseService;

    @Resource
    private PurchaseService purchaseService;

    @Resource
    private SnowflakeIdWorker3rd snowflakeIdWorker3rd;

    @Resource
    private MybatisIdProperties mybatisIdProperties;

    @Resource
    private RechargeActiveService rechargeActiveService;

    @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);

        // 校验产品和产品明细
        ProductsDetailDO productsDetailDO = productsDetailService.validateProductDetailsExists(payProductOrderUnifiedReqVO.getProductDetailsId());
        ProductsDO productsDO = productsDetailService.validateProductExist(productsDetailDO.getProductId());

        // 查询组织余额信息
        OrganAmountDO organAmount = organAmountService.validOrganAmount(organId);

        // 根据购买类型做价格处理
        PayProductProcessor payProductProcessor = new PayProductProcessor(productsDO, productsDetailDO, organAmount, organId, snowflakeIdWorker3rd, payConfig, mybatisIdProperties);
        ProductProcessorRespVO productProcessorRespVO = payProductProcessor.processPayment(PayProductChannelCodeEnum.fromType(payProductOrderUnifiedReqVO.getProductChannelCode()));

        // 记录入库，如果有外部支付发起请求二维码
        // 购买记录入库
        purchaseService.createRecord(productProcessorRespVO.getPurchaseRecordDO());
        // 收支明细入库
        IncomeExpenseDetailsDO incomeExpenseDetailsDO = productProcessorRespVO.getIncomeExpenseDetailsDO();
        if (ObjectUtil.isNotNull(incomeExpenseDetailsDO)) {
            incomeExpenseService.createDetail(incomeExpenseDetailsDO);
        }

        // 更新组织资金，纯第三方支付不需要更新余额
        OrganAmountDO finalOrganAmountDO = productProcessorRespVO.getOrganAmountDO();
        if (ObjectUtil.isNotNull(finalOrganAmountDO)) {
            organAmountService.productPay(finalOrganAmountDO);
        }

        // 如果有外部支付，发起请求
        PayOrderDO payOrder = productProcessorRespVO.getPayOrderDO();
        if(ObjectUtil.isNotNull(payOrder)) {
            payOrderMapper.insert(payOrder);

            // 拼接充值个性化参数
            Map<String, Object> passbackParams = new HashMap<>();
            passbackParams.put(PayConstants.PAY_PASSBACK_PARAMS_TRADE_TYPE_KEY, TradeTypeEnum.PRODUCT.getCode());// 交易类型
            passbackParams.put(PayConstants.PAY_PASSBACK_PARAMS_PURCHASE_RECORD_ID_KEY, productProcessorRespVO.getPurchaseRecordDO().getId());// 购买记录id，用于更新开票状态

            // 发起渠道方支付
            PayOrderUnifiedReqDTO payOrderUnifiedReqDTO = null;
            Integer channelCode = PayProductChannelCodeEnum.isAliPay(payProductOrderUnifiedReqVO.getProductChannelCode()) ? PayChannelCodeEnum.ALIPAY_PC.getCode() : PayChannelCodeEnum.WECHAT_NATIVE.getCode();
            try {
                // 获取渠道方处理器
                AbstractPayHandler payHandler = payHandlerFactory.getPayHandler(channelCode);

                payOrderUnifiedReqDTO = createPayOrderUnifiedReqDTO(payOrder, passbackParams);
                return payHandler.unifiedOrder(payOrderUnifiedReqDTO);
            } catch (Throwable e) {
                // 系统异常，则包装成 PayException 异常抛出
                log.error("[unifiedOrder][客户端({}) request({}) 发起支付异常]",
                        channelCode, JSON.toJSONString(payOrderUnifiedReqDTO), e);
                throw new PayException(PAY_ORDER_SUBMIT_CHANNEL_ERROR);
            }
        }

        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PayOrderRespDTO rechargePay(PayRechargeOrderReqVO payRechargeOrderReqVO) {
        // 主动校验一次入参
        ValidationUtils.validate(validator, payRechargeOrderReqVO);

        Long organId = payRechargeOrderReqVO.getOrganId();
        // 校验组织名称
        AssertUtils.notEquals(organId, SecurityFrameworkUtils.getUserOrganId(), PAY_RECHARGE_ORDER_ORGANID_CHECK_ERROR);

        // 校验组织套餐
        organAmountService.validOrganAmount(organId);

        // 获取渠道方处理器
        Integer channelCode = payRechargeOrderReqVO.getChannelCode();
        AbstractPayHandler payHandler = payHandlerFactory.getPayHandler(channelCode);

        // 创建支付订单
        PayOrderDO payOrder = createPayOrder(payRechargeOrderReqVO.getPrice(), "imes充值", channelCode);
        // 设置赠送活动编号
        Long rechargeActivityId = payRechargeOrderReqVO.getRechargeActivityId();
        if(ObjectUtil.isNotNull(rechargeActivityId)) {
            // 校验活动类型和渠道是否匹配
            validRechargeActivity(rechargeActivityId, channelCode);
            payOrder.setRechargeActivityId(rechargeActivityId);
        }
        payOrderMapper.insert(payOrder);

        // 拼接充值个性化参数
        Map<String, Object> passbackParams = new HashMap<>();
        passbackParams.put(PayConstants.PAY_PASSBACK_PARAMS_TRADE_TYPE_KEY, TradeTypeEnum.RECHARGE.getCode());

        // 发起渠道方支付
        PayOrderUnifiedReqDTO payOrderUnifiedReqDTO = null;
        try {
            payOrderUnifiedReqDTO = createPayOrderUnifiedReqDTO(payOrder, passbackParams);
            return payHandler.unifiedOrder(payOrderUnifiedReqDTO);
        } catch (Throwable e) {
            // 系统异常，则包装成 PayException 异常抛出
            log.error("[unifiedOrder][客户端({}) request({}) 发起支付异常]",
                    channelCode, JSON.toJSONString(payOrderUnifiedReqDTO), e);
            throw new PayException(PAY_ORDER_SUBMIT_CHANNEL_ERROR);
        }
    }

    /**
     * 校验充值活动和渠道是否匹配
     *
     * @param rechargeActivityId 活动id
     * @param channelCode        支付渠道编码
     */
    private void validRechargeActivity(Long rechargeActivityId, Integer channelCode) {
        GiftMoneyDetailsDO giftMoneyDetailsDO = rechargeActiveService.get(rechargeActivityId);
        Integer rechargeType = giftMoneyDetailsDO.getRechargeType();
        // 当充值活动只支持微信时，渠道只能是微信
        if (RechargeTypeEnum.isWechat(rechargeType) && !PayChannelCodeEnum.isWechat(channelCode)) {
            throw ServiceExceptionUtil.exception(PAY_RECHARGE_ACTIVITY_CHANNEL_MISMATCH_ERROR);
        }
        // 当充值活动只支持支付宝，渠道只能是支付宝
        if (RechargeTypeEnum.isAlipay(rechargeType) && !PayChannelCodeEnum.isAlipay(channelCode)) {
            throw ServiceExceptionUtil.exception(PAY_RECHARGE_ACTIVITY_CHANNEL_MISMATCH_ERROR);
        }
        // 校验是否过期
        if (giftMoneyDetailsDO.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(PAY_RECHARGE_ACTIVITY_EXPIRE_ERROR);
        }
    }

    /**
     * 计算赠送金
     *
     * @param rechargeActivityId
     * @param rechargeAmount
     * @return
     */
    private BigDecimal calcGiftAmount(Long rechargeActivityId, BigDecimal rechargeAmount) {
        BigDecimal totalGift = null;
        if (ObjectUtil.isNull(rechargeActivityId)) {
            return totalGift;
        }
        GiftMoneyDetailsDO rechargeActivityDO = rechargeActiveService.getDefaultNull(rechargeActivityId);

        if (ObjectUtil.isNotNull(rechargeActivityDO)) {
            // 赠送活动已经过期
            if (rechargeActivityDO.getEndTime().isBefore(LocalDateTime.now())) {
                return totalGift;
            }
        } else {
            return totalGift;
        }
        BigDecimal threshold = rechargeActivityDO.getRechargeAmount();
        BigDecimal giftAmount = rechargeActivityDO.getGiftAmount();

        switch (RechargeGiftTypeEnum.fromStatus(rechargeActivityDO.getRechargeGiftType())) {
            case FULL_X_GIVE_X:
                // 满 threshold 才送 giftAmount
                if (rechargeAmount.compareTo(threshold) >= 0) {
                    totalGift = giftAmount;
                }
                break;

            case EVERY_X_GIVE_X:
                // 每满 threshold 送 giftAmount，可累计
                BigDecimal times = rechargeAmount.divide(threshold, 0, RoundingMode.FLOOR);
                totalGift = times.multiply(giftAmount);
                break;
        }

        return totalGift;
    }

    /**
     * 创建支付订单
     *
     * @param
     * @return
     */
    private PayOrderDO createPayOrder(BigDecimal price, String subject, Integer channelCode) {
        PayOrderDO payOrderDO = new PayOrderDO();

        Long payOrderId = IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId();
        Long organId = SecurityFrameworkUtils.getUserOrganId();

        payOrderDO.setId(payOrderId);
        payOrderDO.setPayNo(String.format(PayConstants.PAY_ORDER_PREFIX_FORMAT, organId, payOrderId));
        payOrderDO.setStatus(PayOrderStatusEnum.WAITING);
        payOrderDO.setExpireTime(LocalDateTime.now().plus(payConfig.getPayExpireTime()));
        payOrderDO.setPrice(price);
        payOrderDO.setSubject(subject);
        payOrderDO.setChannelCode(PayChannelCodeEnum.fromType(channelCode));
        payOrderDO.setOrganId(organId);
        return payOrderDO;
    }

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

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

        return payOrderUnifiedReqDTO;
    }

    @Override
    public void queryPayOrderStatus(String outTradeNo, int channelCode) {
        // 校验支付订单是否存在
        PayOrderDO payOrderDO = payOrderMapper.selectOne(new LambdaQueryWrapper<PayOrderDO>()
                .eq(PayOrderDO::getPayNo, outTradeNo)
                .eq(PayOrderDO::getOrganId, validOrganId())
                .eq(PayOrderDO::getDeleted, false)
        );
        if (ObjectUtils.isNull(payOrderDO)) {
            throw exception(PAY_ORDER_NOT_EXIST, outTradeNo);
        }
        // 订单已被更新成功，直接支付成功了
        if (PayOrderStatusEnum.SUCCESS.getStatus().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 PayException(PAY_ORDER_CHANNEL_QUERY_NOT_EXIST);
            }
        } catch (Throwable e) {
            log.error("渠道方查询支付订单异常", e);
            throw new PayException(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 PayException(PAY_ORDER_NOTIFY_DATA_PARSE_ERROR);
        }
    }

    /**
     * 处理回调数据成功/失败
     *
     * @param notify
     */
    public void notifyOrder(PayOrderRespDTO notify) {
        // 从payNo中解析组织id
        Long organId = getOrganIdFromPayNo(notify.getOutTradeNo());

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

    /**
     * 从payNo中解析组织id
     *
     * @param outTradeNo
     * @return
     */
    private Long getOrganIdFromPayNo(String outTradeNo) {
        Matcher matcher = pattern.matcher(outTradeNo);

        if (matcher.find()) {
            return Long.valueOf(matcher.group(1));  // 提取 P 和 O 中间的数字
        } else {
            throw new ServiceException(GlobalErrorCodeConstants.UNAUTHORIZED);
        }
    }

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

        // 3. 根据交易类型做对应回调处理
        Map<String, Object> passbackParams = notify.getPassbackParams();
        Object passbackObj = passbackParams.get(PayConstants.PAY_PASSBACK_PARAMS_TRADE_TYPE_KEY);
        AssertUtils.notEmpty(passbackObj, PAY_NOTIFY_PASSBACK_NOT_EXISTS_ERROR);
        switch (TradeTypeEnum.fromCode(NumberUtil.parseInt(passbackObj.toString()))) {
            case RECHARGE:
                notifyRechargeOrderSuccess(order, organId);
                break;
            case PRODUCT:
                notifyProductPayOrderSuccess(organId, passbackParams);
                break;
            default:
                throw ServiceExceptionUtil.exception(PAY_NOTIFY_PASSBACK_TRADETYPE_NOT_SUPPORT_ERROR, passbackObj.toString());
        }
    }

    /**
     * 充值成功回调
     *
     * @param payOrderDO
     * @param organId
     */
    private void notifyRechargeOrderSuccess(PayOrderDO payOrderDO, Long organId) {
        // 支付订单的金额
        BigDecimal price = payOrderDO.getPrice();
        // 订单流水号
        String payNo = payOrderDO.getPayNo();

        // 1.更新组织资金
        // 检查赠送金是否存在已经是否在有效期，有效的话计入组织充值赠送金
        BigDecimal giftAmount = calcGiftAmount(payOrderDO.getRechargeActivityId(), price);

        // 更新
        OrganRechargeAmountRespVO rechargeAmountRespVO = organAmountService.recharge(organId, price, giftAmount);

        // 2.新增收支明细
        IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
                .organId(organId)
                .businessNo(payNo)
                .tradeType(TradeTypeEnum.RECHARGE.getCode())
                .incomeExpenseType(IncomeExpenseTypeEnum.INCOME.getCode())
                .cashAmountChange(price)
                .giftAmountChange(giftAmount)
                .availableAmount(rechargeAmountRespVO.getAvailableAmount())
                .amount(rechargeAmountRespVO.getAmount())
                .giftAmount(rechargeAmountRespVO.getGiftAmount())
                .build();
        incomeExpenseDetailsDO.setCreator(payOrderDO.getCreator());
        incomeExpenseDetailsDO.setUpdater(payOrderDO.getUpdater());
        incomeExpenseService.createDetail(incomeExpenseDetailsDO);
    }

    /**
     * 产品购买成功回调
     * @param organId
     * @param passbackParams
     */
    private void notifyProductPayOrderSuccess(Long organId, Map<String, Object> passbackParams) {
        // 1、更新购买记录的开票状态
        Object purchaseRecordIdObj = passbackParams.get(PayConstants.PAY_PASSBACK_PARAMS_PURCHASE_RECORD_ID_KEY);
        if (ObjectUtil.isNotNull(purchaseRecordIdObj)) {
            purchaseService.updateRecordInvoicable(NumberUtil.parseLong(purchaseRecordIdObj.toString()), organId);
        }
    }

    /**
     * 支付失败回调处理
     *
     * @param notify
     */
    private void notifyOrderClosed(PayOrderRespDTO notify, Long organId) {
        // 1. 查询 PayOrderDO
        String outTradeNo = notify.getOutTradeNo();
        PayOrderDO order = payOrderMapper.selectOne(new LambdaQueryWrapper<PayOrderDO>()
                .eq(PayOrderDO::getPayNo, outTradeNo)
                .eq(PayOrderDO::getOrganId, organId));
        if (ObjectUtil.isNull(order)) {
            throw exception(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 exception(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.FAIL.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 exception(PAY_ORDER_STATUS_IS_NOT_WAITING);
        }
        log.debug("[notifyOrderClosed][order({}) 更新为支付关闭]", order.getId());

        // 3. 根据交易类型做对应回调处理
        Map<String, Object> passbackParams = notify.getPassbackParams();
        Object passbackObj = passbackParams.get(PayConstants.PAY_PASSBACK_PARAMS_TRADE_TYPE_KEY);
        AssertUtils.notEmpty(passbackObj, PAY_NOTIFY_PASSBACK_NOT_EXISTS_ERROR);
        switch (TradeTypeEnum.fromCode(NumberUtil.parseInt(passbackObj.toString()))) {
            case RECHARGE:
                notifyRechargeOrderFail(order, organId);
                break;
            case PRODUCT:
                notifyProductPayOrderFail(organId, passbackParams);
                break;
            default:
                throw ServiceExceptionUtil.exception(PAY_NOTIFY_PASSBACK_TRADETYPE_NOT_SUPPORT_ERROR, passbackObj.toString());
        }
    }

    /**
     * 充值失败回调
     *
     * @param payOrderDO
     * @param organId
     */
    private void notifyRechargeOrderFail(PayOrderDO payOrderDO, Long organId) {
        // todo 充值失败回调
    }

    /**
     * 产品购买失败回调
     *
     * @param organId
     * @param passbackParams
     */
    private void notifyProductPayOrderFail(Long organId, Map<String, Object> passbackParams) {
        Object purchaseRecordIdObj = passbackParams.get(PayConstants.PAY_PASSBACK_PARAMS_PURCHASE_RECORD_ID_KEY);
        if (ObjectUtil.isNotNull(purchaseRecordIdObj)) {
            PurchaseRecordDO record = purchaseService.getRecord(NumberUtil.parseLong(purchaseRecordIdObj.toString()));

            // 1、混合支付失败，退回扣除的现金和赠送金
            BigDecimal accountBalanceSpent = record.getAccountBalanceSpent();
            BigDecimal giftMoneySpent = record.getGiftMoneySpent();
            OrganRechargeAmountRespVO organRefundProductRespVO = organAmountService.refundProductOrganAmountBalanceAndGift(organId, accountBalanceSpent, giftMoneySpent);

            // 2、新增退回的收支记录
            IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
                    .organId(organId)
                    .tradeType(TradeTypeEnum.PRODUCT.getCode())
                    .incomeExpenseType(IncomeExpenseTypeEnum.INCOME.getCode())
                    .cashAmountChange(accountBalanceSpent)
                    .giftAmountChange(giftMoneySpent)
                    .availableAmount(organRefundProductRespVO.getAvailableAmount())
                    .amount(organRefundProductRespVO.getAmount())
                    .giftAmount(organRefundProductRespVO.getGiftAmount())
                    .build();
            incomeExpenseDetailsDO.setCreator(record.getCreator());
            incomeExpenseDetailsDO.setUpdater(record.getUpdater());
            incomeExpenseService.createDetail(incomeExpenseDetailsDO);
        }
    }

    /**
     * 校验获取当前token请求下的组织id
     *
     * @return
     */
    private Long validOrganId() {
        // 校验token
        Long organId = OrganContextHolder.getOrganId();
        if (ObjectUtil.isNull(organId)) {
            throw new ServiceException(GlobalErrorCodeConstants.UNAUTHORIZED);
        }
        return 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(order.getPayNo());
            if (PayOrderStatusEnum.isSuccess(payOrderRespDTO.getStatus())) {
                notifyOrder(payOrderRespDTO);
                return false;
            }
            log.info("[expireOrder][order({}) 更新为支付关闭失败]", order.getId());
            return true;
        } catch (Throwable e) {
            log.error("[expireOrder][order({}) 过期订单异常]", order.getId(), e);
            return false;
        }
    }
}
