package cn.iocoder.yudao.module.pay.service.divide;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.pay.core.client.PayClient;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.enums.divide.PayDivideRefundStatusRespEnum;
import cn.iocoder.yudao.framework.pay.core.enums.divide.PayDivideStatusRespEnum;
import cn.iocoder.yudao.module.member.api.card.CardApi;
import cn.iocoder.yudao.module.pay.controller.admin.divide.vo.DividePageReqVO;
import cn.iocoder.yudao.module.pay.controller.admin.divide.vo.DivideSaveReqVO;
import cn.iocoder.yudao.module.pay.controller.app.divide.vo.DrawMoneyVO;
import cn.iocoder.yudao.module.pay.dal.dataobject.card.DivideCardDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.divide.DivideDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.dividecompany.DivideCompanyDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.divideinfo.DivideInfoDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.dividerecord.DivideRecordDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.refundrecord.RefundRecordDO;
import cn.iocoder.yudao.module.pay.dal.mysql.card.DivideCardMapper;
import cn.iocoder.yudao.module.pay.dal.mysql.divide.DivideMapper;
import cn.iocoder.yudao.module.pay.dal.mysql.dividerecord.DivideRecordMapper;
import cn.iocoder.yudao.module.pay.dal.mysql.refundrecord.RefundRecordMapper;
import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO;
import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties;
import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
import cn.iocoder.yudao.module.pay.service.dividecompany.DivideCompanyService;
import cn.iocoder.yudao.module.pay.service.divideinfo.DivideInfoService;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.binarywang.wxpay.bean.profitsharing.request.ProfitSharingV3Request;
import com.github.binarywang.wxpay.bean.profitsharing.result.ProfitSharingV3Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;

/**
 * 分账单 Service 实现类
 *
 * @author 管理员
 */
@Service
@Validated
@Slf4j
public class DivideServiceImpl implements DivideService {

    @Resource
    private DivideMapper divideMapper;

    @Resource
    private DivideInfoService divideInfoService;

    @Resource
    private DivideCompanyService divideCompanyService;

    @Resource
    private PayChannelService channelService;

    @Resource
    private PayNoRedisDAO noRedisDAO;

    @Resource
    private PayProperties payProperties;
    @Resource
    private DivideRecordMapper divideRecordMapper;

    @Resource
    private RefundRecordMapper refundRecordMapper;

    @Resource
    private DivideCardMapper divideCardMapper;

    @Resource
    private StringRedisTemplate divideRedisTemplate;

    @Resource
    private CardApi cardApi;


    @Override
    public Long createDivide(DivideSaveReqVO createReqVO) {
        // 插入
        DivideDO divide = BeanUtils.toBean(createReqVO, DivideDO.class);
        divideMapper.insert(divide);
        // 返回
        return divide.getId();
    }

    @Override
    public void updateDivide(DivideSaveReqVO updateReqVO) {
        // 校验存在
        validateDivideExists(updateReqVO.getId());
        // 更新
        DivideDO updateObj = BeanUtils.toBean(updateReqVO, DivideDO.class);
        divideMapper.updateById(updateObj);
    }

    @Override
    public void deleteDivide(Long id) {
        // 校验存在
        validateDivideExists(id);
        // 删除
        divideMapper.deleteById(id);
    }

    private void validateDivideExists(Long id) {
        if (divideMapper.selectById(id) == null) {
            throw exception(DIVIDE_NOT_EXISTS);
        }
    }

    @Override
    public DivideDO getDivide(Long id) {
        return divideMapper.selectById(id);
    }

    @Override
    public PageResult<DivideDO> getDividePage(DividePageReqVO pageReqVO) {
        return divideMapper.selectPage(pageReqVO);
    }


    public List<ProfitSharingV3Request.Receiver> createDivideInfo(DivideDO divideDO) {

        List<ProfitSharingV3Request.Receiver> receivers = new ArrayList<>();

        List<DivideCompanyDO> companyList = divideCompanyService.getList();

        List<DivideInfoDO> infoDOList = new ArrayList<>();

        for (DivideCompanyDO divideCompanyDO : companyList) {
            DivideInfoDO divideInfoDO = new DivideInfoDO();
            int money = (int) Math.ceil(divideDO.getUnPrice() * Double.valueOf(divideCompanyDO.getProportion()));
            divideInfoDO.setTransactionId(divideDO.getChannelOrderNo())
                    .setOutOrderNo(divideDO.getNo())
                    .setName(divideCompanyDO.getName())
                    .setType(divideCompanyDO.getType())
                    .setAccount(divideCompanyDO.getAccount())
                    .setAmount(money)
                    .setRefundAmount(money)
                    .setComputeAmount(money)
                    .setDescription("给" + divideCompanyDO.getName() + "分账");
            infoDOList.add(divideInfoDO);

            ProfitSharingV3Request.Receiver receiver = new ProfitSharingV3Request.Receiver();
            BeanUtil.copyProperties(divideInfoDO, receiver);
            receivers.add(receiver);
        }
        divideInfoService.insertList(infoDOList);

        return receivers;
    }


    @Override
    public int startDivide() {

        BigDecimal today = divideMapper.getToday();
        if (today == null) {
            return 0;
        }

        //转换单位  元-->分
        int money = today.multiply(new BigDecimal("100")).intValue();

        //去除当天退款
        BigDecimal refundToday = divideMapper.getRefund();
        if (refundToday == null) {
            refundToday = BigDecimal.ZERO;
        }
        int refund = refundToday.multiply(new BigDecimal("100")).intValue();
        money = money - refund;

        //查询29天内的充值订单，原因：微信30天自动解冻，订单将不能分账
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime thirtyDaysAgo = now.minusDays(29);
        List<DivideDO> divideDOS = divideMapper.selectList(Wrappers.<DivideDO>lambdaQuery()
                .eq(DivideDO::getStatus, PayDivideStatusRespEnum.WAITING.getStatus())
                .eq(DivideDO::getUnfreezeStatus, "0")
                .ge(DivideDO::getCreateTime, thirtyDaysAgo)
                .orderByAsc(DivideDO::getCreateTime));
        if (CollectionUtil.isEmpty(divideDOS)) {
            return 0;
        }

        List<DivideDO> divideList = new ArrayList<>();

        int sum = 0;
        for (DivideDO divideDO : divideDOS) {
            Integer totalPrice = divideDO.getTotalPrice() - divideDO.getCpPrice();

            if (totalPrice == 0) {
                continue;
            }


            sum = sum + totalPrice;
            if (sum < money) {
                divideDO.setUnPrice(totalPrice);
                divideDO.setCpPrice(divideDO.getCpPrice() + totalPrice);
                divideDO.setPsPrice(divideDO.getPsPrice() + totalPrice);
                divideList.add(divideDO);
            } else if (sum == money) {
                divideDO.setUnPrice(totalPrice);
                divideDO.setCpPrice(divideDO.getCpPrice() + totalPrice);
                divideDO.setPsPrice(divideDO.getPsPrice() + totalPrice);
                divideList.add(divideDO);
                break;
            } else {
                int leftMoney = money + totalPrice - sum;
                divideDO.setUnPrice(leftMoney);
                divideDO.setCpPrice(divideDO.getCpPrice() + leftMoney);
                divideDO.setPsPrice(divideDO.getPsPrice() + leftMoney);
                divideList.add(divideDO);
                break;
            }
        }
        List<DivideRecordDO> divideRecordDOList = new ArrayList<>();
        int counter = 0;
        for (DivideDO divideDO : divideList) {
            PayClient client = channelService.getPayClient(divideDO.getChannelId());
            PayDivideUnifiedDto payDivideUnifiedDto = new PayDivideUnifiedDto();

            payDivideUnifiedDto.setTransactionId(divideDO.getChannelOrderNo())
                    .setOutOrderNo(divideDO.getNo())
                    .setUnfreezeUnsplit(false)
                    .setReceivers(createDivideInfo(divideDO));

            if (divideDO.getTotalPrice().equals(divideDO.getCpPrice())) {
                payDivideUnifiedDto.setUnfreezeUnsplit(true);
                divideDO.setStatus(PayDivideStatusRespEnum.PROGRESS.getStatus());
            }
            PayDivideRespDto payDivideRespDto = client.unifiedDivide(payDivideUnifiedDto);

            DivideRecordDO divideRecordDO = new DivideRecordDO();
            divideRecordDO.setTransactionId(divideDO.getChannelOrderNo())
                    .setOutOrderNo(divideDO.getNo());
            divideRecordDOList.add(divideRecordDO);


            divideDO.setOrderId(payDivideRespDto.getOrderId());

            // 每执行250次，休息一分钟
            if (++counter % 250 == 0) {
                try {
                    TimeUnit.MINUTES.sleep(1);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    e.printStackTrace();
                }
            }
        }
        if (CollectionUtil.isNotEmpty(divideRecordDOList)) {
            divideMapper.updateBatch(divideDOS);
            divideRecordMapper.insertBatch(divideRecordDOList);
        }
        return divideRecordDOList.size();

    }

    @Override
    public int getDivideResult() {
        List<DivideRecordDO> divideRecordDOList = divideRecordMapper.getList();

        if (CollectionUtil.isEmpty(divideRecordDOList)) {
            return 0;
        }

        List<String> transactionIdList = divideRecordDOList.stream().map(DivideRecordDO::getTransactionId).collect(Collectors.toList());
        List<String> OutOrderNoList = divideRecordDOList.stream().map(DivideRecordDO::getOutOrderNo).collect(Collectors.toList());

        List<DivideDO> divideDOS = divideMapper.selectList(Wrappers.<DivideDO>lambdaQuery()
                .in(DivideDO::getChannelOrderNo, transactionIdList)
                .in(DivideDO::getNo, OutOrderNoList));

        if (CollectionUtil.isEmpty(divideDOS)) {
            return 0;
        }

        for (DivideDO divideDO : divideDOS) {
            PayClient client = channelService.getPayClient(divideDO.getChannelId());
            PayDivideUnifiedDto payDivideUnifiedDto = new PayDivideUnifiedDto();

            payDivideUnifiedDto.setTransactionId(divideDO.getChannelOrderNo())
                    .setOutOrderNo(divideDO.getNo());
            PayDivideRespDto payDivideRespDto = client.unifiedDivideResult(payDivideUnifiedDto);
            updateDivideInfo(payDivideRespDto.getReceivers(), divideDO.getNo(), payDivideRespDto.getOrderId());

            if (divideDO.getTotalPrice().equals(divideDO.getCpPrice())) {
                divideDO.setStatus(PayDivideStatusRespEnum.COMPLETE.getStatus());
            } else {
                divideDO.setNo(noRedisDAO.generate(payProperties.getOrderNoPrefix()));
            }
        }
        divideMapper.updateBatch(divideDOS);
        return divideDOS.size();
    }

    void updateDivideInfo(List<ProfitSharingV3Result.Receiver> receivers, String outOrderNo, String orderId) {
        List<DivideCompanyDO> list1 = divideCompanyService.getList();
        List<String> accountList = list1.stream().map(DivideCompanyDO::getAccount).collect(Collectors.toList());

        List<DivideInfoDO> list = new ArrayList<>();
        for (ProfitSharingV3Result.Receiver receiver : receivers) {
            if (!accountList.contains(receiver.getAccount())) {
                continue;
            }

            DivideInfoDO divideInfo = divideInfoService.getDivideInfo(outOrderNo, receiver.getAccount());

            divideInfo.setResult(receiver.getResult())
                    .setFailReason(receiver.getFailReason())
                    .setDetailId(receiver.getDetailId())
                    .setDCreateTime(receiver.getCreateTime())
                    .setDFinishTime(receiver.getFinishTime())
                    .setOrderId(orderId);
            list.add(divideInfo);
        }
        divideInfoService.updateList(list);
    }

    @Override
    public int getDivideFreeze() {
        List<DivideDO> divideDOS = divideMapper.selectList(Wrappers.<DivideDO>lambdaQuery()
                .eq(DivideDO::getStatus, PayDivideStatusRespEnum.COMPLETE.getStatus()));
        if (CollectionUtil.isEmpty(divideDOS)) {
            return 0;
        }

        for (DivideDO divideDO : divideDOS) {
            PayClient client = channelService.getPayClient(divideDO.getChannelId());
            PayDivideUnifiedDto payDivideUnifiedDto = new PayDivideUnifiedDto();
            if (checkSuccess(divideDO.getNo())) {
                payDivideUnifiedDto.setTransactionId(divideDO.getChannelOrderNo())
                        .setOutOrderNo(divideDO.getNo());
                client.unifiedDivideFreeze(payDivideUnifiedDto);
                divideDO.setStatus(PayDivideStatusRespEnum.FINISH.getStatus());
            }
        }
        divideMapper.updateBatch(divideDOS);
        return divideDOS.size();
    }

    boolean checkSuccess(String outOrderNo) {
        List<DivideInfoDO> divideList = divideInfoService.getDivideList(outOrderNo);
        for (DivideInfoDO divideInfoDO : divideList) {
            if (!"SUCCESS".equals(divideInfoDO.getResult())) {
                return false;
            }
        }
        return true;
    }

    @Override
    public Boolean drawMoney(DrawMoneyVO drawMoneyVO) {

        //判断订单是否完成
        int i = divideMapper.orderCount(drawMoneyVO.getUserId());
        if (i > 0) {
            throw new ServiceException(ORDER_NOT_COMPLETE);
        }

        //判断是否有金额可退款
        BigDecimal wxAmount = divideMapper.getWxAmount(drawMoneyVO.getUserId());
        if (wxAmount == null) {
            throw new ServiceException(WX_ACCOUNT_NO);
        }

        //判断是否有金额可退款

        int money = wxAmount.multiply(new BigDecimal("100")).intValue();
        if (money <= 0) {
            throw new ServiceException(WX_ACCOUNT_NO);
        }
        //指定金额
        if (drawMoneyVO.getTotalPrice() != null) {
            if (money < drawMoneyVO.getTotalPrice()) {
                throw new ServiceException(WX_ACCOUNT_NO);
            }
            money = drawMoneyVO.getTotalPrice();
        }
        LocalDateTime now = LocalDateTime.now(); // 获取当前时间
        LocalDateTime threeYearsAgo = now.minusDays(365);

        //查询最近的充值订单，时间倒序退款
        List<DivideDO> divideDOS = divideMapper.selectList(Wrappers.<DivideDO>lambdaQuery()
                .eq(DivideDO::getChannelUserId, drawMoneyVO.getChannelUserId())
                .eq(DivideDO::getRefundStatus, "0")
                .ge(DivideDO::getCreateTime, threeYearsAgo)
                .orderByDesc(DivideDO::getCreateTime));

        //筛选出未分账的订单
        List<DivideDO> unDivideList = divideDOS.stream().filter(vo -> vo.getPsPrice() == 0).collect(Collectors.toList());

        //筛选出已分账的订单
        List<DivideDO> divideCpList = divideDOS.stream().filter(vo -> vo.getPsPrice() != 0).collect(Collectors.toList());

        //筛选出分账完的订单
        List<DivideDO> divideList = divideCpList.stream().filter(vo -> vo.getTotalPrice().equals(vo.getCpPrice())).collect(Collectors.toList());

        //剩下的是未分账完 的订单
        divideCpList.removeAll(divideList);

        //需要回退的订单
        List<DivideDO> backList = new ArrayList<>();
        //不需要回退的订单
        List<DivideDO> noBackList = new ArrayList<>();

        int sum = 0;

        //先从未分账的订单计算

        for (DivideDO divideDO : unDivideList) {
            Integer totalPrice = divideDO.getTotalPrice() - divideDO.getCpPrice();
            sum = sum + totalPrice;

            if (sum < money) {
                divideDO.setUnRePrice(totalPrice);
                divideDO.setRefundPrice(totalPrice + divideDO.getCpPrice());
                divideDO.setCpPrice(divideDO.getCpPrice() + totalPrice);
                noBackList.add(divideDO);
            } else if (sum == money) {
                divideDO.setUnRePrice(totalPrice);
                divideDO.setRefundPrice(totalPrice + divideDO.getCpPrice());
                divideDO.setCpPrice(divideDO.getCpPrice() + totalPrice);
                noBackList.add(divideDO);
                break;
            } else {
                int leftMoney = money + totalPrice - sum;
                divideDO.setUnRePrice(leftMoney);
                divideDO.setRefundPrice(divideDO.getCpPrice() + leftMoney);
                divideDO.setCpPrice(divideDO.getCpPrice() + leftMoney);
                noBackList.add(divideDO);
                break;
            }
        }
        //从分账完的订单进行退款
        if (sum < money) {
            for (DivideDO divideDO : divideList) {
                Integer totalPrice = divideDO.getTotalPrice() - divideDO.getRefundPrice();

                sum = sum + totalPrice;

                if (sum < money) {
                    divideDO.setUnRePrice(totalPrice);
                    divideDO.setRefundPrice(divideDO.getTotalPrice());
                    divideDO.setBackPrice(totalPrice);
                    divideDO.setPsPrice(0);
                    backList.add(divideDO);
                } else if (sum == money) {
                    divideDO.setUnRePrice(totalPrice);
                    divideDO.setRefundPrice(divideDO.getTotalPrice());
                    divideDO.setBackPrice(totalPrice);
                    divideDO.setPsPrice(0);
                    backList.add(divideDO);
                    break;
                } else {
                    int leftMoney = money + totalPrice - sum;
                    divideDO.setUnRePrice(leftMoney);
                    divideDO.setRefundPrice(leftMoney + divideDO.getRefundPrice());
                    divideDO.setBackPrice(leftMoney);
                    divideDO.setPsPrice(divideDO.getPsPrice() - leftMoney);
                    backList.add(divideDO);
                    break;
                }
            }
        }

        //从部分分账完的订单进行退款，由于分账的特性只会存在一条未分账完全的资金
        if (sum < money) {
            for (DivideDO divideDO : divideCpList) {
                //剩余未解冻的资金
                Integer totalPrice = divideDO.getTotalPrice() - divideDO.getCpPrice();
                sum = sum + totalPrice;
                if (sum < money) {
                    if ("0".equals(divideDO.getUnfreezeStatus())) {
                        //先解冻剩余资金
                        String unFreeNo = noRedisDAO.generate(payProperties.getOrderNoPrefix());
                        PayClient client = channelService.getPayClient(divideDO.getChannelId());
                        PayDivideUnifiedDto payDivideUnifiedDto = new PayDivideUnifiedDto();
                        payDivideUnifiedDto.setTransactionId(divideDO.getChannelOrderNo())
                                .setOutOrderNo(unFreeNo);
                        client.unifiedDivideFreeze(payDivideUnifiedDto);
                        DivideDO unfree = new DivideDO();
                        unfree.setUnfreezeStatus("1").setId(divideDO.getId());
                        divideMapper.updateById(unfree);

                        String state = "";
                        do {
                            try {
                                //等待解冻成功
                                TimeUnit.SECONDS.sleep(15);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                            PayDivideUnifiedDto resultDto = new PayDivideUnifiedDto();
                            resultDto.setTransactionId(divideDO.getChannelOrderNo())
                                    .setOutOrderNo(unFreeNo);
                            PayDivideRespDto payDivideRespDto = client.unifiedDivideResult(resultDto);

                            state = payDivideRespDto.getState();
                        } while ("PROCESSING".equals(state));

                    }
                    //计算退款资金
                    //可用于退款的资金
                    Integer psPrice = divideDO.getTotalPrice() - divideDO.getRefundPrice();

                    sum = sum - totalPrice + psPrice;
                    if (sum <= money) {
                        divideDO.setUnRePrice(psPrice);
                        divideDO.setRefundPrice(divideDO.getTotalPrice());
                        divideDO.setBackPrice(divideDO.getPsPrice());
                        divideDO.setPsPrice(0);
                    } else {
                        int left = money - sum + psPrice;
                        divideDO.setUnRePrice(left);
                        divideDO.setRefundPrice(divideDO.getRefundPrice() + left);
                        //分账回退金额
                        int back = left - totalPrice;
                        divideDO.setBackPrice(back);
                        divideDO.setPsPrice(divideDO.getPsPrice() - back);
                    }
                    backList.add(divideDO);
                    divideDO.setCpPrice(divideDO.getTotalPrice());
                } else {
                    //计算使用资金
                    Integer useMoney = sum - money;
                    divideDO.setUnRePrice(useMoney);
                    divideDO.setRefundPrice(useMoney + divideDO.getRefundPrice());
                    divideDO.setCpPrice(divideDO.getCpPrice() + useMoney);
                    noBackList.add(divideDO);
                }
                break;
            }
        }

        //退款记录
        List<RefundRecordDO> list = new ArrayList();

        int failNum = 0;
        //调用退款接口
        if (CollectionUtil.isNotEmpty(noBackList)) {
            for (DivideDO divideDO : noBackList) {
                String outRefundNo = noRedisDAO.generate(payProperties.getRefundNoPrefix());
                RefundRecordDO refundRecordDO = new RefundRecordDO();
                refundRecordDO.setOutTradeNo(divideDO.getNo());
                refundRecordDO.setOutRefundNo(outRefundNo);
                refundRecordDO.setTransactionId(divideDO.getChannelOrderNo());
                refundRecordDO.setChannelId(divideDO.getChannelId());
                refundRecordDO.setChannelUserId(divideDO.getChannelUserId());
                refundRecordDO.setChannelCode(divideDO.getChannelCode());
                refundRecordDO.setUserId(String.valueOf(drawMoneyVO.getUserId()));
                refundRecordDO.setTotalPrice(divideDO.getUnRePrice());

                PayChannelDO channel = channelService.validPayChannel(divideDO.getChannelId());

                PayClient client = channelService.getPayClient(divideDO.getChannelId());
                PayRefundUnifiedReqDTO payRefundUnifiedReqDTO = new PayRefundUnifiedReqDTO();
                payRefundUnifiedReqDTO.setTransactionId(divideDO.getChannelOrderNo())
                        .setReason("提现").setRefundPrice(divideDO.getUnRePrice())
                        .setOutRefundNo(outRefundNo).setPayPrice(divideDO.getTotalPrice())
                        .setNotifyUrl(genChannelRefundNotifyUrl(channel));
                PayRefundRespDTO payRefundRespDTO = client.unifiedRefund(payRefundUnifiedReqDTO);

                String channelRefundNo = payRefundRespDTO.getChannelRefundNo();
                refundRecordDO.setRefundId(channelRefundNo);
                if (cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum.WAITING.getStatus().equals(payRefundRespDTO.getStatus())) {
                    refundRecordDO.setStatus(PayDivideRefundStatusRespEnum.PROCESSING.getStatus());
                } else if (cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum.SUCCESS.getStatus().equals(payRefundRespDTO.getStatus())) {
                    refundRecordDO.setStatus(PayDivideRefundStatusRespEnum.SUCCESS.getStatus());
                } else {
                    failNum++;
                    refundRecordDO.setStatus(PayDivideRefundStatusRespEnum.ABNORMAL.getStatus());
                }
                refundRecordMapper.insert(refundRecordDO);
                if (divideDO.getRefundPrice().equals(divideDO.getTotalPrice())) {
                    divideDO.setRefundStatus("1");
                }
                if (divideDO.getTotalPrice().equals(divideDO.getCpPrice())) {
                    divideDO.setStatus(PayDivideStatusRespEnum.CASH.getStatus());
                }
                String jsonString = JsonUtils.toJsonString(divideDO);
                //DivideDO divideDO1 = JsonUtils.parseObject(jsonString, DivideDO.class);
                divideRedisTemplate.opsForValue().set("R" + outRefundNo, jsonString);
                //divideMapper.updateById(divideDO);

                //list.add(refundRecordDO);
            }
        }

        //调用退款接口
        if (CollectionUtil.isNotEmpty(backList)) {
            for (DivideDO divideDO : backList) {
                String outRefundNo = noRedisDAO.generate(payProperties.getRefundNoPrefix());
                RefundRecordDO refundRecordDO = new RefundRecordDO();
                refundRecordDO.setOutTradeNo(divideDO.getNo());
                refundRecordDO.setOutRefundNo(outRefundNo);
                refundRecordDO.setTransactionId(divideDO.getChannelOrderNo());
                refundRecordDO.setChannelId(divideDO.getChannelId());
                refundRecordDO.setChannelUserId(divideDO.getChannelUserId());
                refundRecordDO.setChannelCode(divideDO.getChannelCode());
                refundRecordDO.setUserId(String.valueOf(drawMoneyVO.getUserId()));
                refundRecordDO.setTotalPrice(divideDO.getUnRePrice());

                PayChannelDO channel = channelService.validPayChannel(divideDO.getChannelId());

                PayClient client = channelService.getPayClient(divideDO.getChannelId());
                PayRefundUnifiedReqDTO payRefundUnifiedReqDTO = new PayRefundUnifiedReqDTO();
                payRefundUnifiedReqDTO.setTransactionId(divideDO.getChannelOrderNo())
                        .setReason("提现").setRefundPrice(divideDO.getUnRePrice())
                        .setOutRefundNo(outRefundNo).setPayPrice(divideDO.getTotalPrice())
                        .setNotifyUrl(genChannelRefundNotifyUrl(channel));
                PayRefundRespDTO payRefundRespDTO = client.unifiedRefund(payRefundUnifiedReqDTO);

                String channelRefundNo = payRefundRespDTO.getChannelRefundNo();
                refundRecordDO.setRefundId(channelRefundNo);
                if (cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum.WAITING.getStatus().equals(payRefundRespDTO.getStatus())) {
                    refundRecordDO.setStatus(PayDivideRefundStatusRespEnum.PROCESSING.getStatus());
                } else if (cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum.SUCCESS.getStatus().equals(payRefundRespDTO.getStatus())) {
                    refundRecordDO.setStatus(PayDivideRefundStatusRespEnum.SUCCESS.getStatus());
                } else {
                    failNum++;
                    refundRecordDO.setStatus(PayDivideRefundStatusRespEnum.ABNORMAL.getStatus());
                }
                //list.add(refundRecordDO);
                refundRecordMapper.insert(refundRecordDO);
                if (divideDO.getRefundPrice().equals(divideDO.getTotalPrice())) {
                    divideDO.setRefundStatus("1");
                }
                if (divideDO.getTotalPrice().equals(divideDO.getCpPrice())) {
                    divideDO.setStatus(PayDivideStatusRespEnum.CASH.getStatus());
                }
                String jsonString = JsonUtils.toJsonString(divideDO);
                //DivideDO divideDO1 = JsonUtils.parseObject(jsonString, DivideDO.class);
                divideRedisTemplate.opsForValue().set("R" + outRefundNo, jsonString);

                //divideMapper.updateById(divideDO);
            }
        }

        //分账回退
        //if(CollectionUtil.isNotEmpty(backList)){
        //    returnMoney(backList);
        //}
        //refundRecordMapper.insertBatch(list);

        // todo：清空
        return failNum > 0 ? false : true;
    }

    private String genChannelRefundNotifyUrl(PayChannelDO channel) {
        return payProperties.getRefundNotifyUrl() + "/" + channel.getId();
    }

    @Override
    public void returnMoney(List<DivideDO> backList) {
        List<DivideCompanyDO> companyList = divideCompanyService.getList();
        Map<String, DivideCompanyDO> companyMap = companyList.stream().collect(Collectors.toMap(DivideCompanyDO::getName, vo -> vo));

        //更新可分账回退金额
        List<DivideInfoDO> updateList = new ArrayList<>();
        for (DivideDO divideDO : backList) {
            PayClient client = channelService.getPayClient(divideDO.getChannelId());
            //查询出分账的具体条数
            List<DivideInfoDO> divideListByTi = divideInfoService.getDivideListByTi(divideDO.getChannelOrderNo());

            //全部回退
            if (divideDO.getBackPrice().equals(divideDO.getTotalPrice())) {
                for (DivideInfoDO divideInfoDO : divideListByTi) {
                    updateList.add(divideInfoDO.setRefundAmount(0));
                    doReturn(client, divideInfoDO, companyMap.get(divideInfoDO.getName()).getAccount());
                }
            } else {
                Integer refundPrice = divideDO.getBackPrice();
                //算出每个公司应回退的金额
                for (DivideCompanyDO companyDO : companyList) {
                    int backmoney = (int) Math.ceil(refundPrice * Double.valueOf(companyDO.getProportion()));

                    List<DivideInfoDO> baList = divideListByTi.stream().filter(vo -> vo.getName().equals(companyDO.getName())).collect(Collectors.toList());
                    int sum = 0;
                    for (DivideInfoDO infoDO : baList) {
                        if(infoDO.getRefundAmount()==0){
                            continue;
                        }
                        sum = sum + infoDO.getRefundAmount();
                        if (sum < backmoney) {
                            doReturn(client, infoDO, companyDO.getAccount());
                            updateList.add(infoDO.setRefundAmount(0));
                        } else if (sum == backmoney) {
                            doReturn(client, infoDO, companyDO.getAccount());
                            updateList.add(infoDO.setRefundAmount(0));
                            break;
                        } else {
                            int leftRefundAmount = sum -backmoney;
                            int left = backmoney + infoDO.getAmount() - sum;
                            infoDO.setRefundAmount(left);
                            doReturn(client, infoDO, companyDO.getAccount());
                            updateList.add(infoDO.setRefundAmount(leftRefundAmount));
                            break;
                        }
                    }
                }
            }
        }
        if (CollectionUtil.isNotEmpty(updateList)) {
            divideInfoService.updateList(updateList);
        }

    }

    void doReturn(PayClient client, DivideInfoDO divideInfoDO, String account) {
        PayDivideBackUnifiedDto payDivideBackUnifiedDto = new PayDivideBackUnifiedDto();
        payDivideBackUnifiedDto.setAmount(divideInfoDO.getRefundAmount().longValue());
        payDivideBackUnifiedDto.setDescription("分账回退");
        payDivideBackUnifiedDto.setOrderId(divideInfoDO.getOrderId());
        payDivideBackUnifiedDto.setReturnMchid(account);
        payDivideBackUnifiedDto.setOutOrderNo(divideInfoDO.getOutOrderNo());
        payDivideBackUnifiedDto.setOutReturnNo(noRedisDAO.generate(payProperties.getOrderNoPrefix()));
        PayDivideBackRespDto payDivideBackRespDto = client.unifiedDivideback(payDivideBackUnifiedDto);
        log.info("订单：" + payDivideBackRespDto.getOrderId() + "回退结果：" + payDivideBackRespDto.getResult());
    }

    @Override
    public void subtractWx(Integer amount, Long userId) {
        cardApi.wxDraw(amount, userId);
    }

    @Override
    public void updateDivideById(DivideDO divideDO) {
        divideMapper.updateById(divideDO);
    }
}