package com.yida.data.device.consume.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yida.data.common.core.entity.constant.UPayConstant;
import com.yida.data.common.core.entity.constant.UnionPayConstant;
import com.yida.data.common.core.entity.constant.WxPayConstant;
import com.yida.data.common.core.entity.consume.DeptConsumeTerminalCache;
import com.yida.data.common.core.entity.consume.EduConsumeOrder;
import com.yida.data.common.core.entity.consume.EduConsumeOrderRefund;
import com.yida.data.common.core.entity.pay.URefund;
import com.yida.data.common.core.entity.pay.UnionRefund;
import com.yida.data.common.core.entity.system.Dept;
import com.yida.data.common.core.entity.system.EduPayWxConfig;
import com.yida.data.common.core.entity.system.UPayConfig;
import com.yida.data.common.core.entity.system.UnionPayConfig;
import com.yida.data.common.core.enums.*;
import com.yida.data.common.core.exception.FebsException;
import com.yida.data.common.core.utils.*;
import com.yida.data.common.service.CommonService;
import com.yida.data.device.consume.mapper.EduConsumeOrderMapper;
import com.yida.data.device.consume.service.EduConsumeOrderRefundService;
import com.yida.data.device.consume.service.EduConsumeOrderService;
import com.yida.data.device.consume.service.H5ConsumeService;
import com.yida.data.device.dto.consume.ConsumeOrderPageDTO;
import com.yida.data.device.vo.consume.ConsumeOrderPageVO;
import com.yida.data.device.vo.consume.ConsumeRefundOrderVO;
import com.yida.data.system.feign.RemoteDeptService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 消费订单(充值记录)Service实现
 *
 * @author ZYJ
 * @date 2023/3/23
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(rollbackFor = Exception.class)
public class EduConsumeOrderServiceImpl extends ServiceImpl
        <EduConsumeOrderMapper, EduConsumeOrder> implements EduConsumeOrderService {

    private final EduConsumeOrderRefundService eduConsumeOrderRefundService;

    @Lazy
    @Resource
    private H5ConsumeService h5ConsumeService;

    private final RemoteDeptService remoteDeptService;

    private final CommonService commonService;
    private final UnionPayPublicUtil unionPayPublicUtil;
    private static final String CONSUME_WX_REFUND_NOTIFY_URL = "mall/in/consume/wxRefundNotify";

    @Value("${febs.apiUrl}")
    private String apiUrl;

    @Override
    public String uPayNotify(String result, HttpServletRequest request) {
        try {
            JSONObject map = JSONUtil.parseObj(result);
            // 根据订单号查询对应的订单信息
            EduConsumeOrder order = getOne(new LambdaQueryWrapper<>(new EduConsumeOrder())
                    .eq(EduConsumeOrder::getOrderCode, map.get("client_sn")));
            if (Objects.isNull(order)) {
                log.error("收钱吧支付回调, 未找到订单信息, orderNO: {}", map.get("client_sn"));
                return UPayNotifyResultEnum.FAILED_STATUS.getValue();
            }
            // 判断支付结果是否处理
            if (OrderStatusEnum.PAY_SUCCESS.getStatus().equals(order.getOrderStatus())) {
                log.info("收钱吧支付回调, 该订单已经处理: {}", order.getOrderCode());
                return UPayNotifyResultEnum.SUCCESS_STATUS.getValue();
            }
            // 获取收钱吧配置信息
            UPayConfig config = commonService.getUPayConfigByDept(order.getDeptId());
            if (Objects.isNull(config)) {
                log.info("收钱吧支付回调, 该学校没有对应的配置信息: {}", order.getDeptId());
                return UPayNotifyResultEnum.FAILED_STATUS.getValue();
            }
            // 返回的订单状态
            String orderStatus = (String) map.get("order_status");
            // 新订单和不明确订单直接返回
            if (UPayTradeStateEnum.CREATED.getStatus().equals(orderStatus)
                    || UPayTradeStateEnum.INVALID_STATUS_CODE.getStatus().equals(orderStatus)) {
                log.info("新订单和不明确订单, orderNo: {}", order.getOrderCode());
                return UPayNotifyResultEnum.FAILED_STATUS.getValue();
            }
            // 签名
            String signature = request.getHeader("Authorization");
            // 验签
            boolean checkFlag = UPayUtil.validateSign(result, signature, config.getPublicKey());
            if (!checkFlag) {
                log.error("收钱吧支付回调, 验签失败");
                return UPayNotifyResultEnum.FAILED_STATUS.getValue();
            }
            // 支付时间
            String payTime = (String) map.get("finish_time");
            // 修改订单状态
            UPayTradeStateEnum tradeStateEnum = EnumUtils.valueOf(UPayTradeStateEnum.class, orderStatus,
                    UPayTradeStateEnum.class.getMethod("getStatus"));
            switch (Objects.requireNonNull(tradeStateEnum)) {
                // 支付成功
                case PAID:
//                    h5ConsumeService.consumeOrderPaySuccess(order.getOrderCode(), LocalDateTimeUtil.parse(payTime, DatePattern.NORM_DATETIME_PATTERN));
                    h5ConsumeService.consumeOrderPaySuccess(order.getOrderCode(), LocalDateTimeUtil.ofUTC(Long.parseLong(payTime)));
                    break;
                // 订单关闭
                case CANCELED:
                    order.setOrderStatus(OrderStatusEnum.CLOSED.getStatus());
                    order.setPayStatus(PayStatusEnum.CANCEL.getStatus());
                    updateById(order);
                    break;
                default:
                    break;
            }

        } catch (Exception e) {
            log.error("处理收钱吧支付回调数据失败", e);
            return UPayNotifyResultEnum.FAILED_STATUS.getValue();
        }
        return UPayNotifyResultEnum.SUCCESS_STATUS.getValue();
    }

    @Override
    public void updateOrderStatus() {
        // 查询超时订单
        List<EduConsumeOrder> list = baseMapper.selectTimeOutConsumeOrder();
        if (CollUtil.isNotEmpty(list)) {
            for (EduConsumeOrder order : list) {
                // 微信订单
                if (PayWay.WEI_XIN.getValue().equals(order.getPayWay())) {
                    updateWxOrderStatus(order);
                } else if (PayWay.UNION.getValue().equals(order.getPayWay())) {
                    updateUnionOrderStatus(order);
                } else if (PayWay.U_PAY.getValue().equals(order.getPayWay())) {
                    updateUPayOrderStatus(order);
                }
            }
        }
    }

    /**
     * 更新微信超时消费订单状态
     *
     * @param order 订单信息
     * @author ZYJ
     * @date 2023/4/3 13:34
     */
    private void updateWxOrderStatus(EduConsumeOrder order) {
        try {
            // 查询学校微信支付配置信息
            EduPayWxConfig config = commonService.getPayWxConfigBySchool(order.getDeptId());
            // 主动查询订单支付状态
            JSONObject resultJson = JSONUtil.parseObj(WxPayUtil.queryOrder(config, order.getOrderCode()));
            log.info("微信消费订单查询返回数据, consumeOrderCode: {}, resultJson: {}", order.getOrderCode(), resultJson);
            if (resultJson.containsKey(WxPayConstant.CODE_NAME)
                    && WxPayConstant.ORDER_NOT_EXIST.equals(resultJson.getStr(WxPayConstant.CODE_NAME))) {
                // 订单不存在  取消订单
                order.setOrderStatus(OrderStatusEnum.CLOSED.getStatus());
                order.setPayStatus(PayStatusEnum.CANCEL.getStatus());
            } else {
                String tradeState = resultJson.getStr("trade_state");
                // 未支付则关闭订单
                if (WxTradeStateEnum.NOT_PAY.getStatus().equals(tradeState)) {
                    WxPayUtil.closeOrder(config, order.getOrderCode());
                }
                WxTradeStateEnum wxTradeStateEnum = EnumUtils.valueOf(WxTradeStateEnum.class, tradeState,
                        WxTradeStateEnum.class.getMethod("getStatus"));
                switch (Objects.requireNonNull(wxTradeStateEnum)) {
                    // 支付成功情况
                    case SUCCESS:
                        h5ConsumeService.consumeOrderPaySuccess(order.getOrderCode(),
                                DateUtil.parseLocalDateTime(resultJson.getStr("success_time"),
                                        "yyyy-MM" + "-dd'T" + "'HH:mm" + ":ssXXX"));
                        break;
                    // 作废订单
                    case NOT_PAY:
                    case CLOSED:
                    case REVOKED:
                    case PAY_ERROR:
                        order.setOrderStatus(OrderStatusEnum.CLOSED.getStatus());
                        order.setPayStatus(PayStatusEnum.CANCEL.getStatus());
                        break;
                    default:
                        break;
                }
            }
            if (OrderStatusEnum.CLOSED.getStatus().equals(order.getOrderStatus())) {
                baseMapper.updateById(order);
            }
        } catch (Exception e) {
            log.error("更新微信消费订单状态失败, order: {}", order, e);
        }
    }

    /**
     * 更新银联超时消费订单状态
     *
     * @param order 订单信息
     * @author ZYJ
     * @date 2023/4/3 13:41
     */
    private void updateUnionOrderStatus(EduConsumeOrder order) {
        try {
            // 查询学校银联配置信息
            UnionPayConfig config = commonService.getUnionPayConfigByDept(order.getDeptId());
            // 主商户信息
            UnionPayConfig parentConfig = config.getParentConfig();
            // 开发者ID
            String appId = Objects.nonNull(parentConfig) ? parentConfig.getAppId() : config.getAppId();
            // 开发者key
            String appKey = Objects.nonNull(parentConfig) ? parentConfig.getAppKey() : config.getAppKey();

            // 主动查询订单支付状态
            JSONObject resultJson = UnionPayUtil.queryOrder(config, unionPayPublicUtil.getAccessToken(appId, appKey), order.getOrderCode());
            log.info("银联消费订单查询返回数据, consumeOrderCode: {}, resultJson: {}", order.getOrderCode(), resultJson);
            // 返回code
            String code = (String) resultJson.get(UnionPayConstant.CODE_NAME);
            // 无法找到指定的订单 需要直接关闭订单
            if (UnionPayConstant.NO_ORDER_CODE.equalsIgnoreCase(code)
                    || UnionPayConstant.TARGET_FAIL_CODE.equalsIgnoreCase(code)) {
                order.setOrderStatus(OrderStatusEnum.CLOSED.getStatus());
                order.setPayStatus(PayStatusEnum.CANCEL.getStatus());
                baseMapper.updateById(order);
                return;
            }
            // 支付状态
            String status = (String) resultJson.get("status");
            // 未支付则关闭订单 (银联云闪付不支付关闭订单)
            if (UnionTradeStateEnum.NEW_ORDER.getStatus().equals(status)
                    && !UnionPayTypeEnum.UNION_H5_TYPE.getType().equals(order.getPayType())) {
                UnionPayUtil.closeOrder(config, unionPayPublicUtil.getAccessToken(appId, appKey), order.getOrderCode());
            }
            UnionTradeStateEnum tradeStateEnum = EnumUtils.valueOf(UnionTradeStateEnum.class, status,
                    UnionTradeStateEnum.class.getMethod("getStatus"));
            switch (Objects.requireNonNull(tradeStateEnum)) {
                // 支付成功
                case TRADE_SUCCESS:
                    h5ConsumeService.consumeOrderPaySuccess(order.getOrderCode(),
                            LocalDateTimeUtil.parse((String) resultJson.get("payTime"), DatePattern.NORM_DATETIME_PATTERN));
                    break;
                // 作废订单
                case NEW_ORDER:
                case TRADE_CLOSED:
                case WAIT_BUYER_PAY:
                    order.setOrderStatus(OrderStatusEnum.CLOSED.getStatus());
                    order.setPayStatus(PayStatusEnum.CANCEL.getStatus());
                    break;
                default:
                    break;
            }
            if (OrderStatusEnum.CLOSED.getStatus().equals(order.getOrderStatus())) {
                baseMapper.updateById(order);
            }
        } catch (Exception e) {
            log.error("更新银联消费订单状态失败, order: {}", order, e);
        }
    }

    /**
     * 更新收钱吧超时消费订单状态
     *
     * @param order 订单信息
     * @author ZYJ
     * @date 2023/4/3 13:48
     */
    private void updateUPayOrderStatus(EduConsumeOrder order) {
        try {
            DeptConsumeTerminalCache terminalCache = commonService.getDeptConsumeTerminal(order.getDeptId());
            JSONObject jsonObject = UPayUtil.queryOrder(order.getOrderCode(), terminalCache.getTerminalSn(), terminalCache.getTerminalKey());
            log.info("收钱吧消费订单查询返回数据, consumeOrderCode: {}, resultJson: {}", order.getOrderCode(), jsonObject);
            JSONObject bizResponse = jsonObject.getJSONObject("biz_response");
            // 无法找到指定的订单 需要直接关闭订单
            if (UPayConstant.NO_ORDER_CODE.equals(bizResponse.getStr(UPayConstant.ERROR_CODE_NAME))
                    || UPayConstant.TRADE_NOT_EXIST.equals(bizResponse.getStr(UPayConstant.ERROR_CODE_NAME))) {
                order.setOrderStatus(OrderStatusEnum.CLOSED.getStatus());
                order.setPayStatus(PayStatusEnum.CANCEL.getStatus());
                baseMapper.updateById(order);
                return;
            }
            JSONObject resultJson = bizResponse.getJSONObject("data");
            // 支付状态
            String status = (String) resultJson.get("order_status");
            // 无法找到指定的订单 需要直接关闭订单
            if (UPayTradeStateEnum.CREATED.getStatus().equals(status)
                    || UPayTradeStateEnum.INVALID_STATUS_CODE.getStatus().equals(status)) {
                order.setOrderStatus(OrderStatusEnum.CLOSED.getStatus());
                order.setPayStatus(PayStatusEnum.CANCEL.getStatus());
                baseMapper.updateById(order);
                return;
            }
            UPayTradeStateEnum tradeStateEnum = EnumUtils.valueOf(UPayTradeStateEnum.class, status,
                    UPayTradeStateEnum.class.getMethod("getStatus"));
            switch (Objects.requireNonNull(tradeStateEnum)) {
                // 支付成功
                case PAID:
                    h5ConsumeService.consumeOrderPaySuccess(order.getOrderCode(), LocalDateTimeUtil.ofUTC(Long.parseLong(resultJson.getStr("finish_time"))));
                    break;
                // 作废订单
                case CREATED:
                case CANCELED:
                    order.setOrderStatus(OrderStatusEnum.CLOSED.getStatus());
                    order.setPayStatus(PayStatusEnum.CANCEL.getStatus());
                    break;
                default:
                    break;
            }
            if (OrderStatusEnum.CLOSED.getStatus().equals(order.getOrderStatus())) {
                baseMapper.updateById(order);
            }
        } catch (Exception e) {
            log.error("更新收钱吧消费订单状态失败, order: {}", order, e);
        }
    }

    @Override
    public void updateRefundStatus() {
        // 查询退款中状态的消费订单
        List<ConsumeRefundOrderVO> refundOrderList = baseMapper.selectRefundOrder();
        if (CollUtil.isNotEmpty(refundOrderList)) {
            refundOrderList.forEach(refundOrderVO -> {
                // 微信
                if (PayWay.WEI_XIN.getValue().equals(refundOrderVO.getPayWay())) {
                    updateWxRefundStatus(refundOrderVO);
                } else if (PayWay.UNION.getValue().equals(refundOrderVO.getPayWay())) {
                    updateUnionRefundStatus(refundOrderVO);
                } else if (PayWay.U_PAY.getValue().equals(refundOrderVO.getPayWay())) {
                    updateUPayRefundStatus(refundOrderVO);
                }
            });
        }
    }

    /**
     * 更新微信退款订单状态
     *
     * @param vo 退款信息
     * @author ZYJ
     * @date 2023/4/3 16:11
     */
    private void updateWxRefundStatus(ConsumeRefundOrderVO vo) {
        try {
            EduPayWxConfig config = commonService.getPayWxConfigBySchool(vo.getDeptId());
            // 主动查询退款状态
            JSONObject resultJson = JSONUtil.parseObj(WxPayUtil.queryRefund(config, vo.getRefundCode()));
            log.info("账单订单退款查询返回数据, refundOrderCode: {}, resultJson: {}", vo.getRefundCode(), resultJson);
            // 状态
            String status = resultJson.getStr("status");
            WxRefundStatusEnum wxRefundStatusEnum = EnumUtils.valueOf(WxRefundStatusEnum.class, status,
                    WxRefundStatusEnum.class.getMethod("getStatus"));
            switch (Objects.requireNonNull(wxRefundStatusEnum)) {
                case REFUND_SUCCESS:
                    // 更新订单退款状态
                    EduConsumeOrder order = getById(vo.getOrderId());
                    order.setOrderStatus(OrderStatusEnum.CLOSED.getStatus());
                    order.setPayStatus(PayStatusEnum.REFUND_SUCCESS.getStatus());
                    order.setIsRefund(RefundStatusEnum.IS_REFUND.getStatus());
                    baseMapper.updateById(order);
                    // 更新退款时间
                    EduConsumeOrderRefund eduBillOrderRefund = eduConsumeOrderRefundService.getById(vo.getRefundId());
                    eduBillOrderRefund.setRefundTime(DateUtil.parseLocalDateTime(resultJson.getStr("success_time"), "yyyy-MM-dd'T'HH:mm:ssXXX"));
                    eduConsumeOrderRefundService.updateById(eduBillOrderRefund);
                    break;
                case REFUND_ABNORMAL:
                    // TODO 2022/6/27 需要后续处理退款失败情况
                    break;
                default:
                    break;
            }
        } catch (Exception e) {
            log.error("查询微信消费订单退款状态失败, refundOrder: {}", vo, e);
        }
    }

    /**
     * 更新银联退款订单状态
     *
     * @param vo 退款信息
     * @author ZYJ
     * @date 2023/4/3 16:22
     */
    private void updateUnionRefundStatus(ConsumeRefundOrderVO vo) {
        try {
            // 查询学校银联配置信息
            UnionPayConfig config = commonService.getUnionPayConfigByDept(vo.getDeptId());
            // 主商户信息
            UnionPayConfig parentConfig = config.getParentConfig();
            // 开发者ID
            String appId = Objects.nonNull(parentConfig) ? parentConfig.getAppId() : config.getAppId();
            // 开发者key
            String appKey = Objects.nonNull(parentConfig) ? parentConfig.getAppKey() : config.getAppKey();
            // 主动查询退款状态
            JSONObject resultJson = UnionPayUtil.refundQuery(config, vo.getRefundCode(), unionPayPublicUtil.getAccessToken(appId, appKey));
            log.info("银联账单订单退款查询返回数据, refundOrderCode: {}, resultJson: {}", vo.getRefundCode(), resultJson);
            // 退款状态
            String refundStatus = resultJson.getStr("refundStatus");
            UnionRefundStatusEnum refundStatusEnum = EnumUtils.valueOf(UnionRefundStatusEnum.class, refundStatus,
                    UnionRefundStatusEnum.class.getMethod("getStatus"));
            switch (Objects.requireNonNull(refundStatusEnum)) {
                case REFUND_SUCCESS:
                    // 更新订单退款状态
                    EduConsumeOrder eduBillOrder = getById(vo.getOrderId());
                    eduBillOrder.setOrderStatus(OrderStatusEnum.CLOSED.getStatus());
                    eduBillOrder.setPayStatus(PayStatusEnum.REFUND_SUCCESS.getStatus());
                    eduBillOrder.setIsRefund(RefundStatusEnum.IS_REFUND.getStatus());
                    baseMapper.updateById(eduBillOrder);
                    // 更新退款时间
                    EduConsumeOrderRefund eduBillOrderRefund = eduConsumeOrderRefundService.getById(vo.getRefundId());
                    eduBillOrderRefund.setRefundTime(LocalDateTimeUtil.parse((String) resultJson.get("responseTimestamp"), DatePattern.NORM_DATETIME_PATTERN));
                    eduConsumeOrderRefundService.updateById(eduBillOrderRefund);
                    break;
                case REFUND_FAIL:
                    // TODO 2022/6/27 需要后续处理退款失败情况
                    break;
                default:
                    break;
            }
        } catch (Exception e) {
            log.error("查询银联消费订单退款状态失败, refundOrder: {}", vo, e);
        }
    }

    /**
     * 更新收钱吧退款订单状态
     *
     * @param vo 退款信息
     * @author ZYJ
     * @date 2023/4/3 16:26
     */
    private void updateUPayRefundStatus(ConsumeRefundOrderVO vo) {
        try {
            DeptConsumeTerminalCache terminalCache = commonService.getDeptConsumeTerminal(vo.getDeptId());
            JSONObject jsonObject = UPayUtil.queryRefundOrder(vo.getOrderCode(), vo.getRefundCode(), terminalCache.getTerminalSn(), terminalCache.getTerminalKey());
            log.info("收钱吧消费订单查询返回数据, consumeOrderCode: {}, resultJson: {}", vo.getOrderCode(), jsonObject);
            JSONObject resultJson = jsonObject.getJSONObject("biz_response").getJSONObject("data");

            // 订单状态
            String status = (String) resultJson.get("order_status");
            UPayTradeStateEnum tradeStateEnum = EnumUtils.valueOf(UPayTradeStateEnum.class, status,
                    UPayTradeStateEnum.class.getMethod("getStatus"));
            switch (Objects.requireNonNull(tradeStateEnum)) {
                // 支付成功
                case REFUNDED:
                    // 更新订单退款状态
                    EduConsumeOrder eduBillOrder = getById(vo.getOrderId());
                    eduBillOrder.setOrderStatus(OrderStatusEnum.CLOSED.getStatus());
                    eduBillOrder.setPayStatus(PayStatusEnum.REFUND_SUCCESS.getStatus());
                    eduBillOrder.setIsRefund(RefundStatusEnum.IS_REFUND.getStatus());
                    baseMapper.updateById(eduBillOrder);
                    // 更新退款时间
                    EduConsumeOrderRefund eduBillOrderRefund = eduConsumeOrderRefundService.getById(vo.getRefundId());
                    eduBillOrderRefund.setRefundTime(LocalDateTimeUtil.ofUTC(Long.parseLong(resultJson.getStr("finish_time"))));
                    eduConsumeOrderRefundService.updateById(eduBillOrderRefund);
                    break;
                case REFUND_ERROR:
                    // TODO 2023/4/3 需要后续处理退款失败情况
                    break;
                default:
                    break;
            }
        } catch (Exception e) {
            log.error("查询收钱吧消费订单退款状态失败, refundOrder: {}", vo, e);
        }
    }

    @Override
    public Page<ConsumeOrderPageVO> listConsumeOrderPage(ConsumeOrderPageDTO dto) {

        List<Long> schoolIds = new ArrayList<>();
        if (Objects.isNull(dto.getSchoolId())) {
            List<Dept> deptList = remoteDeptService.getSchoolByArea(null, "").getData();
            if (CollUtil.isEmpty(deptList)) {
                schoolIds.add(-1L);
            } else {
                schoolIds = deptList.stream().map(Dept::getDeptId).collect(Collectors.toList());
            }
        } else {
            schoolIds.add(dto.getSchoolId());
        }

        return baseMapper.listConsumeOrderPage(new Page<>(dto.getPageNum(), dto.getPageSize()), schoolIds, dto);
    }

    @Override
    public void refundOrder(String orderCode) {
        EduConsumeOrder order = getOne(new LambdaQueryWrapper<>(new EduConsumeOrder()).eq(EduConsumeOrder::getOrderCode, orderCode));
        // 判断订单能否退款
        Asserts.isTrue(OrderStatusEnum.PAY_SUCCESS.getStatus().equals(order.getOrderStatus())
                && PayStatusEnum.PAY_SUCCESS.getStatus().equals(order.getPayStatus()), "该笔订单无法进行退款");
        // 保存退款信息
        EduConsumeOrderRefund eduConsumeOrderRefund = new EduConsumeOrderRefund();
        eduConsumeOrderRefund.setOrderCode(orderCode);
        // 生成退款订单号
        String refundOrderCode = "";
        if (PayWay.WEI_XIN.getValue().equals(order.getPayWay())) {
            // 微信
            refundOrderCode = "R" + orderCode;
        } else if (PayWay.UNION.getValue().equals(order.getPayWay())) {
            // 银联
            // 获取银联商户号相关信息
            UnionPayConfig config = commonService.getUnionPayConfigByDept(order.getDeptId());
            refundOrderCode = UnionPayUtil.getOrderNumber(Objects.nonNull(config.getParentConfig())
                    ? config.getParentConfig().getSourceNumber() : config.getSourceNumber());
        } else if (PayWay.U_PAY.getValue().equals(order.getPayWay())) {
            // 收钱吧
            refundOrderCode = "R" + orderCode;
        }
        eduConsumeOrderRefund.setRefundCode(refundOrderCode);
        eduConsumeOrderRefund.setRefundSerialNumber(order.getTransactionNumber().replace("P", "R"));
        eduConsumeOrderRefundService.save(eduConsumeOrderRefund);
        // 修改订单状态
        order.setOrderStatus(OrderStatusEnum.USER_REFUNDING.getStatus());
        updateById(order);
        try {
            // 调用退款接口
            PayWay payWay = EnumUtils.valueOf(PayWay.class, order.getPayWay(),
                    PayWay.class.getMethod("getValue"));
            switch (Objects.requireNonNull(payWay)) {
                // 微信
                case WEI_XIN:
                    // 查询学校微信支付配置
                    EduPayWxConfig config = commonService.getPayWxConfigBySchool(order.getDeptId());
                    WxPayUtil.refund(config, eduConsumeOrderRefund.getOrderCode(),
                            eduConsumeOrderRefund.getRefundReason(),
                            eduConsumeOrderRefund.getRefundCode(),
                            NumberUtil.mul(order.getPracticalMoney(), new BigDecimal("100")).longValue(),
                            NumberUtil.mul(order.getPracticalMoney(), new BigDecimal("100")).longValue(),
                            apiUrl + CONSUME_WX_REFUND_NOTIFY_URL);
                    break;
                case UNION:
                    // 查询学校银联云闪付支付配置
                    UnionPayConfig unionPayConfig = commonService.getUnionPayConfigByDept(order.getDeptId());
                    UnionPayConfig parentConfig = unionPayConfig.getParentConfig();
                    UnionPayUtil.refund(unionPayConfig, UnionRefund.builder()
                                    .orderNo(orderCode)
                                    .refundOrderNo(eduConsumeOrderRefund.getRefundCode())
                                    .transactionNumber(eduConsumeOrderRefund.getRefundSerialNumber())
                                    .refundAmount(order.getPracticalMoney())
                                    .build()
                            , unionPayPublicUtil.getAccessToken(
                                    Objects.nonNull(parentConfig) ? parentConfig.getAppId() : unionPayConfig.getAppId(),
                                    Objects.nonNull(parentConfig) ? parentConfig.getAppKey() : unionPayConfig.getAppKey()
                            ));
                    break;
                case U_PAY:
                    DeptConsumeTerminalCache terminalCache = commonService.getDeptConsumeTerminal(order.getDeptId());
                    JSONObject jsonObject = UPayUtil.refund(URefund.builder()
                            .clientSn(orderCode)
                            .refundRequestNo(refundOrderCode)
                            .operator(order.getPhone())
                            .refundAmount(order.getPracticalMoney())
                            .build(), terminalCache.getTerminalSn(), terminalCache.getTerminalKey());
                    JSONObject resultJson = jsonObject.getJSONObject("biz_response").getJSONObject("data");
                    // 更新订单退款状态
                    order.setOrderStatus(OrderStatusEnum.CLOSED.getStatus());
                    order.setPayStatus(PayStatusEnum.REFUND_SUCCESS.getStatus());
                    order.setIsRefund(RefundStatusEnum.IS_REFUND.getStatus());
                    baseMapper.updateById(order);
                    // 更新退款时间
                    EduConsumeOrderRefund eduBillOrderRefund = eduConsumeOrderRefundService.getById(eduConsumeOrderRefund.getId());
                    eduBillOrderRefund.setRefundTime(LocalDateTimeUtil.ofUTC(Long.parseLong(resultJson.getStr("finish_time"))));
                    eduConsumeOrderRefundService.updateById(eduBillOrderRefund);
                    break;
                default:
                    break;
            }
        } catch (Exception e) {
            log.error("退款失败", e);
            throw new FebsException("退款失败");
        }
    }
}
