
package com.tmerclub.cloud.payment.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.tmerclub.cloud.api.auth.constant.SysTypeEnum;
import com.tmerclub.cloud.api.multishop.bo.OrderChangeShopWalletAmountBO;
import com.tmerclub.cloud.api.multishop.feign.ShopDetailFeignClient;
import com.tmerclub.cloud.api.multishop.feign.ShopWalletFeignClient;
import com.tmerclub.cloud.api.multishop.feign.ShopWalletLogFeignClient;
import com.tmerclub.cloud.api.multishop.vo.ShopDetailVO;
import com.tmerclub.cloud.api.order.constant.FinanceType;
import com.tmerclub.cloud.api.order.constant.OrderStatus;
import com.tmerclub.cloud.api.order.feign.OrderFeignClient;
import com.tmerclub.cloud.api.order.feign.OrderRefundFeignClient;
import com.tmerclub.cloud.api.order.vo.*;
import com.tmerclub.cloud.api.payment.constant.PaySysType;
import com.tmerclub.cloud.api.payment.vo.AccountDetailVO;
import com.tmerclub.cloud.api.payment.vo.ShopAccountDetailVO;
import com.tmerclub.cloud.api.payment.vo.SplitRuleVO;
import com.tmerclub.cloud.api.platform.feign.ConfigFeignClient;
import com.tmerclub.cloud.api.user.bo.BuyVipNotifyBO;
import com.tmerclub.cloud.api.user.bo.RechargeNotifyBO;
import com.tmerclub.cloud.api.user.bo.UserScoreBO;
import com.tmerclub.cloud.api.user.feign.UserBalanceLogFeignClient;
import com.tmerclub.cloud.api.user.feign.UserLevelLogFeignClient;
import com.tmerclub.cloud.api.user.vo.UserPayInfoVO;
import com.tmerclub.cloud.common.constant.AllinpayConstant;
import com.tmerclub.cloud.common.constant.Constant;
import com.tmerclub.cloud.common.constant.PayType;
import com.tmerclub.cloud.common.database.dto.PageDTO;
import com.tmerclub.cloud.common.database.util.PageUtil;
import com.tmerclub.cloud.common.database.vo.PageVO;
import com.tmerclub.cloud.common.exception.LuckException;
import com.tmerclub.cloud.common.leaf.manager.SegmentManager;
import com.tmerclub.cloud.common.order.bo.EsOrderBO;
import com.tmerclub.cloud.common.order.bo.PayNotifyBO;
import com.tmerclub.cloud.common.order.vo.ShopCartOrderMergerVO;
import com.tmerclub.cloud.common.product.constant.PreSaleType;
import com.tmerclub.cloud.common.response.ResponseEnum;
import com.tmerclub.cloud.common.response.ServerResponseEntity;
import com.tmerclub.cloud.common.rocketmq.config.RocketMqConstant;
import com.tmerclub.cloud.common.security.AuthUserContext;
import com.tmerclub.cloud.common.util.Arith;
import com.tmerclub.cloud.common.util.BeanUtil;
import com.tmerclub.cloud.common.util.ExcelUtil;
import com.tmerclub.cloud.common.util.PriceUtil;
import com.tmerclub.cloud.payment.bo.PayInfoBO;
import com.tmerclub.cloud.payment.bo.PayInfoResultBO;
import com.tmerclub.cloud.payment.constant.DistributedIdKey;
import com.tmerclub.cloud.payment.constant.PayEntry;
import com.tmerclub.cloud.payment.constant.PayStatus;
import com.tmerclub.cloud.payment.dto.AccountSearchDTO;
import com.tmerclub.cloud.payment.dto.BuyVipPayInfoDTO;
import com.tmerclub.cloud.payment.dto.PayInfoDTO;
import com.tmerclub.cloud.payment.dto.RechargePayInfoDTO;
import com.tmerclub.cloud.payment.manager.AllinpayManager;
import com.tmerclub.cloud.payment.mapper.PayInfoMapper;
import com.tmerclub.cloud.payment.model.PayInfo;
import com.tmerclub.cloud.payment.service.PayInfoService;
import com.tmerclub.cloud.payment.vo.PayInfoExcelVO;
import com.tmerclub.cloud.payment.vo.PayInfoVO;
import io.seata.spring.annotation.GlobalTransactional;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 订单支付记录
 *
 * @author tmerclub
 * @date 2020-12-25 09:50:59
 */
@Service
public class PayInfoServiceImpl implements PayInfoService {
    private static final Logger log = LoggerFactory.getLogger(PayInfoServiceImpl.class);

    @Autowired
    private PayInfoMapper payInfoMapper;
    @Autowired
    private SegmentManager segmentManager;
    @Autowired
    private AllinpayManager allinpayManager;
    @Autowired
    private RocketMQTemplate userScoreTemplate;
    @Autowired
    private RocketMQTemplate orderNotifyTemplate;
    @Autowired
    private RocketMQTemplate buyVipNotifyTemplate;
    @Autowired
    private RocketMQTemplate orderSettledShopTemplate;
    @Autowired
    private RocketMQTemplate userRechargeNotifyTemplate;
    @Autowired
    private RocketMQTemplate shopRechargeNotifyTemplate;
    @Autowired
    private RocketMQTemplate shopBalancePaySuccessTemplate;
    @DubboReference
    private OrderFeignClient orderFeignClient;
    @DubboReference
    private UserBalanceLogFeignClient userBalanceLogFeignClient;
    @DubboReference
    private ShopWalletLogFeignClient shopWalletLogFeignClient;
    @DubboReference
    private UserLevelLogFeignClient userLevelLogFeignClient;
    @DubboReference
    private OrderRefundFeignClient orderRefundFeignClient;
    @DubboReference
    private ShopWalletFeignClient shopWalletFeignClient;
    @DubboReference
    private ShopDetailFeignClient shopDetailFeignClient;
    @DubboReference
    private ConfigFeignClient configFeignClient;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public PayInfoBO pay(Long userId, PayInfoDTO payParam, String bizUserId) {
        List<Long> orderIds = payParam.getOrderIds();
        ServerResponseEntity<OrderAmountVO> ordersAmountAndIfNoCancelResponse = orderFeignClient.getOrdersAmountAndIfNoCancel(orderIds,userId);
        // 如果订单已经关闭了，此时不能够支付了
        if (!ordersAmountAndIfNoCancelResponse.isSuccess()) {
            throw new LuckException(ordersAmountAndIfNoCancelResponse.getMsg());
        }
        OrderAmountVO orderAmount = ordersAmountAndIfNoCancelResponse.getData();
        // 支付单号
        Long payId = segmentManager.getSegmentIdWithDateTime(DistributedIdKey.tmerclub_PAY, userId);

        //如果金额小于0.01 支付积分大于0则为纯积分支付
        checkAmount(payParam, orderAmount);
        PayInfo payInfo = new PayInfo();
        payInfo.setPayId(payId);
        payInfo.setUserId(userId);
        payInfo.setRefundId(0L);
        payInfo.setPayStatus(PayStatus.UNPAY.value());
        payInfo.setPayAmount(orderAmount.getPayAmount());
        payInfo.setPayEntry(PayEntry.ORDER.value());
        payInfo.setPayScore(orderAmount.getPayScore());
        payInfo.setVersion(0);
        payInfo.setPayType(payParam.getPayType());
        payInfo.setSysType(AuthUserContext.get().getSysType());
        payInfo.setPaySysType(configFeignClient.getPaySysType().getData());
        payInfo.setSystemModel(payParam.getSystemModel());
        // 保存多个支付订单号
        payInfo.setOrderIds(StrUtil.join(StrUtil.COMMA, orderIds));
        payInfo.setBizUserId(bizUserId);
        // 保存预支付信息
        payInfoMapper.save(payInfo);
        PayInfoBO payInfoBo = new PayInfoBO();
        payInfoBo.setBody("商城支付订单");
        payInfoBo.setPayAmount(orderAmount.getPayAmount());
        payInfoBo.setPayId(payId);
        payInfoBo.setOrderIds(payInfo.getOrderIds());
        for (ShopAmountVO shopAmountVO : orderAmount.getShopAmountList()) {
            shopAmountVO.setBizUserId(AllinpayConstant.SHOP + shopAmountVO.getBizUserId());
        }
        payInfoBo.setReceiveList(orderAmount.getShopAmountList());
        return payInfoBo;
    }

    private static void checkAmount(PayInfoDTO payParam, OrderAmountVO orderAmount) {
        if (orderAmount.getPayAmount() < 1 && orderAmount.getPayScore() > 0 && !Objects.equals(PayType.SCOREPAY.value(), payParam.getPayType())) {
            throw new LuckException("订单金额有误，无法进行支付");
        }
        // 金额小于0.01且支付方式不为积分支付
        else if (orderAmount.getPayAmount() < 1 && !Objects.equals(PayType.SCOREPAY.value(), payParam.getPayType())) {
            throw new LuckException("订单金额有误，无法进行支付");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PayInfoBO payPreSaleBalance(Long userId, PayInfoDTO payParam) {
        // 支付单号
        Long payId = segmentManager.getSegmentIdWithDateTime(DistributedIdKey.tmerclub_PAY, userId);

        List<Long> orderIds = payParam.getOrderIds();
        ServerResponseEntity<OrderAmountVO> ordersAmountAndIfNoCancelResponse = orderFeignClient.getOrdersAmountAndCheckOrderStatus(orderIds);
        // 如果订单不是代付尾款状态，此时不能够支付了
        if (!ordersAmountAndIfNoCancelResponse.isSuccess()) {
            throw new LuckException(ordersAmountAndIfNoCancelResponse.getMsg());
        }
        OrderAmountVO orderAmount = ordersAmountAndIfNoCancelResponse.getData();
        //如果金额小于0.01 支付积分大于0则为纯积分支付
        checkAmount(payParam, orderAmount);
        PayInfo payInfo = new PayInfo();
        payInfo.setUserId(userId);
        payInfo.setRefundId(0L);
        payInfo.setPayId(payId);
        payInfo.setPayStatus(PayStatus.UNPAY.value());
        payInfo.setPayAmount(orderAmount.getPayAmount());
        payInfo.setPayScore(orderAmount.getPayScore());
        payInfo.setPayType(payParam.getPayType());
        payInfo.setSysType(AuthUserContext.get().getSysType());
        payInfo.setPayEntry(PayEntry.ORDER.value());
        payInfo.setVersion(0);
        payInfo.setSystemModel(payParam.getSystemModel());
        // 保存多个支付订单号
        payInfo.setOrderIds(StrUtil.join(StrUtil.COMMA, orderIds));

        // 保存预支付信息
        payInfoMapper.save(payInfo);
        PayInfoBO payInfoBo = new PayInfoBO();
        payInfoBo.setBody("商城支付订单");
        payInfoBo.setPayAmount(orderAmount.getPayAmount());
        payInfoBo.setPayId(payId);
        for (ShopAmountVO shopAmountVO : orderAmount.getShopAmountList()) {
            shopAmountVO.setBizUserId(AllinpayConstant.SHOP + shopAmountVO.getBizUserId());
        }
        payInfoBo.setReceiveList(orderAmount.getShopAmountList());
        return payInfoBo;
    }

    @Override
    public PayInfo getByPayId(Long payId) {
        return payInfoMapper.getByPayId(payId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void paySuccess(PayInfoResultBO payInfoResult, PayInfo temp, List<Long> orderIds, Integer paySysType, ShopCartOrderMergerVO shopCartOrderMergerVO) {
        // 标记为支付成功状态
        PayInfo payInfo = new PayInfo();
        payInfo.setPayId(payInfoResult.getPayId());
        payInfo.setBizPayNo(payInfoResult.getBizPayNo());
        payInfo.setCallbackTime(new Date());
        payInfo.setCallbackContent(payInfoResult.getCallbackContent());
        payInfo.setPayStatus(PayStatus.PAYED.value());
        payInfo.setPaySysType(paySysType);
        int updateStatus = payInfoMapper.updateToSuccess(payInfo);
        // 不能重复支付
        if (updateStatus == 0) {
            throw new LuckException("支付状态错误，更新失败");
        }
        // 发送消息，订单支付成功
        SendStatus sendStatus = orderNotifyTemplate.syncSend(RocketMqConstant.ORDER_NOTIFY_TOPIC, new GenericMessage<>(new PayNotifyBO(orderIds, Objects.requireNonNull(PayType.instance(temp.getPayType())).value(), payInfo.getPayId(), paySysType,shopCartOrderMergerVO))).getSendStatus();
        if (!Objects.equals(sendStatus, SendStatus.SEND_OK)) {
            // 消息发不出去就抛异常，因为订单回调会有多次，几乎不可能每次都无法发送出去，发的出去无所谓因为接口是幂等的
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
        UserScoreBO userScoreBo = new UserScoreBO();
        userScoreBo.setUserId(temp.getUserId());
        userScoreBo.setPayType(payInfo.getPayType());
        userScoreBo.setOrderIds(orderIds);
        userScoreBo.setUnLock(true);
        SendStatus userScoreSendStatus = userScoreTemplate.syncSend(RocketMqConstant.SCORE_UNLOCK_TOPIC, new GenericMessage<>(userScoreBo)).getSendStatus();
        if (!Objects.equals(userScoreSendStatus, SendStatus.SEND_OK)) {
            // 消息发不出去就抛异常，发的出去无所谓
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
    }

    @Override
    public Integer getPayStatusByOrderIds(String orderIds) {
        return payInfoMapper.getPayStatusByOrderIds(orderIds);
    }

    @Override
    public Integer isPay(String orderIds, Long userId, Integer payEntry, Integer payAmountType) {
        Integer sysType = AuthUserContext.get().getSysType();
        String[] orderIdList = orderIds.split(StrUtil.COMMA);
        ServerResponseEntity<EsOrderBO> orderResp = orderFeignClient.getEsOrder(Long.valueOf(orderIdList[0]));
        if (!orderResp.isSuccess()) {
            throw new LuckException(orderResp.getMsg());
        }
        if (Objects.nonNull(orderResp.getData())) {
            EsOrderBO esOrderBO = orderResp.getData();
            // 如果是定金支付，判断一下
            if (Objects.equals(esOrderBO.getPreSaleType(), PreSaleType.DEPOSIT.value())) {
                if (Objects.equals(esOrderBO.getStatus(), OrderStatus.WAIT_BALANCE.value()) && Objects.equals(payAmountType, 0)) {
                    return 1;
                }
                if (Objects.equals(esOrderBO.getStatus(), OrderStatus.PAYED.value()) && Objects.equals(payAmountType, 1)) {
                    return 1;
                }
                return 0;
            }
        }
        return payInfoMapper.isPay(orderIds, userId, sysType, payEntry);
    }

    @Override
    public void markerRefund(Long payId) {
        PayInfo payInfo = new PayInfo();
        payInfo.setPayId(payId);
        payInfo.setPayStatus(PayStatus.REFUND.value());
        payInfoMapper.update(payInfo);
    }

    @Override
    public void update(PayInfo payInfo) {
        payInfoMapper.update(payInfo);
    }

    @Override
    public List<PayInfo> listByOrderIds(List<Long> orderIds) {
        return payInfoMapper.listByOrderIds(orderIds);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PayInfoBO recharge(Long userId, RechargePayInfoDTO payParam) {

        // 支付单号
        Long payId = segmentManager.getSegmentIdWithDateTime(DistributedIdKey.tmerclub_PAY, userId);
        Long rechargeLogId;

        boolean isShopUser = false;
        // 充值信息
        ServerResponseEntity<Long> payAmountResponse;
        if (Objects.equals(AuthUserContext.get().getSysType(), SysTypeEnum.MULTISHOP.value())) {
            if (Objects.isNull(payParam.getShopWalletLogId())) {
                throw new LuckException("请求参数错误");
            }
            isShopUser = true;
            rechargeLogId = payParam.getShopWalletLogId();
            payAmountResponse = shopWalletLogFeignClient.getPayAmount(rechargeLogId);
        } else if (Objects.equals(AuthUserContext.get().getSysType(), SysTypeEnum.ORDINARY.value())) {
            if (Objects.isNull(payParam.getBalanceLogId())) {
                throw new LuckException("请求参数错误");
            }
            rechargeLogId = payParam.getBalanceLogId();
            payAmountResponse = userBalanceLogFeignClient.getPayAmount(rechargeLogId);
        } else {
            throw new LuckException("你没有权限进行此操作");
        }

        if (!payAmountResponse.isSuccess()) {
            throw new LuckException(payAmountResponse.getMsg());
        }
        Long payAmount = payAmountResponse.getData();
        if (payAmount == null || payAmount < 1) {
            throw new LuckException("充值金额有误");
        }

        PayInfo payInfo = new PayInfo();
        payInfo.setPayId(payId);
        payInfo.setUserId(userId);
        payInfo.setRefundId(0L);
        payInfo.setPayScore(0L);
        payInfo.setPayAmount(payAmount);
        payInfo.setPayStatus(PayStatus.UNPAY.value());
        payInfo.setSysType(AuthUserContext.get().getSysType());
        payInfo.setPayType(payParam.getPayType());
        payInfo.setPayEntry(PayEntry.RECHARGE.value());
        payInfo.setPaySysType(PaySysType.DEFAULT.value());
        payInfo.setVersion(0);
        // 充值id
        payInfo.setOrderIds(String.valueOf(rechargeLogId));

        // 保存预支付信息
        payInfoMapper.save(payInfo);

        PayInfoBO payInfoDto = new PayInfoBO();
        payInfoDto.setPayId(payId);
        payInfoDto.setPayAmount(payAmount);
        payInfoDto.setBody(isShopUser ? "商家余额充值订单" : "商城余额充值订单");
        payInfoDto.setIsRecharge(true);
        payInfoDto.setPaySysType(PaySysType.DEFAULT.value());
        payInfoDto.setOrderIds(String.valueOf(rechargeLogId));
        payInfoDto.setValidateType(payParam.getValidateType());
        return payInfoDto;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rechargeSuccess(PayInfoResultBO payInfoResult, PayType payType, Long rechargeLogId, Integer sysType) {
        // 标记为支付成功状态
        PayInfo payInfo = new PayInfo();
        payInfo.setPayId(payInfoResult.getPayId());
        payInfo.setBizPayNo(payInfoResult.getBizPayNo());
        payInfo.setCallbackTime(new Date());
        payInfo.setCallbackContent(payInfoResult.getCallbackContent());
        payInfo.setPayStatus(PayStatus.PAYED.value());
        int updateStatus = payInfoMapper.updateToSuccess(payInfo);
        // 不能重复支付
        if (updateStatus == 0) {
            throw new LuckException("支付状态错误，更新失败");
        }
        SendStatus sendStatus;
        // 发送消息，订单支付成功
        if (Objects.equals(sysType, SysTypeEnum.MULTISHOP.value())) {
            sendStatus = shopRechargeNotifyTemplate.syncSend(RocketMqConstant.SHOP_RECHARGE_NOTIFY_TOPIC, new GenericMessage<>(new RechargeNotifyBO(rechargeLogId, payType.value(), payInfo.getPayId()))).getSendStatus();
        } else if (Objects.equals(sysType, SysTypeEnum.ORDINARY.value())) {
            sendStatus = userRechargeNotifyTemplate.syncSend(RocketMqConstant.USER_RECHARGE_NOTIFY_TOPIC, new GenericMessage<>(new RechargeNotifyBO(rechargeLogId, payType.value(), payInfo.getPayId()))).getSendStatus();
        } else {
            return;
        }
        if (!Objects.equals(sendStatus, SendStatus.SEND_OK)) {
            // 消息发不出去就抛异常，因为订单回调会有多次，几乎不可能每次都无法发送出去，发的出去无所谓因为接口是幂等的
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void payShopBalanceSuccess(PayInfoResultBO payInfoResult, Long orderId) {
        // 标记为支付成功状态
        PayInfo payInfo = new PayInfo();
        payInfo.setPayId(payInfoResult.getPayId());
        payInfo.setPayStatus(PayStatus.PAYED.value());
        payInfo.setBizPayNo(payInfoResult.getBizPayNo());
        payInfo.setCallbackContent(payInfoResult.getCallbackContent());
        payInfo.setCallbackTime(new Date());
        int updateStatus = payInfoMapper.updateToSuccess(payInfo);
        // 不能重复支付
        if (updateStatus == 0) {
            throw new LuckException("支付状态错误，更新失败");
        }
        // 发送消息，余额支付成功，通知修改订单为已采购状态及后续操作
        SendStatus sendStatus = shopBalancePaySuccessTemplate.syncSend(RocketMqConstant.SHOP_BALANCE_PAY_SUCCESS_TOPIC, new GenericMessage<>(orderId)).getSendStatus();
        if (!Objects.equals(sendStatus, SendStatus.SEND_OK)) {
            // 消息发不出去就抛异常，因为订单回调会有多次，几乎不可能每次都无法发送出去，发的出去无所谓因为接口是幂等的
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
    }

    @Override
    public PayInfoBO vipBuy(Long userId, BuyVipPayInfoDTO payParam) {
        // 支付单号
        Long payId = segmentManager.getSegmentIdWithDateTime(DistributedIdKey.tmerclub_PAY, userId);
        Long userLevelLogId = payParam.getUserLevelLogId();

        // 充值信息
        ServerResponseEntity<Long> payAmountResponse = userLevelLogFeignClient.getPayAmount(userLevelLogId);
        if (!payAmountResponse.isSuccess()) {
            throw new LuckException(payAmountResponse.getMsg());
        }
        Long payAmount = payAmountResponse.getData();
        PayInfo payInfo = new PayInfo();
        payInfo.setPayId(payId);
        payInfo.setRefundId(0L);
        payInfo.setUserId(userId);
        payInfo.setPayScore(0L);
        payInfo.setPayAmount(payAmount);
        payInfo.setPayStatus(PayStatus.UNPAY.value());
        payInfo.setSysType(AuthUserContext.get().getSysType());
        payInfo.setPayType(payParam.getPayType());
        payInfo.setPaySysType(payParam.getPaySysType());
        payInfo.setPayEntry(PayEntry.VIP.value());
        payInfo.setVersion(0);
        // 充值id
        payInfo.setOrderIds(String.valueOf(userLevelLogId));


        // 保存预支付信息
        payInfoMapper.save(payInfo);
        PayInfoBO payInfoDto = new PayInfoBO();
        BeanUtils.copyProperties(payInfo, payInfoDto);
        payInfoDto.setBody("商城VIP订购订单");
        payInfoDto.setSystemType(payParam.getSystemType());
        payInfoDto.setValidateType(payParam.getValidateType());
        return payInfoDto;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void buyVipSuccess(PayInfoResultBO payInfoResult, PayType payType, Long userLevelLogId) {
        // 标记为支付成功状态
        PayInfo payInfo = new PayInfo();
        payInfo.setBizPayNo(payInfoResult.getBizPayNo());
        payInfo.setCallbackTime(new Date());
        payInfo.setPayId(payInfoResult.getPayId());
        payInfo.setCallbackContent(payInfoResult.getCallbackContent());
        payInfo.setPayStatus(PayStatus.PAYED.value());
        int updateStatus = payInfoMapper.updateToSuccess(payInfo);
        // 不能重复支付
        if (updateStatus == 0) {
            throw new LuckException("支付状态错误，更新失败");
        }
        // 通联余额支付，更新用户余额记录
        if (Objects.equals(PaySysType.ALLINPAY.value(), payInfoResult.getPaySysType())
                && payType.equals(PayType.BALANCE)) {
            ServerResponseEntity<Void> response = userBalanceLogFeignClient.updateToOrderPaySuccess(payInfo.getPayId());
            if (!response.isSuccess()) {
                throw new LuckException(response.getMsg());
            }
        }
        // 发送消息，订单支付成功
        SendStatus sendStatus = buyVipNotifyTemplate.syncSend(RocketMqConstant.BUY_VIP_NOTIFY_TOPIC, new GenericMessage<>(new BuyVipNotifyBO(userLevelLogId, payType.value(), payInfo.getPayId()))).getSendStatus();
        if (!Objects.equals(sendStatus, SendStatus.SEND_OK)) {
            // 消息发不出去就抛异常，因为订单回调会有多次，几乎不可能每次都无法发送出去，发的出去无所谓因为接口是幂等的
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
    }

    @Override
    public AccountDetailVO getIncomeAccountDetail(Date startTime, Date endTime, String shopName) {
        ServerResponseEntity<AccountOrderDetailVO> orderResponse = orderFeignClient.getAccountOrderDetail(startTime, endTime, shopName, null);
        if (!orderResponse.isSuccess()) {
            throw new LuckException(orderResponse.getMsg());
        }
        ServerResponseEntity<ShopAccountDetailVO> userResponse = userBalanceLogFeignClient.getPlatformAccountDetail(startTime, endTime);
        if (!userResponse.isSuccess()) {
            throw new LuckException(userResponse.getMsg());
        }
        ShopAccountDetailVO platformAccountInfo = userResponse.getData();
        AccountDetailVO accountDetailVO = BeanUtil.map(orderResponse.getData(), AccountDetailVO.class);

        if (Objects.isNull(accountDetailVO)) {
            accountDetailVO = new AccountDetailVO();

        }
        accountDetailVO.setAlipayAmount((Objects.isNull(platformAccountInfo.getAlipayAmount()) ? 0L : platformAccountInfo.getAlipayAmount()) + (Objects.isNull(accountDetailVO.getAlipayAmount()) ? 0L : accountDetailVO.getAlipayAmount()));

        accountDetailVO.setBalanceAmount((Objects.isNull(platformAccountInfo.getBalanceAmount()) ? 0L : platformAccountInfo.getBalanceAmount()) + (Objects.isNull(accountDetailVO.getBalanceAmount()) ? 0L : accountDetailVO.getBalanceAmount()));
        accountDetailVO.setWechatAmount((Objects.isNull(platformAccountInfo.getWechatAmount()) ? 0L : platformAccountInfo.getWechatAmount()) + (Objects.isNull(accountDetailVO.getWechatAmount()) ? 0L : accountDetailVO.getWechatAmount()));
//        accountDetailVO.setAlipayAmount((Objects.isNull(platformAccountInfo.getAlipayAmount()) ? 0L : platformAccountInfo.getAlipayAmount()) + (Objects.isNull(accountDetailVO.getAlipayAmount()) ? 0L : accountDetailVO.getAlipayAmount()));

        accountDetailVO.setScoreCount((Objects.isNull(platformAccountInfo.getScoreCount()) ? 0L : platformAccountInfo.getScoreCount()) + (Objects.isNull(accountDetailVO.getScoreCount()) ? 0L : accountDetailVO.getScoreCount()));

        accountDetailVO.setWechatPercent(0.00);
        accountDetailVO.setAlipayPercent(0.00);
        accountDetailVO.setBalancePercent(0.00);
        accountDetailVO.setTotal(0L);
        getTotal(accountDetailVO);
        return accountDetailVO;
    }

    @Override
    public AccountDetailVO getPlatformIncomeAccountDetail(Date startTime, Date endTime) {
        // 获取平台信息
        ServerResponseEntity<ShopAccountDetailVO> userResponse = userBalanceLogFeignClient.getPlatformAccountDetail(startTime, endTime);
        if (!userResponse.isSuccess()) {
            throw new LuckException(userResponse.getMsg());
        }
        ShopAccountDetailVO platformAccountInfo = userResponse.getData();
        ServerResponseEntity<AccountOrderDetailVO> platformOrderResponse = orderFeignClient.getAccountOrderDetail(startTime, endTime, "", Constant.PLATFORM_SHOP_ID);
        if (!platformOrderResponse.isSuccess()) {
            throw new LuckException(platformOrderResponse.getMsg());
        }
        AccountOrderDetailVO accountOrderDetailVO = platformOrderResponse.getData();
        AccountDetailVO platformAccountDetail = new AccountDetailVO();
        platformAccountDetail.setAlipayAmount((Objects.isNull(platformAccountInfo.getAlipayAmount()) ? 0L : platformAccountInfo.getAlipayAmount()) + (Objects.isNull(accountOrderDetailVO.getAlipayAmount()) ? 0L : accountOrderDetailVO.getAlipayAmount()));
        platformAccountDetail.setWechatAmount((Objects.isNull(platformAccountInfo.getWechatAmount()) ? 0L : platformAccountInfo.getWechatAmount()) + (Objects.isNull(accountOrderDetailVO.getWechatAmount()) ? 0L : accountOrderDetailVO.getWechatAmount()));
        platformAccountDetail.setBalanceAmount((Objects.isNull(platformAccountInfo.getBalanceAmount()) ? 0L : platformAccountInfo.getBalanceAmount()) + (Objects.isNull(accountOrderDetailVO.getBalanceAmount()) ? 0L : accountOrderDetailVO.getBalanceAmount()));
        platformAccountDetail.setScoreCount((Objects.isNull(platformAccountInfo.getScoreCount()) ? 0L : platformAccountInfo.getScoreCount()) + (Objects.isNull(accountOrderDetailVO.getScoreCount()) ? 0L : accountOrderDetailVO.getScoreCount()));
        platformAccountDetail.setTotal(platformAccountDetail.getAlipayAmount() + platformAccountDetail.getBalanceAmount() + platformAccountDetail.getWechatAmount());
        return platformAccountDetail;
    }

    private void getTotal(AccountDetailVO accountDetailVO) {
        Long wechatAmount = Objects.isNull(accountDetailVO.getWechatAmount()) ? 0L : accountDetailVO.getWechatAmount();
        Long balanceAmount = Objects.isNull(accountDetailVO.getBalanceAmount()) ? 0L : accountDetailVO.getBalanceAmount();
        Long alipayAmount = Objects.isNull(accountDetailVO.getAlipayAmount()) ? 0L : accountDetailVO.getAlipayAmount();
        long total = wechatAmount + alipayAmount + balanceAmount;
        accountDetailVO.setWechatPercent(Arith.div(wechatAmount.doubleValue(), total, 4));
        accountDetailVO.setAlipayPercent(Arith.div(alipayAmount.doubleValue(), total, 4));
        accountDetailVO.setBalancePercent(Arith.div(balanceAmount.doubleValue(), total, 4));
        accountDetailVO.setTotal(total);
    }

    @Override
    public PageVO<PayInfoVO> getPayInfoPage(PageDTO pageDTO, AccountSearchDTO accountSearchDTO) {
        PageVO<PayInfoVO> pageVO = new PageVO<>();
        if (Objects.equals(accountSearchDTO.getFinanceType(), FinanceType.RECHARGE.value()) || Objects.equals(accountSearchDTO.getFinanceType(), FinanceType.LEVEL.value())) {
            ServerResponseEntity<PageVO<UserPayInfoVO>> userResponse = userBalanceLogFeignClient.pageUserPayInfo(accountSearchDTO.getStartTime(), accountSearchDTO.getEndTime(), accountSearchDTO.getFinanceType(), pageDTO.getPageSize(), pageDTO.getPageNum());
            if (!userResponse.isSuccess()) {
                throw new LuckException(userResponse.getMsg());
            }
            PageVO<UserPayInfoVO> userResponseData = userResponse.getData();
            pageVO.setPages(userResponseData.getPages());
            pageVO.setTotal(userResponseData.getTotal());
            pageVO.setList(BeanUtil.mapAsList(userResponseData.getList(), PayInfoVO.class));
        } else {
            ServerResponseEntity<PageShopAccountOrderVO> orderResponse = orderFeignClient.listOrderPayInfo(accountSearchDTO.getStartTime(), accountSearchDTO.getEndTime(), accountSearchDTO.getShopId(), null, pageDTO.getPageSize(), pageDTO.getPageNum());
            if (!orderResponse.isSuccess()) {
                throw new LuckException(orderResponse.getMsg());
            }
            pageVO.setTotal(orderResponse.getData().getTotal());
            pageVO.setList(BeanUtil.mapAsList(orderResponse.getData().getPayInfoList(), PayInfoVO.class));
            pageVO.setPages(PageUtil.getPages(orderResponse.getData().getTotal(), pageDTO.getPageSize()));
        }
        if (CollectionUtils.isEmpty(pageVO.getList())) {
            return pageVO;
        }
        // 查询店铺信息
        Set<Long> shopIds = pageVO.getList().stream().map(PayInfoVO::getShopId).collect(Collectors.toSet());
        List<ShopDetailVO> shops = shopDetailFeignClient.listByShopIds(new ArrayList<>(shopIds)).getData();
        Map<Long, String> shopNameMap = new HashMap<>(Constant.INITIAL_CAPACITY);
        if (!CollectionUtils.isEmpty(shops)) {
            shopNameMap = shops.stream().collect(Collectors.toMap(ShopDetailVO::getShopId, ShopDetailVO::getShopName));
        }
        // 补充信息
        String shopName;
        for (PayInfoVO payInfoVO : pageVO.getList()) {
            if (!ObjectUtils.isEmpty(shopName = shopNameMap.get(payInfoVO.getShopId()))) {
                payInfoVO.setShopName(shopName);
            }
        }
        return pageVO;
    }

    @Override
    public void excelPayInfo(HttpServletResponse response, AccountSearchDTO accountSearchDTO) {
        accountSearchDTO.setFinanceType(FinanceType.ORDER.value());
        ServerResponseEntity<PageShopAccountOrderVO> orderResponse = orderFeignClient.listOrderPayInfo(accountSearchDTO.getStartTime(), accountSearchDTO.getEndTime(), null, accountSearchDTO.getShopName(), null, null);
        if (!orderResponse.isSuccess()) {
            throw new LuckException(orderResponse.getMsg());
        }
        List<PayInfoVO> orderPayInfoList = BeanUtil.mapAsList(orderResponse.getData().getPayInfoList(), PayInfoVO.class);
        accountSearchDTO.setFinanceType(FinanceType.RECHARGE.value());
        ServerResponseEntity<List<UserPayInfoVO>> balanceResponse = userBalanceLogFeignClient.listUserPayInfo(accountSearchDTO.getStartTime(), accountSearchDTO.getEndTime(), accountSearchDTO.getFinanceType());
        if (!balanceResponse.isSuccess()) {
            throw new LuckException(balanceResponse.getMsg());
        }
        accountSearchDTO.setFinanceType(FinanceType.LEVEL.value());
        ServerResponseEntity<List<UserPayInfoVO>> levelResponse = userBalanceLogFeignClient.listUserPayInfo(accountSearchDTO.getStartTime(), accountSearchDTO.getEndTime(), accountSearchDTO.getFinanceType());
        if (!levelResponse.isSuccess()) {
            throw new LuckException(levelResponse.getMsg());
        }
        List<PayInfoVO> rechargePayInfoList = BeanUtil.mapAsList(balanceResponse.getData(), PayInfoVO.class);
        List<PayInfoVO> levelPayInfoList = BeanUtil.mapAsList(levelResponse.getData(), PayInfoVO.class);

        Map<Integer, String[]> map = new HashMap<>(8);
        ExcelWriter excelWriter = null;
        try {
            // 先执行合并策略
            ExcelWriterBuilder excelWriterMerge = ExcelUtil.getExcelWriterMerge(response, PayInfoExcelVO.EXCEL_NAME, PayInfoExcelVO.MERGE_ROW_INDEX, PayInfoExcelVO.MERGE_COLUMN_INDEX);
            excelWriter = ExcelUtil.getExcelModel(excelWriterMerge, map, Constant.START_ROW).build();
            // 业务代码
            writeExcel(orderPayInfoList, FinanceType.ORDER.str(), excelWriter, FinanceType.ORDER.value());
            // 余额充值
            writeExcel(rechargePayInfoList, FinanceType.RECHARGE.str(), excelWriter, FinanceType.RECHARGE.value());
            // 会员购买
            writeExcel(levelPayInfoList, FinanceType.LEVEL.str(), excelWriter, FinanceType.LEVEL.value());
        } catch (Exception e) {
            log.error("导出财务对账收入报表出错", e);
        } finally {
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }

    private Set<String> getExcelColumn(Integer financeType) {
        Set<String> column = new HashSet<>(Constant.INITIAL_CAPACITY);
        if (Objects.equals(financeType, FinanceType.RECHARGE.value())) {
            // 余额充值没有支付积分和余额金额和店铺名称
            column.add("payScore");
            column.add("balanceAmount");
            column.add("shopName");
        }
        if (Objects.equals(financeType, FinanceType.LEVEL.value())) {
            // 会员购买没有支付积分和店铺名称
            column.add("payScore");
            column.add("shopName");
        }
        return column;
    }

    private void writeExcel(List<PayInfoVO> payInfoList, String type, ExcelWriter excelWriter, Integer financeType) {
        Set<String> excelColumn = getExcelColumn(financeType);
        List<PayInfoExcelVO> payInfoExcelList = getExcelList(payInfoList);
        if (CollUtil.isNotEmpty(payInfoExcelList)) {
            payInfoExcelList.add(new PayInfoExcelVO());
        }
        WriteSheet sheetWriter = EasyExcel.writerSheet(type).sheetNo(financeType).head(PayInfoExcelVO.class).excludeColumnFieldNames(excelColumn).build();
        excelWriter.write(payInfoExcelList, sheetWriter);
    }

    private List<PayInfoExcelVO> getExcelList(List<PayInfoVO> payInfoList) {
        List<PayInfoExcelVO> payInfoExcelList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        int index = 1;
        for (PayInfoVO payInfoVO : payInfoList) {
            PayInfoExcelVO payInfoExcelVO = new PayInfoExcelVO();
            payInfoExcelVO.setSeq(String.valueOf(index));
            payInfoExcelVO.setShopName(payInfoVO.getShopName());
            payInfoExcelVO.setPayId(payInfoVO.getPayId());
            payInfoExcelVO.setOrderIds(payInfoVO.getOrderIds());
            payInfoExcelVO.setPayEntry(PayEntry.getPayTypeName(payInfoVO.getPayEntry()));
            payInfoExcelVO.setPayType(PayType.getPayTypeName(payInfoVO.getPayType()));
            payInfoExcelVO.setPayScore(Objects.isNull(payInfoVO.getScoreCount()) ? "0" : payInfoVO.getScoreCount().toString());
            payInfoExcelVO.setAlipayAmount(PriceUtil.toDecimalPrice(payInfoVO.getAlipayAmount()).toString());
            payInfoExcelVO.setBalanceAmount(PriceUtil.toDecimalPrice(payInfoVO.getBalanceAmount()).toString());
            payInfoExcelVO.setWechatAmount(PriceUtil.toDecimalPrice(payInfoVO.getWechatAmount()).toString());
            payInfoExcelVO.setPayTime(payInfoVO.getPayTime());
            payInfoExcelList.add(payInfoExcelVO);
            index++;
        }
        return payInfoExcelList;
    }

    @Override
    public PageVO<ShopAccountDetailVO> pageIncomeAccountDetail(PageDTO pageDTO, AccountSearchDTO accountSearchDTO) {
        // 获取店铺信息
        ServerResponseEntity<PageShopAccountOrderVO> orderResponse = orderFeignClient.listShopAccountDetail(accountSearchDTO.getStartTime(), accountSearchDTO.getEndTime(), accountSearchDTO.getShopName(), pageDTO.getPageSize(), pageDTO.getPageNum());
        if (!orderResponse.isSuccess()) {
            throw new LuckException(orderResponse.getMsg());
        }
        PageShopAccountOrderVO data = orderResponse.getData();
        List<ShopAccountOrderDetailVO> shopList = data.getShopList();
        List<ShopAccountDetailVO> result = BeanUtil.mapAsList(shopList, ShopAccountDetailVO.class);
        PageVO<ShopAccountDetailVO> shopPage = new PageVO<>();
        shopPage.setTotal(data.getTotal());
        shopPage.setList(result);
        shopPage.setPages(PageUtil.getPages(data.getTotal(), pageDTO.getPageSize()));
        if (CollectionUtils.isEmpty(result)) {
            return shopPage;
        }
        // 查询店铺信息
        List<Long> shopIds = result.stream().map(ShopAccountDetailVO::getShopId).toList();
        List<ShopDetailVO> shops = shopDetailFeignClient.listByShopIds(shopIds).getData();
        Map<Long, String> shopNameMap = new HashMap<>(Constant.INITIAL_CAPACITY);
        if (!CollectionUtils.isEmpty(shops)) {
            shopNameMap = shops.stream().collect(Collectors.toMap(ShopDetailVO::getShopId, ShopDetailVO::getShopName));
        }
        // 补充信息
        String shopName;
        for (ShopAccountDetailVO shopAccountDetailVO: result) {
            if (!ObjectUtils.isEmpty(shopName = shopNameMap.get(shopAccountDetailVO.getShopId()))) {
                shopAccountDetailVO.setShopName(shopName);
            }
        }
        return shopPage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @GlobalTransactional(rollbackFor = Exception.class)
    public void noticeShopBalancePayRefundSpread(PayInfoResultBO payInfoResultBO, Long orderId) {
        // 标记为支付成功状态
        PayInfo payInfo = new PayInfo();
        payInfo.setPayId(payInfoResultBO.getPayId());
        payInfo.setBizPayNo(payInfoResultBO.getBizPayNo());
        payInfo.setCallbackContent(payInfoResultBO.getCallbackContent());
        payInfo.setCallbackTime(new Date());
        payInfo.setPayStatus(PayStatus.PAYED.value());
        int updateStatus = payInfoMapper.updateToSuccess(payInfo);
        // 不能重复支付
        if (updateStatus == 0) {
            throw new LuckException("支付状态错误，更新失败");
        }
        ServerResponseEntity<OrderRefundVO> orderRefundResp = orderRefundFeignClient.getOrderRefundByRefundId(payInfoResultBO.getRefundId());
        if (!orderRefundResp.isSuccess()) {
            throw new LuckException(orderRefundResp.getMsg());
        }
        OrderRefundVO orderRefundVO = orderRefundResp.getData();
        // 1.执行减少商家钱包余额，并添加日志
        OrderChangeShopWalletAmountBO orderChangeShopWalletAmountBO = new OrderChangeShopWalletAmountBO();
        orderChangeShopWalletAmountBO.setOrderId(orderId);
        orderChangeShopWalletAmountBO.setRefundId(orderRefundVO.getRefundId());
        orderChangeShopWalletAmountBO.setShopId(orderRefundVO.getShopId());
        orderChangeShopWalletAmountBO.setPaySysType(PaySysType.ALLINPAY.value());
        orderChangeShopWalletAmountBO.setRefundAmount(orderRefundVO.getRefundAmount());
        orderChangeShopWalletAmountBO.setActualTotal(payInfoResultBO.getPayAmount());
        shopWalletFeignClient.subSettlementAmount(orderChangeShopWalletAmountBO);
        // 2.执行商家同意退款
        orderRefundFeignClient.doRefund(payInfoResultBO.getExtendInfo());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void noticeSettlementOrder(PayInfoResultBO payInfoResultBO, PayInfo payInfo) {
        // 判断下是否已经结算过
        if (Objects.equals(payInfo.getPayStatus(), 1)) {
            return;
        }
        payInfo.setPayStatus(1);
        // 标记为支付成功状态
        payInfo.setPayId(payInfoResultBO.getPayId());
        payInfo.setBizPayNo(payInfoResultBO.getBizPayNo());
        payInfo.setCallbackContent(payInfoResultBO.getCallbackContent());
        payInfo.setCallbackTime(new Date());
        payInfo.setPayStatus(PayStatus.PAYED.value());
        int updateStatus = payInfoMapper.updateToSuccess(payInfo);
        // 不能重复支付
        if (updateStatus == 0) {
            throw new LuckException("支付状态错误，更新失败");
        }
        // 1.执行订单后续结算
        OrderChangeShopWalletAmountBO orderChangeShopWalletAmountBO = new OrderChangeShopWalletAmountBO();
        orderChangeShopWalletAmountBO.setOrderId(Long.valueOf(payInfo.getOrderIds()));
        orderChangeShopWalletAmountBO.setPaySysType(PaySysType.ALLINPAY.value());
        // 开启事务消息，通知订单自己，开始往各个服务发送通知了
        TransactionSendResult transactionSendResult = orderSettledShopTemplate.sendMessageInTransaction(RocketMqConstant.ORDER_SETTLED_SHOP_TOPIC, new GenericMessage<>(Collections.singletonList(orderChangeShopWalletAmountBO)), null);

        if (!Objects.equals(transactionSendResult.getLocalTransactionState(), LocalTransactionState.COMMIT_MESSAGE)) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void platformTransfer(SplitRuleVO splitRuleVO) {
        PayInfo payInfo = payInfoMapper.getByOrderIdAndEntryAndToUserId(splitRuleVO.getBizUserId(), splitRuleVO.getOrderId(), PayEntry.PLATFORM_TRANSFER.value());
        if (Objects.nonNull(payInfo)) {
            return;
        }
        PayInfo platformPayInfo = new PayInfo();
        // 支付单号
        Long payId = segmentManager.getSegmentIdWithDateTime(DistributedIdKey.tmerclub_PAY, Constant.PLATFORM_SHOP_ID);
        platformPayInfo.setPayId(payId);
        platformPayInfo.setRefundId(0L);
        platformPayInfo.setUserId(0L);
        platformPayInfo.setPayAmount(splitRuleVO.getAmount());
        platformPayInfo.setPayScore(0L);
        platformPayInfo.setPayStatus(PayStatus.PAYED.value());
        platformPayInfo.setSysType(AuthUserContext.get().getSysType());
        platformPayInfo.setPayType(PayType.PLATFORM_BALANCE.value());
        platformPayInfo.setVersion(0);
        platformPayInfo.setToUserId(splitRuleVO.getBizUserId());
        // 保存多个支付订单号
        platformPayInfo.setOrderIds(splitRuleVO.getOrderId().toString());
        platformPayInfo.setPayEntry(PayEntry.PLATFORM_TRANSFER.value());
        // 保存预支付信息
        payInfoMapper.save(platformPayInfo);
        boolean flag = allinpayManager.execApplicationTransfer(payId, splitRuleVO.getBizUserId(), platformPayInfo.getPayAmount());
        if (!flag) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
    }

    @Override
    public List<PayInfo> getPaySystemTypeByUserIdAndOrderId(Long userId, Long orderId) {
        return payInfoMapper.getPayInfoByUserIdAndOrderId(userId, orderId);
    }

    @Override
    public void updateToSuccess(PayInfo payInfo) {
        int updateStatus = payInfoMapper.updateToSuccess(payInfo);
        // 不能重复支付
        if (updateStatus == 0) {
            throw new LuckException("支付状态错误，更新失败");
        }
    }
}
