package com.iformall.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.iformall.common.ErrorCode;
import com.iformall.common.IdWorker;
import com.iformall.common.Result;
import com.iformall.common.ResultData;
import com.iformall.domain.dto.WxSharingOrderDto;
import com.iformall.domain.po.*;
import com.iformall.domain.vo.PromotionCalc;
import com.iformall.domain.vo.WxCardSpendVo;
import com.iformall.enums.*;
import com.iformall.exception.MallinkException;
import com.iformall.mapper.*;
import com.iformall.service.*;
import com.iformall.utils.PayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Service
public class WxCardSpendServiceImpl implements WxCardSpendService {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    WxCardSpendMapper wxCardSpendMapper;

    @Autowired
    WxCardInfoMapper wxCardInfoMapper;

    @Autowired
    WxCouponMapper wxCouponMapper;

    @Autowired
    WxMerchantMapper wxMerchantMapper;

    @Autowired
    WxMerchantSubsidyMapper wxMerchantSubsidyMapper;

    @Autowired
    WxCouponMerchantMapper wxCouponMerchantMapper;

    @Autowired
    WxPayAccountMapper payAccountMapper;

    @Autowired
    WxOrderMapper wxOrderMapper;

    @Autowired
    WxCouponOrderMapper wxCouponOrderMapper;

    @Autowired
    WxProfitSharingOrderService profitSharingOrderService;

    @Autowired
    ExcelService excelService;

    @Autowired
    WxCreditHistoryService wxCreditHistoryService;

    @Autowired
    WxScoreRulesService wxScoreRulesService;

    @Override
    public WxCouponMerchant checkMerchantInCoupon(WxCardInfo cardInfo, Long merchantId) {
        WxCouponMerchant couponMerchant = null;
        WxCouponMerchant couponMerchantQ = new WxCouponMerchant();
        couponMerchantQ.setTenantId(cardInfo.getTenantId());
        couponMerchantQ.setProductId(cardInfo.getCouponId());
        couponMerchantQ.setMerchantId(merchantId);
        couponMerchantQ.setStatus(EnumCouponMerchantStatus.COUPON_MERCHANT_STATUS_VALID.getCode());
        try {
            couponMerchant = wxCouponMerchantMapper.selectOne(new QueryWrapper(couponMerchantQ));
        } catch (Exception e) {
            logger.error("couponmerchant not found: " + cardInfo.getCouponId());
        }
        return couponMerchant;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class})
    public ResultData createCardSpend(WxCardSpend record, WxOrder order, WxCouponMerchant couponMerchant) {
        Date curDate = new Date();
        final IdWorker idWorker = IdWorker.get();
        // 1. get card info
        WxCardInfo cardInfo = wxCardInfoMapper.selectById(record.getCardId());
        if (cardInfo == null) {
            logger.error("card not found: " + record.getCardId());
            return new ResultData(ErrorCode.CARD_IS_NOT_FOUND);
        }
        // 2. coupon
        Integer subsidyRate = 0;
        WxCoupon coupon = wxCouponMapper.selectById(cardInfo.getCouponId());
        if (coupon == null) {
            logger.error("card's coupon not found: " + cardInfo.getCouponId());
            return new ResultData(ErrorCode.CARD_IS_NOT_FOUND);
        }
        if (coupon.getSubsidyNum() == null) {
            logger.error("card's coupon not found: " + cardInfo.getCouponId());
            return new ResultData(ErrorCode.SYS_PARAMETER_ERROR.getCode(), "券补贴额未填写");
        }

        // 3. get pay account
        WxPayAccount payAccount = null;
        WxPayAccount payAccountQ = new WxPayAccount();
        payAccountQ.setTenantId(record.getTenantId());
        try {
            payAccount = payAccountMapper.selectOne(new QueryWrapper(payAccountQ));
        } catch (Exception e) {
            logger.error("获取payAccount error: " + record.getTenantId());
            return new ResultData(ErrorCode.DB_FAIL.getCode(), "获取payAccount失败");
        }

        // 4.  扣减计算
        Double paymentD = 1.0 * cardInfo.getSaleAmount() * record.getDeductionAmount() / cardInfo.getAmount();
        Integer payment = paymentD.intValue();
        int iChargeFee = PayUtils.getPayRate(payment, payAccount.getRate(), false);
        Integer real_payment = payment - iChargeFee;
        Integer remain_real_pament = cardInfo.getRemainingShareFeeAmount() - real_payment;
        Integer remaingAmount = cardInfo.getRemainingAmount() - record.getDeductionAmount();
        if (remaingAmount.equals(0)) {
            // 最后一次支付时
            real_payment = Math.min(real_payment, cardInfo.getRemainingShareFeeAmount());
            remain_real_pament = cardInfo.getRemainingShareFeeAmount() - real_payment;
        }

        // 5 insert card_spend
        record.setId(idWorker.nextId());
        record.setTenantId(cardInfo.getTenantId());
        record.setPayFrom(EnumCardSpendFrom.C.getCode());
        record.setPayment(payment);
        record.setRealPayment(real_payment);
        record.setCardBeforeAmount(cardInfo.getRemainingAmount());
        record.setCardRemainAmount(remaingAmount);
        record.setCardBeforeRealAmount(cardInfo.getRemainingShareFeeAmount());
        record.setCardRemainRealAmount(remain_real_pament);
        record.setPayStatus(EnumCardSpendStatus.NOT_PAY.getCode());
        record.setCreateDate(curDate);
        record.setUpdateDate(curDate);
        try {
            wxCardSpendMapper.insert(record);
        } catch (Exception e) {
            logger.error("card spend insert error");
            return new ResultData(ErrorCode.DB_FAIL.getCode(), "card spend 插入出错！");
        }

        // 6. update order status
        WxOrder orderUpdate = new WxOrder();
        orderUpdate.setId(order.getId());
        orderUpdate.setTenantId(record.getTenantId());
        orderUpdate.setCUserId(record.getOwnerId());
        orderUpdate.setPaymentTime(curDate);
        orderUpdate.setOrderStatus(EnumOrderStatus.ORDER_STATUS_PAYMENT_SUCCESS.getCode());
        orderUpdate.setUpdateDate(curDate);
        try {
            wxOrderMapper.updateById(orderUpdate);
        } catch (Exception e) {
            logger.error("order update error");
            return new ResultData(ErrorCode.DB_FAIL.getCode(), "order update 出错！");
        }

        // 7. update card info
        WxCardInfo updateCardInfo = new WxCardInfo();
        updateCardInfo.setId(cardInfo.getId());
        updateCardInfo.setRemainingAmount(remaingAmount);
        updateCardInfo.setRemainingShareFeeAmount(remain_real_pament);
        updateCardInfo.setUpdateDate(curDate);
        cardInfo.setRemainingAmount(remaingAmount);
        cardInfo.setRemainingShareFeeAmount(remain_real_pament);
        cardInfo.setUpdateDate(curDate);
        // 7.1、消费后修改卡转赠状态
        if (cardInfo.getSupportTransfer().equals(EnumCouponTransfer.YES.getCode())) {
            updateCardInfo.setSupportTransfer(EnumCouponTransfer.NO.getCode());
            cardInfo.setSupportTransfer(EnumCouponTransfer.NO.getCode());
        }
        try {
            wxCardInfoMapper.updateById(updateCardInfo);
        } catch (Exception e) {
            logger.error("card info update error");
            return new ResultData(ErrorCode.DB_FAIL.getCode(), "card info 更新出错！");
        }

        // 7.2 update coupon card status
        if (cardInfo.getRemainingAmount().equals(0)) {
            if (cardFinished(curDate, cardInfo)) {
                return new ResultData(ErrorCode.DB_FAIL.getCode(), "couponOrder status 更新出错！");
            }
        }

        // 8. 补贴
        // 补贴额 = 商城补贴总额/卡券面额 * 抵扣额(本次支付额)
        Double subsidyFeeD = 1.0 * record.getDeductionAmount() * coupon.getSubsidyNum() / cardInfo.getAmount();
        Integer subsidyFee = subsidyFeeD.intValue();
        logger.info("补贴额: " + subsidyFee);
        if (cardInfo.getSaleAmount().equals(0) || subsidyFee > 0) {
            // 有补贴时入库
            int iSubChargeFee = PayUtils.getPayRate(subsidyFee, payAccount.getRate(), false);
            Integer realSubsidyFee = subsidyFee - iSubChargeFee;
            WxMerchantSubsidy merchantSubsidy = new WxMerchantSubsidy();
            merchantSubsidy.setId(idWorker.nextId());
            merchantSubsidy.setTenantId(record.getTenantId());
            merchantSubsidy.setOrderId(order.getId());
            merchantSubsidy.setOrderType(EnumOrderType.PREPAIDCARD.getCode());
            merchantSubsidy.setMerchantId(record.getMerchantId());
            merchantSubsidy.setCouponOrderId(cardInfo.getId());
            merchantSubsidy.setCouponType(EnumCouponType.CARD_MULTIMCH.getCode());
            merchantSubsidy.setOrderPayment(record.getDeductionAmount());
            merchantSubsidy.setReceiverPayment(payment);
            merchantSubsidy.setRealPayment(real_payment);
            merchantSubsidy.setSubsidy(subsidyFee);
            merchantSubsidy.setRealSubsidy(realSubsidyFee);
            if (coupon.getSubsidyType().equals(EnumCouponSubsidyType.WECHAT_COUPON.getCode())) {
                // 立减领券
                merchantSubsidy.setStatus(EnumMerchantSubsidyStatus.AUTO_SUBSIDIED.getCode());
            } else if (coupon.getSubsidyType().equals(EnumCouponSubsidyType.WECHAT_MCHPAY.getCode())) {
                // 转账倒现金
            } else {
                // 未补贴
                merchantSubsidy.setStatus(EnumMerchantSubsidyStatus.NOT_SUBSIDY.getCode());
            }

            merchantSubsidy.setType(EnumMerchantSubsidyType.MANUAL.getCode());
            merchantSubsidy.setSource(EnumMerchantSubsidySource.CARD.getCode());

            merchantSubsidy.setCreateDate(curDate);
            merchantSubsidy.setUpdateDate(curDate);
            try {
                wxMerchantSubsidyMapper.insert(merchantSubsidy);
            } catch (Exception e) {
                logger.error("merchantSubsidy info insert error");
                return new ResultData(ErrorCode.DB_FAIL.getCode(), "merchantSubsidy info 插入出错！");
            }
        }

        if (payAccount.checkShare()) {
            // 9. 分账
            try {
                shareForCardPay(record.getTenantId(), record.getCardId(), record.getOrderId(), record.getId());
            } catch (MallinkException e) {
                logger.error(e.getMessage());
                if (ErrorCode.PROFIT_SHARING_NUM_UP_LIMIT.getCode() == e.getErrorCode()) {
                    // 分账已到20次限制，后面只记录，无法分账
                    logger.error("分账已到20次限制，后面只记录，无法分账");
                    try {
                        creteMerchantSubsidy(record, curDate, idWorker, coupon, payment);
                    } catch (Exception ex) {
                        logger.error("分账已到20次限制，记录到结算表失败");
                    }
                } else {
                    throw new MallinkException(e.getErrorCode(), e.getMessage());
                }
            }
        } else {
            if (cardInfo.getSaleAmount() > 0) {
                try {
                    creteMerchantSubsidy(record, curDate, idWorker, coupon, payment);
                } catch (Exception ex) {
                    logger.error("记录到结算表失败");
                }
            }
        }

        // 成长值
        try {
            orderUpdate.setPayment(real_payment);
            wxScoreRulesService.addScore2(EnumScoreType.CONSUMPTION, orderUpdate, coupon.getBusiness());
        } catch (Exception e) {
            logger.error("成长值:" + e.getMessage());
        }

        // 积分
        try {
            //-------此处为【现金支付】记录增加积分操作-------
            WxCreditHistory creditHistory = new WxCreditHistory();
            creditHistory.setOperatorType(EnumUserType.CUSER.getCode());
            creditHistory.setOperatorId(record.getOwnerId());
            creditHistory.setCUserId(record.getOwnerId());
            creditHistory.setCreateDate(new Date());
            creditHistory.setTenantId(record.getTenantId());
            creditHistory.setCreditType(EnumScoreType.CONSUMPTION.getCode());
            creditHistory.setBusinessId(coupon.getBusiness());
            creditHistory.setSpend(real_payment);
            //如果券与商户一对一 则直接将消费商户更新为此商户 若一对多 则消费商户显示多商户
            creditHistory.setMerchantId(record.getMerchantId());
            wxCreditHistoryService.saveOrUpdate(creditHistory);
        } catch (Exception e) {
            logger.error("积分值:" + e.getMessage());
        }

        return new ResultData(record);
    }

    public void creteMerchantSubsidy(WxCardSpend record, Date curDate, IdWorker idWorker, WxCoupon coupon, Integer payment) {
        WxMerchantSubsidy merchantSubsidy = new WxMerchantSubsidy();
        merchantSubsidy.setId(idWorker.nextId());
        merchantSubsidy.setTenantId(record.getTenantId());
        merchantSubsidy.setOrderId(record.getOrderId());
        merchantSubsidy.setOrderType(EnumOrderType.PREPAIDCARD.getCode());
        merchantSubsidy.setMerchantId(record.getMerchantId());
        merchantSubsidy.setCouponOrderId(record.getCardId());
        merchantSubsidy.setCouponType(coupon.getType());
        merchantSubsidy.setOrderPayment(record.getPayment());
        merchantSubsidy.setReceiverPayment(record.getPayment());
        merchantSubsidy.setRealPayment(record.getRealPayment());
        merchantSubsidy.setSubsidy(payment);
        merchantSubsidy.setRealSubsidy(payment);
        merchantSubsidy.setCreateDate(curDate);
        merchantSubsidy.setUpdateDate(curDate);
        merchantSubsidy.setType(EnumMerchantSubsidyType.WECHAT.getCode());
        merchantSubsidy.setSource(EnumMerchantSubsidySource.CARD.getCode());
        wxMerchantSubsidyMapper.insert(merchantSubsidy);
    }

    /**
     * 卡已用完
     *
     * @param curDate
     * @param cardInfo
     * @return
     */
    private boolean cardFinished(Date curDate, WxCardInfo cardInfo) {
        WxCouponOrder couponOrder = new WxCouponOrder();
        couponOrder.setId(cardInfo.getId());
        couponOrder.setCouponId(cardInfo.getCouponId());
        couponOrder.setCouponOrderStatus(EnumCouponOrderStatus.CARD_COMPLETE.getCode());
        couponOrder.setUpdateDate(curDate);
        try {
            wxCouponOrderMapper.updateById(couponOrder);
        } catch (Exception e) {
            logger.error("couponOrder status update error");
            return true;
        }
        return false;
    }


    @Override
    public PageInfo<WxCardSpendVo> listAsPage(WxCardSpendVo record, Integer pageIndex, Integer pageSize) {
        return PageHelper.startPage(pageIndex, pageSize).doSelectPageInfo(() -> wxCardSpendMapper.findCardSpendVoList(record));
    }

    @Override
    public List<Map<String, Object>> sumCardSpendForMerchant(WxCardSpendVo record) {
        return wxCardSpendMapper.sumCardSpendVoForMerchant(record);
    }

    @Override
    public List<Map<String, Object>> sumCardSpendForOwner(WxCardSpendVo record) {
        return wxCardSpendMapper.sumCardSpendVoForOwner(record);
    }

    @Override
    public WxCardSpend getById(Long id) {
        return wxCardSpendMapper.selectById(id);
    }

    @Override
    public void saveOrUpdate(WxCardSpend record) {
        if (record.getId() == null) {
            //record.setId(UUID.randomUUID().toString().replaceAll("-", ""));
            final IdWorker idWorker = IdWorker.get();
            record.setId(idWorker.nextId());
            wxCardSpendMapper.insert(record);
        } else {
            wxCardSpendMapper.updateById(record);
        }
    }

    @Override
    public void update(WxCardSpend record) {
        // 1. 检查符合条件的卡消费记录是否已支付
        List<WxCardSpend> cardSpends = wxCardSpendMapper.findList(record);
        if (cardSpends.stream().anyMatch((cs -> (!cs.getPayStatus().equals(EnumCardSpendStatus.NOT_PAY.getCode()))))) {
            throw new MallinkException(ErrorCode.CARD_SPEND_IS_PAID);
        }
        // 2. 更新支付状态
        record.setPayStatus(EnumCardSpendStatus.MANUAL_PAY.getCode());
        record.setUpdateDate(new Date());
        wxCardSpendMapper.updateByCond(record);
    }

    @Override
    public int finishCard(String tenantId, Long cardId) {
        // admin用户手动帮已用卡消费用户退卡, 此卡需要分账完结, 买卡的钱进商场账户
        WxCardInfo cardInfo = null;
        try {
            cardInfo = wxCardInfoMapper.selectById(cardId);
        } catch (Exception e) {
            logger.error("微信分账: " + e.getMessage());
            throw new MallinkException(ErrorCode.DB_FAIL.getCode(), e.getMessage());
        }
        if (cardInfo != null) {
            if (cardInfo.getRemainingAmount().equals(cardInfo.getAmount())) {
                logger.error("请用订单退款接口: ");
                throw new MallinkException(ErrorCode.CARD_IS_NOT_COST);
            } else if (cardInfo.getRemainingAmount() > 0) {
                // 卡已消费， 余额不为0
                int remainShareFeeAmount = cardInfo.getRemainingShareFeeAmount();
                try {
                    WxCouponOrder couponOrder = wxCouponOrderMapper.selectById(cardInfo.getId());
                    // 更新 coupon order状态
                    couponOrder.setCouponOrderStatus(EnumCouponOrderStatus.CARD_RETURNED.getCode());
                    couponOrder.setUpdateDate(new Date());
                    wxCouponOrderMapper.updateById(couponOrder);
                    // 卡余额
                    WxCardInfo cardInfo1 = new WxCardInfo();
                    cardInfo1.setId(cardInfo.getId());
                    cardInfo1.setRemainingAmount(0);
                    cardInfo1.setUpdateDate(new Date());
                    wxCardInfoMapper.updateById(cardInfo1);

                    // 分账完结
                    WxSharingOrderDto shareOrder = new WxSharingOrderDto();
                    shareOrder.setCUserId(couponOrder.getOwnerId());
                    shareOrder.setTenantId(tenantId);
                    shareOrder.setMerchantId(0L); // 卡完结，默认给0
                    shareOrder.setTransactionId(cardInfo.getTransactionId());
                    cardShareFinished(cardInfo, shareOrder);
                } catch (MallinkException e) {
                    logger.error("微信分账: " + e.getMessage());
                    throw new MallinkException(e.getErrorCode(), e.getMessage());
                }
                return remainShareFeeAmount;
            }
        }
        return 0;
    }

    @Override
    public void deleteById(Long id) {
        wxCardSpendMapper.deleteById(id);
    }


    @Override
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class})
    public void shareForCardPay(String tenantId, Long cardId, Long orderId, Long cardSpendId) {
        // 微信已分账检查次数
        int psNum = 0;
        try {
            WxCardSpend cardSpendQ = new WxCardSpend();
            cardSpendQ.setTenantId(tenantId);
            cardSpendQ.setCardId(cardId);
            psNum = wxCardSpendMapper.selectCount(new QueryWrapper(cardSpendQ));
            if (psNum > 20) {
                logger.error("微信分账已超过限制次数-20次");
                throw new MallinkException(ErrorCode.PROFIT_SHARING_NUM_UP_LIMIT);
            }
        } catch (MallinkException e) {
            logger.error(e.getMessage());
            throw new MallinkException(e.getErrorCode(), e.getMessage());
        } catch (Exception e) {
            logger.error(e.getMessage());
            throw new MallinkException(ErrorCode.DB_FAIL);
        }
        // 微信分账
        try {
            WxCardSpend cardSpend = new WxCardSpend();
            cardSpend.setTenantId(tenantId);
            cardSpend.setId(cardSpendId);
            cardSpend.setCardId(cardId);
            cardSpend.setOrderId(orderId);
            cardSpend = wxCardSpendMapper.selectOne(new QueryWrapper(cardSpend));
            if (cardSpend != null && cardSpend.getRealPayment() <= 0) {
                logger.error("微信分账金额为0, 无法再分");
                return;
            }
            WxCardInfo cardInfo = wxCardInfoMapper.selectById(cardSpend.getCardId());
            WxOrder wxOrder = wxOrderMapper.selectById(orderId);
            WxMerchant merchant = wxMerchantMapper.selectById(cardSpend.getMerchantId());

            if (cardSpend != null && cardInfo != null && merchant != null) {
                // 分账
                WxSharingOrderDto shareOrder = new WxSharingOrderDto();
                shareOrder.setCUserId(cardSpend.getOwnerId());
                shareOrder.setMerchantId(cardSpend.getMerchantId());
                shareOrder.setType(EnumProfitSharingOrderType.PROFIT_SHARING_MULTI.getCode());
                shareOrder.setPayAmount(cardSpend.getPayment());
                shareOrder.setOrderId(cardSpendId);
                shareOrder.setPayTimeStart(cardSpend.getCreateDate());
                shareOrder.setPayTimeEnd(cardSpend.getUpdateDate());
                shareOrder.setTenantId(tenantId);
                shareOrder.setTransactionId(cardInfo.getTransactionId());
                shareOrder.setShareAmount(cardSpend.getRealPayment());
                ResultData resultData = profitSharingOrderService.createSharingOrder(shareOrder);
                if (resultData.code != Result.SUCCESS) {
                    // 分账异常
                    logger.error(resultData.message);
                    throw new MallinkException(resultData.code, resultData.message);
                }

                // 卡消费记录状态更新
                {
                    WxCardSpend updateRecord = new WxCardSpend();
                    updateRecord.setId(cardSpend.getId());
                    updateRecord.setPayStatus(EnumCardSpendStatus.PS_SHARED.getCode());
                    updateRecord.setUpdateDate(new Date());
                    wxCardSpendMapper.updateById(updateRecord);
                }


                if (cardSpend.getCardRemainAmount().equals(0)
                        || psNum == 20) {
                    // 卡余额为0，或者卡已分账20次，发起分账完结
                    if (cardSpend.getCardRemainRealAmount() > 0) {
                        // 卡已被抵扣，但是分账还有余额
                        shareOrder.setMerchantId(0L); // 卡完结，填0
                        cardShareFinished(cardInfo, shareOrder);
                    }
                }
            } else {
                logger.error("微信分账: 未找到payorder or merchant at orderid-" + orderId);
            }
        } catch (MallinkException e) {
            logger.error("微信分账: " + e.getMessage());
            throw new MallinkException(e.getErrorCode(), e.getMessage());
        }
    }


    /**
     * 卡分账完结
     *
     * @param cardInfo
     * @param shareOrder
     */
    private void cardShareFinished(WxCardInfo cardInfo, WxSharingOrderDto shareOrder) {
        Date curDate = new Date();
        // card spend 添加一条 记录]
        final IdWorker idWorker = IdWorker.get();
        WxCardSpend cardSpend = new WxCardSpend();
        cardSpend.setTenantId(cardInfo.getTenantId());
        cardSpend.setId(idWorker.nextId());
        cardSpend.setCardId(cardInfo.getId());
        cardSpend.setOrderId(null);
        cardSpend.setDeductionAmount(0);
        cardSpend.setPayment(cardInfo.getRemainingShareFeeAmount());
        cardSpend.setRealPayment(cardInfo.getRemainingShareFeeAmount());
        cardSpend.setCardBeforeAmount(cardInfo.getRemainingAmount());
        cardSpend.setCardRemainAmount(0);
        cardSpend.setCardBeforeRealAmount(cardInfo.getRemainingShareFeeAmount());
        cardSpend.setCardRemainRealAmount(0);
        cardSpend.setCreateDate(curDate);
        cardSpend.setUpdateDate(curDate);
        try {
            wxCardSpendMapper.insert(cardSpend);
        } catch (Exception e) {
            logger.error("card spend insert error");
            return;
        }
        // card info 更新 分账 余额
        WxCardInfo cardInfo1 = new WxCardInfo();
        cardInfo1.setId(cardInfo.getId());
        cardInfo1.setRemainingShareFeeAmount(0);
        cardInfo1.setUpdateDate(curDate);
        try {
            wxCardInfoMapper.updateById(cardInfo1);
        } catch (Exception e) {
            logger.error("card info remaining error");
            return;
        }

        // 分账完结
        shareOrder.setOrderId(cardSpend.getId());
        shareOrder.setPayAmount(0);
        shareOrder.setOrderId(cardSpend.getId());
        if (cardInfo.getRateAmount() != null) {
            // 分账实际金额 = 分账实际剩余金额 + 通道费差
            shareOrder.setShareAmount(cardInfo.getRemainingShareFeeAmount() + cardInfo.getRateAmount());
        } else {
            shareOrder.setShareAmount(cardInfo.getRemainingShareFeeAmount());
        }
        shareOrder.setType(EnumProfitSharingOrderType.PROFIT_SHARING_MULTI_FINISH.getCode());
        profitSharingOrderService.finishSharingOrder(shareOrder);
    }

    @Override
    public void exportData(WxCardSpendVo wxCardSpendVo, HttpServletRequest request, HttpServletResponse response) {
        List<WxCardSpendVo> list = wxCardSpendMapper.findCardSpendVoList(wxCardSpendVo);
        excelService.exportExcel(list, null, "卡消费记录", WxCardSpendVo.class, "卡消费记录.xlsx", response, false);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class})
    public WxCardSpend cardSpendForPosPrePay(WxCardSpend record) throws MallinkException {
        final IdWorker idWorker = IdWorker.get();
        Date curDate = new Date();
        // 1. get card info
        WxCardInfo cardInfo = wxCardInfoMapper.selectById(record.getCardId());
        if (cardInfo == null) {
            logger.error("card not found: " + record.getCardId());
            throw new MallinkException(ErrorCode.CARD_IS_NOT_FOUND);
        }
        // 2. coupon
        Integer subsidyRate = 0;
        WxCoupon coupon = wxCouponMapper.selectById(cardInfo.getCouponId());
        if (coupon == null) {
            logger.error("card's coupon not found: " + cardInfo.getCouponId());
            throw new MallinkException(ErrorCode.CARD_IS_NOT_FOUND);
        }
        if (coupon.getSubsidyNum() == null) {
            logger.error("卡补贴额未填写: " + cardInfo.getCouponId());
            throw new MallinkException(ErrorCode.CARD_SUBSIDY_NOT_SET);
        }

        // 3. get pay account
        WxPayAccount payAccount = null;
        WxPayAccount payAccountQ = new WxPayAccount();
        payAccountQ.setTenantId(record.getTenantId());
        try {
            payAccount = payAccountMapper.selectOne(new QueryWrapper(payAccountQ));
        } catch (Exception e) {
            logger.error("获取payAccount error: " + record.getTenantId());
            throw new MallinkException(ErrorCode.SYS_MCH_NOT_FOUND);
        }

        // 查询是否有已有的
        WxCardSpend oldCardSpend = null;
        WxCardSpend cardSpendQ = new WxCardSpend();
        cardSpendQ.setCardId(record.getCardId());
        cardSpendQ.setPayFrom(EnumCardSpendFrom.POS.getCode());
        cardSpendQ.setPosOrderId(record.getPosOrderId());
        cardSpendQ.setPayStatus(EnumCardSpendStatus.PREV_PAY.getCode());
        try {
            oldCardSpend = wxCardSpendMapper.selectOne(new QueryWrapper(cardSpendQ));
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        if (oldCardSpend != null) {
            return oldCardSpend;
        }

        // 4.  扣减计算
        Double paymentD = 1.0 * cardInfo.getSaleAmount() * record.getDeductionAmount() / cardInfo.getAmount();
        Integer payment = paymentD.intValue();
        int iChargeFee = PayUtils.getPayRate(payment, payAccount.getRate(), false);
        Integer real_payment = payment - iChargeFee;
        Integer remain_real_pament = cardInfo.getRemainingShareFeeAmount() - real_payment;
        Integer remaingAmount = cardInfo.getRemainingAmount() - record.getDeductionAmount();
        if (remaingAmount.equals(0)) {
            // 最后一次支付时
            real_payment = Math.min(real_payment, cardInfo.getRemainingShareFeeAmount());
            remain_real_pament = cardInfo.getRemainingShareFeeAmount() - real_payment;
        }
        // 4.1 TODO 是否已扣减??

        // 5 insert card_spend
        record.setId(idWorker.nextId());
        record.setTenantId(cardInfo.getTenantId());
        record.setPayment(payment);
        record.setRealPayment(real_payment);
        record.setCardBeforeAmount(cardInfo.getRemainingAmount());
        record.setCardRemainAmount(remaingAmount);
        record.setCardBeforeRealAmount(cardInfo.getRemainingShareFeeAmount());
        record.setCardRemainRealAmount(remain_real_pament);
        record.setPayStatus(EnumCardSpendStatus.PREV_PAY.getCode());
        record.setCreateDate(curDate);
        record.setUpdateDate(curDate);
        try {
            wxCardSpendMapper.insert(record);
        } catch (Exception e) {
            logger.error("card spend insert error");
            throw new MallinkException(ErrorCode.DB_FAIL.getCode(), "卡消费插入出错");
        }

        // 7. update card info
        cardInfo.setRemainingAmount(remaingAmount);
        cardInfo.setRemainingShareFeeAmount(remain_real_pament);
        cardInfo.setUpdateDate(curDate);
        // 7.1 消费后，修改卡转赠状态
        if (cardInfo.getSupportTransfer().equals(EnumCouponTransfer.YES.getCode())) {
            cardInfo.setSupportTransfer(EnumCouponTransfer.NO.getCode());
        }
        try {
            wxCardInfoMapper.updateById(cardInfo);
        } catch (Exception e) {
            String errMessage = "消费卡信息更新出错: " + cardInfo.getId();
            logger.error(errMessage);
            throw new MallinkException(ErrorCode.DB_FAIL.getCode(), errMessage);
        }
        return record;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class})
    public WxCardInfo cardSpendForPosPrePayCancel(WxCardInfo cardInfo, WxCardSpend record) throws MallinkException {
        Date curDate = new Date();
        if (record.getId() == null) {
            throw new MallinkException(ErrorCode.SYS_PARAMETER_NOT_NULL);
        }
        if (!record.getPayStatus().equals(EnumCardSpendStatus.PREV_PAY.getCode())) {
            logger.error("card spend not prev pay");
            throw new MallinkException(ErrorCode.CARD_SPEND_IS_NOT_PREV);
        }
        // 2. coupon
        Integer subsidyRate = 0;
        WxCoupon coupon = wxCouponMapper.selectById(cardInfo.getCouponId());
        if (coupon == null) {
            logger.error("card's coupon not found: " + cardInfo.getCouponId());
            throw new MallinkException(ErrorCode.CARD_IS_NOT_FOUND);
        }

        // 3. get pay account
        WxPayAccount payAccount = null;
        WxPayAccount payAccountQ = new WxPayAccount();
        payAccountQ.setTenantId(record.getTenantId());
        try {
            payAccount = payAccountMapper.selectOne(new QueryWrapper(payAccountQ));
        } catch (Exception e) {
            logger.error("获取payAccount error: " + record.getTenantId());
            throw new MallinkException(ErrorCode.SYS_MCH_NOT_FOUND);
        }

        // 4.  增加计算
        Double paymentD = 1.0 * cardInfo.getSaleAmount() * record.getDeductionAmount() / cardInfo.getAmount();
        Integer payment = paymentD.intValue();
        int iChargeFee = PayUtils.getPayRate(payment, payAccount.getRate(), false);
        Integer real_payment = payment - iChargeFee;
        Integer remain_real_pament = cardInfo.getRemainingShareFeeAmount() + real_payment;
        Integer remaingAmount = cardInfo.getRemainingAmount() + record.getDeductionAmount();

        // 5.、update card info
        WxCardInfo updateCardInfo = new WxCardInfo();
        updateCardInfo.setId(record.getCardId());
        updateCardInfo.setRemainingAmount(remaingAmount);
        updateCardInfo.setRemainingShareFeeAmount(remain_real_pament);
        updateCardInfo.setUpdateDate(curDate);
        // 5.1 如果还是原价, 卡可转增，且当前为不可转增，修改为可转增
        if (cardInfo.getAmount().equals(remaingAmount) &&
                cardInfo.getSupportTransfer().equals(EnumCouponTransfer.NO.getCode()) &&
                coupon.getSupportTransfer().equals(EnumCouponTransfer.YES.getCode())) {
            updateCardInfo.setSupportTransfer(EnumCouponTransfer.YES.getCode());
        }
        try {
            wxCardInfoMapper.updateById(updateCardInfo);
        } catch (Exception e) {
            String errMessage = "卡信息更新出错";
            logger.error(errMessage);
            throw new MallinkException(ErrorCode.DB_FAIL.getCode(), errMessage);
        }

        // 100. delete prev card_spend
        record.setTenantId(cardInfo.getTenantId());
        try {
            wxCardSpendMapper.deleteForPrevCancel(record);
        } catch (Exception e) {
            String errMessage = "卡消费信息删除出错";
            logger.error(errMessage);
            throw new MallinkException(ErrorCode.DB_FAIL.getCode(), errMessage);
        }

        return updateCardInfo;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class})
    public WxCardSpend cardSpendForPosPay(PromotionCalc scoreCreditCalc, WxCardSpend record, WxMerchant merchant, WxMerchantBUser buUser) throws MallinkException {
        final IdWorker idWorker = IdWorker.get();
        Date curDate = new Date();
        // 1. update card_spend
        WxCardSpend updateCardSpend = new WxCardSpend();
        updateCardSpend.setId(record.getId());
        updateCardSpend.setOrderId(record.getOrderId());
        updateCardSpend.setPayStatus(EnumCardSpendStatus.NOT_PAY.getCode());
        updateCardSpend.setUpdateDate(curDate);
        try {
            wxCardSpendMapper.updateById(updateCardSpend);
        } catch (Exception e) {
            logger.error("card spend update error: " + e.getMessage());
            throw new MallinkException(ErrorCode.DB_FAIL.getCode(), "card spend 插入出错！");
        }

        // 2. update coupon card status
        WxCardInfo cardInfo = wxCardInfoMapper.selectById(record.getCardId());
        if (cardInfo == null) {
            logger.error("card not found: " + record.getCardId());
            throw new MallinkException(ErrorCode.CARD_IS_NOT_FOUND);
        }
        if (cardInfo.getRemainingAmount().equals(0)) {
            if (cardFinished(curDate, cardInfo)) {
                throw new MallinkException(ErrorCode.DB_FAIL.getCode(), "couponOrder status 更新出错！");
            }
        }

        // 2. coupon
        Integer subsidyRate = 0;
        WxCoupon coupon = wxCouponMapper.selectById(cardInfo.getCouponId());
        if (coupon == null) {
            logger.error("card's coupon not found: " + cardInfo.getCouponId());
            throw new MallinkException(ErrorCode.CARD_IS_NOT_FOUND);
        }

        // 3. get pay account
        WxPayAccount payAccount = null;
        WxPayAccount payAccountQ = new WxPayAccount();
        payAccountQ.setTenantId(record.getTenantId());
        try {
            payAccount = payAccountMapper.selectOne(new QueryWrapper(payAccountQ));
        } catch (Exception e) {
            logger.error("获取payAccount error: " + record.getTenantId());
            throw new MallinkException(ErrorCode.DB_FAIL.getCode(), "获取payAccount失败");
        }

        // 3. 补贴
        // 补贴额 = 商城补贴总额/卡券面额 * 抵扣额(本次支付额)
        Double subsidyFeeD = 1.0 * record.getDeductionAmount() * coupon.getSubsidyNum() / cardInfo.getAmount();
        Integer subsidyFee = subsidyFeeD.intValue();
        logger.info("补贴额: " + subsidyFee);
        if (subsidyFee > 0) {
            // 有补贴时入库
            int iSubChargeFee = PayUtils.getPayRate(subsidyFee, payAccount.getRate(), false);
            Integer realSubsidyFee = subsidyFee - iSubChargeFee;
            WxMerchantSubsidy merchantSubsidy = new WxMerchantSubsidy();
            merchantSubsidy.setId(idWorker.nextId());
            merchantSubsidy.setTenantId(record.getTenantId());
            merchantSubsidy.setOrderId(record.getOrderId());
            merchantSubsidy.setOrderType(EnumOrderType.PREPAIDCARD.getCode());
            merchantSubsidy.setMerchantId(record.getMerchantId());
            merchantSubsidy.setCouponOrderId(cardInfo.getId());
            merchantSubsidy.setCouponType(EnumCouponType.CARD_MULTIMCH.getCode());
            merchantSubsidy.setOrderPayment(record.getDeductionAmount());
            merchantSubsidy.setReceiverPayment(record.getPayment());
            merchantSubsidy.setRealPayment(record.getRealPayment());
            merchantSubsidy.setSubsidy(subsidyFee);
            merchantSubsidy.setRealSubsidy(realSubsidyFee);
            if (coupon.getSubsidyType().equals(EnumCouponSubsidyType.WECHAT_COUPON.getCode())) {
                // 立减领券
                merchantSubsidy.setStatus(EnumMerchantSubsidyStatus.AUTO_SUBSIDIED.getCode());
            } else if (coupon.getSubsidyType().equals(EnumCouponSubsidyType.WECHAT_MCHPAY.getCode())) {
                // 转账倒现金
            } else {
                // 未补贴
                merchantSubsidy.setStatus(EnumMerchantSubsidyStatus.NOT_SUBSIDY.getCode());
            }

            merchantSubsidy.setCreateDate(curDate);
            merchantSubsidy.setUpdateDate(curDate);
            try {
                wxMerchantSubsidyMapper.insert(merchantSubsidy);
            } catch (Exception e) {
                logger.error("merchantSubsidy info insert error");
                throw new MallinkException(ErrorCode.DB_FAIL.getCode(), "merchantSubsidy info 插入出错！");
            }
        }

        // 9. 分账
        try {
            shareForCardPay(record.getTenantId(), record.getCardId(), record.getOrderId(), record.getId());
        } catch (MallinkException e) {
            logger.error(e.getMessage());
            if (ErrorCode.PROFIT_SHARING_NUM_UP_LIMIT.getCode() == e.getErrorCode()) {
                // 分账已到20次限制，后面只记录，无法分账
                logger.error("分账已到20次限制，后面只记录，无法分账");
            } else {
                throw new MallinkException(e.getErrorCode(), e.getMessage());
            }
        }

        // 成长值
        try {
            WxOrder orderUpdate = new WxOrder();
            orderUpdate.setId(record.getOrderId());
            orderUpdate.setTenantId(record.getTenantId());
            orderUpdate.setCUserId(record.getOwnerId());
            orderUpdate.setPayment(record.getRealPayment());
            orderUpdate.setPaymentTime(record.getUpdateDate());
            orderUpdate.setPaymentType(EnumPayType.PAY_PAYMENT.getCode());
            int point = wxScoreRulesService.addScore2(EnumScoreType.CONSUMPTION, orderUpdate, coupon.getBusiness());
            scoreCreditCalc.setScore(scoreCreditCalc.getScore() + point);
        } catch (Exception e) {
            logger.error("成长值:" + e.getMessage());
        }

        // 积分
        try {
            //-------此处为【现金支付】记录增加积分操作-------
            WxCreditHistory creditHistory = new WxCreditHistory();
            creditHistory.setOperatorType(EnumUserType.BUSER.getCode());
            creditHistory.setOperatorId(buUser.getId());
            creditHistory.setCUserId(record.getOwnerId());
            creditHistory.setCreateDate(new Date());
            creditHistory.setTenantId(record.getTenantId());
            creditHistory.setCreditType(EnumScoreType.CONSUMPTION.getCode());
            creditHistory.setBusinessId(merchant.getBusinessId());
            creditHistory.setSpend(record.getRealPayment());
            //如果券与商户一对一 则直接将消费商户更新为此商户 若一对多 则消费商户显示多商户
            creditHistory.setMerchantId(record.getMerchantId());
            creditHistory = wxCreditHistoryService.saveOrUpdate(creditHistory);
            if (creditHistory != null) {
                scoreCreditCalc.setCredit(scoreCreditCalc.getCredit() + creditHistory.getCreditNum());
            }
        } catch (Exception e) {
            logger.error("积分值:" + e.getMessage());
        }
        return record;
    }
}
