package com.wzj.soopin.transaction.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.wzj.soopin.order.domain.bo.RefundBO;
import com.wzj.soopin.order.domain.entity.Aftersale;
import com.wzj.soopin.order.domain.entity.Order;
import com.wzj.soopin.order.domain.entity.OrderItem;
import com.wzj.soopin.order.emum.OrderStatusEnum;
import com.wzj.soopin.order.service.AftersaleService;
import com.wzj.soopin.order.service.OrderItemService;
import com.wzj.soopin.order.service.OrderService;
import com.wzj.soopin.transaction.config.EasypayConfig;
import com.wzj.soopin.transaction.config.WechatMiniProgramConfig;
import com.wzj.soopin.transaction.domain.bo.*;
import com.wzj.soopin.transaction.domain.bo.easypay.*;
import com.wzj.soopin.transaction.domain.bo.easypay.merchant.add.*;
import com.wzj.soopin.transaction.domain.bo.easypay.merchant.binding.MerchantBindingReqBody;
import com.wzj.soopin.transaction.domain.bo.easypay.merchant.pictureUpload.MerchantPicUploadReqBody;
import com.wzj.soopin.transaction.domain.bo.easypay.merchant.pictureUpload.MerchantPicUploadRespBody;
import com.wzj.soopin.transaction.domain.bo.easypay.refund.apply.req.RefundApplyReqBody;
import com.wzj.soopin.transaction.domain.bo.easypay.refund.apply.req.RefundReqOrderInfo;
import com.wzj.soopin.transaction.domain.bo.easypay.refund.apply.resp.RefundApplyRespBody;
import com.wzj.soopin.transaction.domain.bo.easypay.separate.apply.req.SeparateApplyReqBody;
import com.wzj.soopin.transaction.domain.bo.easypay.separate.apply.req.SeparateReqOrderInfo;
import com.wzj.soopin.transaction.domain.bo.easypay.separate.apply.resp.SeparateApplyRespBody;
import com.wzj.soopin.transaction.domain.bo.easypay.separate.apply.resp.SeparateRespInfoList;
import com.wzj.soopin.transaction.domain.bo.easypay.separate.apply.resp.SeparateRespOrderInfo;
import com.wzj.soopin.transaction.domain.bo.easypay.separate.query.req.SeparateQueryReqBody;
import com.wzj.soopin.transaction.domain.bo.easypay.separate.query.resp.SeparateQueryRespBody;
import com.wzj.soopin.transaction.domain.bo.easypay.separate.refund.resp.SeparateRefundRespBody;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.req.*;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.resp.JsApiRespBody;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.resp.JsapiRespOrderInfo;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.resp.WxJsApiRespParamInfoVO;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.query.req.TradeQueryReqBody;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.query.resp.TradeQueryRespBody;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.query.resp.TradeQueryRespOrderInfo;
import com.wzj.soopin.transaction.domain.bo.easypay.separate.refund.req.SeparateRefundReqBody;
import com.wzj.soopin.transaction.domain.entity.WxAuthResponse;
import com.wzj.soopin.transaction.domain.po.Divide;
import com.wzj.soopin.transaction.domain.po.DivideDetail;
import com.wzj.soopin.transaction.domain.po.PayOrder;
import com.wzj.soopin.transaction.domain.vo.*;
import com.wzj.soopin.transaction.enums.DivideStatus;
import com.wzj.soopin.transaction.enums.TransState;
import com.wzj.soopin.transaction.enums.easypay.*;
import com.wzj.soopin.transaction.mapper.DivideDetailMapper;
import com.wzj.soopin.transaction.mapper.DivideMapper;
import com.wzj.soopin.transaction.mapper.PayOrderMapper;
import com.wzj.soopin.transaction.service.IEasypayService;
import com.wzj.soopin.transaction.util.SnowFlake;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.enums.FormatsType;
import org.dromara.common.core.enums.TenantType;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.ServletUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.rmi.ServerException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.wzj.soopin.transaction.constans.EasypayConstants.*;

/**
 * 会员封禁
 *
 * @author zcc
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class EasypayServiceImpl implements IEasypayService {

    private final EasypayConfig easypayConfig;

    private final WechatMiniProgramConfig wechatMiniProgramConfig;

    private final WxAuthService wxAuthService;

    private final OrderService orderService;

    private final OrderItemService orderItemService;

    private final AftersaleService aftersaleService;

    private final PayOrderMapper payOrderMapper;

    private final DivideMapper divideMapper;

    private final DivideDetailMapper divideDetailMapper;


    /**
     * 生成易生接口请求头参数
     */
    private EasyPayRequestHeader generateEasyPayRequestHeader() {
        return EasyPayRequestHeader.builder()
            .reqType(REQ_TYPE)
            .reqId(easypayConfig.getReqId())
            .transTime(DateUtils.parseDateToStr(FormatsType.YYYYMMDDHHMMSS, new Date()))
            .build();
    }

    /**
     * 递归排序 Map 中的嵌套对象
     */
    private void sortMap(Map<String, Object> map) {
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            Object value = entry.getValue();
            if (value instanceof JSONObject) {
                TreeMap<String, Object> sortedMap = new TreeMap<>((JSONObject) value);
                sortMap(sortedMap);
                map.put(entry.getKey(), sortedMap);
            } else if (value instanceof JSONArray) {
                JSONArray array = (JSONArray) value;
                for (int i = 0; i < array.size(); i++) {
                    Object item = array.get(i);
                    if (item instanceof JSONObject) {
                        TreeMap<String, Object> sortedMap = new TreeMap<>((JSONObject) item);
                        sortMap(sortedMap);
                        array.set(i, sortedMap);
                    }
                }
            }
        }
    }


    /**
     * 获取签名串
     * 待签名串 = ASCII升序(reqHeader) + MD5大写(ASCII升序(reqBody))
     * reqSign = RSA私钥加签(待签名串, 商户私钥)  //RSA模式 ECB/PKCS1_PADDING
     *
     * @param reqHeader
     * @param reqBody
     * @return
     */
    private String getSignStr(EasyPayRequestHeader reqHeader, Object reqBody) {
        TreeMap<String, Object> reqHeaderMap = new TreeMap<>(JSONObject.from(reqHeader));
        TreeMap<String, Object> reqBodyMap = new TreeMap<>(JSONObject.from(reqBody));
        // 递归排序
        sortMap(reqHeaderMap);
        sortMap(reqBodyMap);
        String md5Str = SecureUtil.md5(JSON.toJSONString(reqBodyMap)).toUpperCase();
        String sourceStr = StrBuilder.create(JSON.toJSONString(reqHeaderMap)).append(md5Str).toString();
        log.debug("待加签字符串为:{}", sourceStr);
        Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, easypayConfig.getMerRsaPrivateKey(), null);
        byte[] signatureBytes = sign.sign(sourceStr.getBytes(CharsetUtil.CHARSET_UTF_8));
        String signStr = Base64.encode(signatureBytes);
        log.debug("加签结果为:{}", signStr);
        return signStr;
    }

    /**
     * 验签
     * 待签名串 = ASCII排序(header) + MD5大写(ASCII排序(body))
     *
     * @param header
     * @param body
     * @param signStr
     */
    private void verify(Object header, Object body, String signStr) {
        TreeMap<String, Object> hearMap = new TreeMap<>(JSONObject.from(header));
        TreeMap<String, Object> bodyMap = new TreeMap<>(JSONObject.from(body));
        // 递归排序
        sortMap(hearMap);
        sortMap(bodyMap);
        String md5Str = SecureUtil.md5(JSON.toJSONString(bodyMap)).toUpperCase();
        String sourceStr = StrBuilder.create(JSON.toJSONString(hearMap)).append(md5Str).toString();
        log.debug("待验签字符串为:{}", sourceStr);
        Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, null, easypayConfig.getEasypayPublicKey());
        boolean verify = sign.verify(sourceStr.getBytes(CharsetUtil.CHARSET_UTF_8), Base64.decode(signStr));
        Assert.isTrue(verify, () -> new ServiceException("易生接口请求或响应数据验签失败"));
    }


    /**
     * 发起支付
     *
     * @param paymentBO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public EasypayPrePayVO payment(PaymentBO paymentBO) throws ServerException {
        checkPaymentParamByPayType(paymentBO);
        EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader();
        // 封装结算信息
        SettleParamInfo settleParamInfo = SettleParamInfo.builder()
            .delaySettleFlag(DelaySettleFlag.THE_0.toValue())
            .patnerSettleFlag(PatnerSettleFlag.THE_0.toValue())
            .splitSettleFlag(SplitSettleFlag.THE_1.toValue())
            .build();
        JsApiReqBody apiReqBody = JsApiReqBody.builder()
            .riskData(RiskData.builder().customerIp(ServletUtils.getClientIP()).build())
            .settleParamInfo(settleParamInfo)
            .build();
        //封装请求方信息
        apiReqBody.setReqInfo(ReqInfo.builder().mchtCode(easypayConfig.getMchtCode()).build());
        //封装基础订单信息
        PayOrder payOrder = setReqOrderInfo(apiReqBody, paymentBO);
        // 封装支付信息
        setPayInfo(apiReqBody, paymentBO);
        String reqSign = getSignStr(reqHeader, apiReqBody);
        EasyPayRequest easyPayRequest = EasyPayRequest.builder()
            .reqHeader(reqHeader)
            .reqBody(apiReqBody)
            .reqSign(reqSign)
            .build();
        log.debug("调用易生发起支付接口请求:{}", JSONObject.toJSONString(easyPayRequest));
        String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/jsapi").toString();
        String body = HttpRequest.post(url)
            .timeout(3000)
            .body(JSON.toJSONString(easyPayRequest))
            .execute()
            .body();
        log.debug("调用易生发起支付接口响应:{}", body);
        EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class);
        if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) {
            verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign());
            JsApiRespBody jsApiRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), JsApiRespBody.class);
            RespStateInfo respStateInfo = jsApiRespBody.getRespStateInfo();
            if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) {
                if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK, RSP_BODY_TRANS_PARTIALLY_OK, RSP_BODY_TRANS_OK_WETCAT, RSP_BODY_TRANS_PROCESSING)) {
                    //保存支付单信息到数据库
                    JsapiRespOrderInfo respOrderInfo = jsApiRespBody.getRespOrderInfo();
                    payOrder.setPayType(paymentBO.getPayType().getValue());
                    payOrder.setStartTransDate(new Date());
                    payOrder.setEasypayTrace(respOrderInfo.getOutTrace());
                    payOrder.setTransState(TransState.PAYMENT.getCode());
                    payOrderMapper.updateById(payOrder);
                    //关联订单的最新支付单
                    orderService.updateById(Order.builder()
                        .id(payOrder.getOrderId())
                        .payId(payOrder.getId())
                        .payAmount(BigDecimal.valueOf(payOrder.getTransAmount()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP))
                        .status(OrderStatusEnum.VERIFY.getValue())
                        .build());
                    // 生成返回前端的预支付信息
                    return generatePrePayVO(paymentBO.getPayType(), jsApiRespBody);
                } else {
                    log.error("发起支付失败:{}", respStateInfo.getTransStatusDesc() + (StrUtil.isNotBlank(respStateInfo.getAppendRetMsg()) ? "渠道返回码描述:" + respStateInfo.getAppendRetMsg() : ""));
                    throw new ServiceException("发起支付失败:" + respStateInfo.getTransStatusDesc() + (StrUtil.isNotBlank(respStateInfo.getAppendRetMsg()) ? "渠道返回码描述:" + respStateInfo.getAppendRetMsg() : ""));
                }
            } else {
                log.error("发起支付业务失败:{}", respStateInfo.getRespDesc());
                throw new ServiceException("发起支付业务失败:" + respStateInfo.getRespDesc());
            }
        } else {
            log.error("发起支付通讯失败:{}", easyPayResponse.getRspHeader().getRspInfo());
            throw new ServiceException("发起支付通讯失败:" + easyPayResponse.getRspHeader().getRspInfo());
        }
    }


    /**
     * 根据支付类型校验请求参数是否完整
     *
     * @param paymentBO
     */
    private void checkPaymentParamByPayType(PaymentBO paymentBO) throws ServerException {
        switch (paymentBO.getPayType()) {
            case ALI_PAY_JSAPI, ALI_PAY_MINI_APP ->
                Assert.notBlank(paymentBO.getBuyerId(), () -> new ServiceException("支付宝支付缺少必要参数:buyerId"));
            case WE_CHAT_JSAPI, WE_CHAT_MINI_APP ->
                Assert.notBlank(paymentBO.getWxLoginCode(), () -> new ServiceException("微信支付缺少必要参数:微信登录授权code"));
            case UNION_PAY_JSAPI, UNION_PAY_Js_MINI ->
                Assert.isTrue(StrUtil.isAllNotBlank(paymentBO.getTransType(), paymentBO.getUserAuthCode(),
                    paymentBO.getUserId(), paymentBO.getAreaInfo(), paymentBO.getPaymentValidTime(),
                    paymentBO.getQrCode(), paymentBO.getQrCodeType()), () -> new ServiceException("银联支付缺少必要参数"));
            default -> throw new ServerException("不支持的支付方式");
        }
    }

    /**
     * 封装支付信息
     *
     * @param apiReqBody
     * @param paymentBO
     */
    private void setPayInfo(JsApiReqBody apiReqBody, PaymentBO paymentBO) throws ServerException {
        switch (paymentBO.getPayType()) {
            case ALI_PAY_JSAPI, ALI_PAY_MINI_APP ->
                apiReqBody.setAliBizParam(AliBizParam.builder().buyerId(paymentBO.getBuyerId()).build());
            case WE_CHAT_JSAPI, WE_CHAT_MINI_APP -> {
                WxAuthResponse wxAuthResponse = wxAuthService.getOpenIdByMiniProgramCode(paymentBO.getWxLoginCode());
                apiReqBody.setWxBizParam(WxBizParam.builder().subAppid(wechatMiniProgramConfig.getAppId()).subOpenId(wxAuthResponse.getOpenid()).build());
            }
            case UNION_PAY_JSAPI, UNION_PAY_Js_MINI ->
                apiReqBody.setQrBizParam(BeanUtil.copyProperties(paymentBO, QrBizParam.class));
            default -> throw new ServerException("不支持的支付方式");
        }
        PayInfo payInfo = PayInfo.builder()
            .payType(paymentBO.getPayType().getValue())
            .transDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, new Date()))
            .build();
        apiReqBody.setPayInfo(payInfo);
    }

    /**
     * 封装基础订单信息
     *
     * @param apiReqBody
     */
    private PayOrder setReqOrderInfo(JsApiReqBody apiReqBody, PaymentBO paymentBO) {
        Order order = orderService.getById(paymentBO.getOrderId());
        Assert.notNull(order, () -> new ServiceException("订单不存在"));
        Assert.isTrue(Objects.equals(order.getStatus(), OrderStatusEnum.UNPAID.getValue()), () -> new ServiceException("订单已支付"));
        // 订单中的金额单位为元，需转换为分
        long transAmount = order.getTotalAmount().multiply(BigDecimal.valueOf(100)).longValue();
        PayOrder payOrder;
        // 订单已拉起过支付，支付中或已支付状态不可再次支付
        if (order.getPayId() != null) {
            payOrder = payOrderMapper.selectById(order.getPayId());
            Assert.notNull(payOrder, () -> new ServiceException("订单异常"));
            Assert.isTrue(Objects.equals(payOrder.getTransState(), TransState.PENDING.getCode()), () -> new ServiceException("订单支付中或已支付"));
        }
        payOrder = PayOrder.builder()
            .orderId(order.getId())
            .transAmount(transAmount)
            .startTransDate(new Date())
            .transState(TransState.PENDING.getCode())
            .payType(paymentBO.getPayType().getValue())
            .build();
        payOrderMapper.insert(payOrder);
        JsapiReqOrderInfo jsapiReqOrderInfo = JsapiReqOrderInfo.builder()
            .orgTrace(String.valueOf(payOrder.getId()))
            .transAmount(transAmount)
            .backUrl(easypayConfig.getTradeBackUrl())
            .timeout(String.valueOf(TRACE_TIMEOUT))
            .orderSub("无终街支付订单")
            .orderDes("无终街支付订单:" + order.getMerchantNote())
            .build();
        apiReqBody.setReqOrderInfo(jsapiReqOrderInfo);
        return payOrder;
    }

    /**
     * 生成预支付信息
     *
     * @param payType
     * @param jsApiRespBody
     * @return
     */
    private EasypayPrePayVO generatePrePayVO(PayType payType, JsApiRespBody jsApiRespBody) throws ServerException {
        EasypayPrePayVO easypayPrePayVO = EasypayPrePayVO.builder()
            .payType(payType)
            .build();
        switch (payType) {
            case ALI_PAY_JSAPI, ALI_PAY_MINI_APP -> {
                easypayPrePayVO.setTradeNo(jsApiRespBody.getAliRespParamInfo().getTradeNo());
            }
            case WE_CHAT_JSAPI, WE_CHAT_MINI_APP -> {
                WxJsApiRespParamInfoVO wxRespParamInfo = jsApiRespBody.getWxRespParamInfo();
                EasypayPrePayVO.WxPrePayParamInfo wxPrePayParamInfo = JSONObject.parseObject(wxRespParamInfo.getWcPayData(), EasypayPrePayVO.WxPrePayParamInfo.class);
                wxPrePayParamInfo.setPrepayId(wxRespParamInfo.getPrepayId());
                easypayPrePayVO.setWxPrePayParamInfo(wxPrePayParamInfo);
            }
            case UNION_PAY_JSAPI, UNION_PAY_Js_MINI -> {
                easypayPrePayVO.setQrRedirectUrl(jsApiRespBody.getQrRespParamInfo().getQrRedirectUrl());
            }
            default -> throw new ServerException("不支持的支付方式");
        }
        return easypayPrePayVO;
    }

    /**
     * 查询支付结果
     *
     * @param orderId
     * @return
     * @throws ServerException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public EasypayTransResultVO paymentQuery(Long orderId) throws ServerException {
        Order order = orderService.getById(orderId);
        Assert.notNull(order, () -> new ServiceException("订单不存在"));
        EasypayTransResultVO paymentResultVO = EasypayTransResultVO.builder()
            .orderId(order.getId())
            .payId(order.getPayId())
            .totalAmount(order.getTotalAmount())
            .transState(TransState.PENDING.getCode())
            .build();
        PayOrder payOrder = payOrderMapper.selectById(order.getPayId());
        if (payOrder == null) {
            return paymentResultVO;
        }
        paymentResultVO.setTransState(TransState.PAYMENT.getCode());
        EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader();
        TradeQueryReqBody queryReqBody = TradeQueryReqBody.builder()
            .reqInfo(ReqInfo.builder().mchtCode(easypayConfig.getMchtCode()).build())
            .payInfo(PayInfo.builder().transDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, new Date())).build())
            .reqOrderInfo(TradeQueryReqBody.ReqOrderInfo.builder()
                .orgTrace(StrBuilder.create(TRACE_PREFIX).append(SnowFlake.getIdStr()).toString())
                .oriOrgTrace(String.valueOf(order.getPayId()))
                .oriTransDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, payOrder.getStartTransDate()))
                .build())
            .build();
        String reqSign = getSignStr(reqHeader, queryReqBody);
        EasyPayRequest easyRequest = EasyPayRequest.builder()
            .reqHeader(reqHeader)
            .reqBody(queryReqBody)
            .reqSign(reqSign)
            .build();
        log.debug("调用易生查询支付接口请求:{}", JSONObject.toJSONString(easyRequest));
        String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/tradeQuery").toString();
        String body = HttpRequest.post(url)
            .timeout(3000)
            .body(JSON.toJSONString(easyRequest))
            .execute()
            .body();
        log.debug("调用易生查询支付接口响应:{}", body);
        EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class);
        if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) {
            verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign());
            TradeQueryRespBody tradeQueryRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), TradeQueryRespBody.class);
            RespStateInfo respStateInfo = tradeQueryRespBody.getRespStateInfo();
            if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) {
                // 支付完成 进入终态
                if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK, RSP_BODY_TRANS_OK_WETCAT, RSP_BODY_TRANS_PARTIALLY_OK)) {
                    TradeQueryRespOrderInfo respOrderInfo = tradeQueryRespBody.getRespOrderInfo();
                    // 更新支付订单信息到数据库
                    PayOrder payOrderUpdate = PayOrder.builder()
                        .id(payOrder.getId())
                        .endTransDate(DateUtil.parse(StrBuilder.create(respOrderInfo.getDateEnd()).append(respOrderInfo.getTimeEnd()).toString()))
                        .easypayTrace(respOrderInfo.getOutTrace())
                        .pcTrace(respOrderInfo.getPcTrace())
                        .unTrace(respOrderInfo.getUnTrace())
                        .transState(TransState.PAID.getCode())
                        .build();
                    payOrderMapper.updateById(payOrderUpdate);
                    orderService.updateById(Order.builder()
                        .id(order.getId())
                        .status(OrderStatusEnum.PAID.getValue())
                        .build());
                    paymentResultVO.setTransState(TransState.PAID.getCode());
                }
            } else {
                log.error("查询支付结果失败:{}", respStateInfo.getRespDesc());
                throw new ServiceException("查询支付结果失败:" + respStateInfo.getRespDesc());
            }
        } else {
            log.error("查询支付结果通讯失败:{}", easyPayResponse.getRspHeader().getRspInfo());
            throw new ServiceException("查询支付结果通讯失败:" + easyPayResponse.getRspHeader().getRspInfo());
        }
        return paymentResultVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void handleTradeCallback(EasyPayRequest easyPayRequest) {
        log.debug("易生支付结果通知回调:{}", JSONObject.toJSONString(easyPayRequest));
        verify(easyPayRequest.getReqHeader(), easyPayRequest.getReqBody(), easyPayRequest.getReqSign());
        TradeQueryRespBody tradeQueryRespBody = BeanUtil.toBean(easyPayRequest.getReqBody(), TradeQueryRespBody.class);
        RespStateInfo respStateInfo = tradeQueryRespBody.getRespStateInfo();
        if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) {
            TradeQueryRespOrderInfo respOrderInfo = tradeQueryRespBody.getRespOrderInfo();
            PayOrder payOrder = payOrderMapper.selectById(String.valueOf(respOrderInfo.getOrgTrace()));
            if (payOrder != null) {
                Order order = orderService.getById(payOrder.getOrderId());
                if (order != null) {
                    // 支付完成 进入终态
                    if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK, RSP_BODY_TRANS_OK_WETCAT, RSP_BODY_TRANS_PARTIALLY_OK)) {
                        // 更新支付订单信息
                        payOrderMapper.updateById(PayOrder.builder()
                            .id(payOrder.getId())
                            .endTransDate(DateUtil.parse(StrBuilder.create(respOrderInfo.getDateEnd()).append(respOrderInfo.getTimeEnd()).toString()))
                            .easypayTrace(respOrderInfo.getOutTrace())
                            .pcTrace(respOrderInfo.getPcTrace())
                            .unTrace(respOrderInfo.getUnTrace())
                            .transState(TransState.PAID.getCode())
                            .build());
                        // 更新订单信息
                        orderService.updateById(Order.builder()
                            .id(order.getId())
                            .status(OrderStatusEnum.PAID.getValue())
                            .paymentTime(LocalDateTimeUtil.parse(StrBuilder.create(respOrderInfo.getDateEnd()).append(respOrderInfo.getTimeEnd()).toString(), "yyyyMMddHHmmss"))
                            .payType(PayType.getByValue(payOrder.getPayType()).getChannel())
                            .build());
                    } else {
                        // 支付失败，支付状态重置为待支付
                        payOrderMapper.updateById(PayOrder.builder().id(payOrder.getId()).transState(TransState.PENDING.getCode()).build());
                    }
                } else {
                    log.warn("回调通知的订单不存在:{}", respOrderInfo.getOrgTrace());
                }
            } else {
                log.warn("回调通知的支付订单不存在:{}", respOrderInfo.getOrgTrace());
            }

        } else {
            log.warn("回调通知支付结果异常:{}", respStateInfo.getRespDesc());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refund(RefundBO refundBO) {
        Long orderId = refundBO.getOrderId();
        Order order = orderService.getById(orderId);
        Assert.notNull(order, () -> new ServiceException("订单不存在"));
        PayOrder payOrder = payOrderMapper.selectById(order.getPayId());
        Assert.notNull(payOrder, () -> new ServiceException("订单不存在"));
        Assert.isTrue(payOrder.getTransState() == TransState.PAID.getCode(), () -> new ServiceException("订单未支付，不可发起退款"));
        // 订单已支付或已分账状态才可发起退款
        boolean orderDivided = Objects.equals(order.getStatus(), OrderStatusEnum.PAID.getValue()) || Objects.equals(order.getStatus(), OrderStatusEnum.DIVIDED.getValue());
        // 更新商品订单和支付订单状态为退款中
        orderService.updateById(Order.builder().id(orderId).status(OrderStatusEnum.REFUNDED.getValue()).build());
        payOrderMapper.updateById(PayOrder.builder().id(payOrder.getId()).transState(TransState.REFUND_PENDING.getCode()).build());
        refundBO.setOrder(order);
        List<OrderItem> orderItems = orderItemService.findByOrderId(orderId);
        BigDecimal refundAmount = BigDecimal.ZERO;
        List<RefundBO.RefundItemBO> refundItemBoList = refundBO.getRefundItemBoList();
        for (RefundBO.RefundItemBO refundItemBO : refundItemBoList) {
            Long orderItemId = refundItemBO.getOrderItemId();
            OrderItem orderItem = orderItems.stream().filter(item -> item.getId().equals(orderItemId)).findFirst().orElse(null);
            Assert.notNull(orderItem, () -> new ServiceException("订单项不存在"));
            Assert.isTrue(refundItemBO.getRefundAmount().compareTo(orderItem.getSalePrice()) <= 0, () -> new ServiceException("订单项退款金额超出支付金额"));
            refundAmount = refundAmount.add(refundItemBO.getRefundAmount());
            // 已分账的订单需要先回退订单子项的分账在退款
            if (orderDivided) {
                Divide divide = divideMapper.selectOne(Wrappers.lambdaQuery(Divide.class)
                    .eq(Divide::getOrderId, orderId)
                    .eq(Divide::getOrderItemId, orderItemId)
                    .eq(Divide::getStatus, DivideStatus.SUCCESS.getCode())
                    .last("limit 1"));
                Assert.notNull(divide, () -> new ServiceException("订单项未分账"));
                List<DivideDetail> detailList = divideDetailMapper.selectList(Wrappers.lambdaQuery(DivideDetail.class).eq(DivideDetail::getDivideId, divide.getId()));
                Assert.notEmpty(detailList, () -> new ServiceException("未发现订单项分账明细"));
                //如果退款金额小于实际订单子项金额，回退商家的分账即可
                if (refundItemBO.getRefundAmount().compareTo(divide.getOrderItemMoney()) < 0) {
                    DivideDetail merchantDivideDetail = detailList.stream().filter(detail -> detail.getType() == TenantType.MERCHANT.getType())
                        .findFirst().orElseThrow(() -> new ServiceException("商家分账信息丢失"));
                    SeparateRefundBO separateRefundBO = SeparateRefundBO.builder()
                        .payType(payOrder.getPayType())
                        .orgTrace(StrBuilder.create(TRACE_PREFIX).append(SnowFlake.getIdStr()).toString())
                        .oriSeparateBatchTrace(divide.getSeparateBatchTrace())
                        .oriSeparateTrade(merchantDivideDetail.getSeparateTrade())
                        .oriTransDate(LocalDateTimeUtil.format(merchantDivideDetail.getSeparateTime(), "yyyyMMdd"))
                        .refundAmount(refundItemBO.getRefundAmount().multiply(BigDecimal.valueOf(100)).longValue())
                        .build();
                    separateRefund(separateRefundBO);
                } else {
                    // 退款金额等于实际订单子项金额，则回退全部的分账。
                    detailList.forEach(detail -> {
                        // 分账金额(分) = 订单子项实际销售金额（元） * 100 * 分账比例（百分比值 / 100 ） = 订单子项实际销售金额（元） * 分账比例百分比值
                        long amount = orderItem.getSalePrice().multiply(BigDecimal.valueOf(detail.getMoneyPercent())).longValue();
                        SeparateRefundBO separateRefundBO = SeparateRefundBO.builder()
                            .payType(payOrder.getPayType())
                            .orgTrace(StrBuilder.create(TRACE_PREFIX).append(SnowFlake.getIdStr()).toString())
                            .oriSeparateBatchTrace(divide.getSeparateBatchTrace())
                            .oriSeparateTrade(detail.getSeparateTrade())
                            .oriTransDate(LocalDateTimeUtil.format(detail.getSeparateTime(), "yyyyMMdd"))
                            .refundAmount(amount)
                            .build();
                        separateRefund(separateRefundBO);
                    });
                }
            }
        }
        refundBO.setTotalRefundAmount(refundAmount);
        EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader();
        String orgTrace = StrBuilder.create(TRACE_PREFIX).append(SnowFlake.getIdStr()).toString();
        RefundApplyReqBody refundApplyReqBody = RefundApplyReqBody.builder()
            .reqInfo(ReqInfo.builder().mchtCode(easypayConfig.getMchtCode()).build())
            .payInfo(PayInfo.builder().transDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, new Date())).build())
            .reqOrderInfo(RefundReqOrderInfo.builder()
                .orgTrace(orgTrace)
                .oriOrgTrace(String.valueOf(order.getPayId()))
                .oriTransDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, payOrder.getEndTransDate()))
                .refundAmount(refundAmount.multiply(BigDecimal.valueOf(100)).longValue())
                .build())
            .build();
        refundBO.setOrgTrace(orgTrace);
        String reqSign = getSignStr(reqHeader, refundApplyReqBody);
        EasyPayRequest easyRequest = EasyPayRequest.builder()
            .reqHeader(reqHeader)
            .reqBody(refundApplyReqBody)
            .reqSign(reqSign)
            .build();
        log.debug("调用易生实时退款接口请求:{}", JSONObject.toJSONString(easyRequest));
        String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/refund/apply").toString();
        String body = HttpRequest.post(url)
            .timeout(3000)
            .body(JSON.toJSONString(easyRequest))
            .execute()
            .body();
        log.debug("调用易实时退款接口响应:{}", body);
        EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class);
        if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) {
            verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign());
            RefundApplyRespBody refundApplyRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), RefundApplyRespBody.class);
            RespStateInfo respStateInfo = refundApplyRespBody.getRespStateInfo();
            if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) {
                if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK)) {
                    // 更新支付单退款状态
                    payOrderMapper.updateById(PayOrder.builder().id(payOrder.getId()).transState(TransState.REFUNDED.getCode()).refundDate(new Date()).build());
                    orderService.refund(refundBO);
                }
            } else {
                log.error("易生退款失败:{}", respStateInfo.getRespDesc());
                payOrderMapper.updateById(PayOrder.builder().id(payOrder.getId()).transState(TransState.PAID.getCode()).build());
                throw new ServiceException("易生退款失败:" + respStateInfo.getRespDesc());
            }
        } else {
            log.error("易生退款通讯失败:{}", easyPayResponse.getRspHeader().getRspInfo());
            payOrderMapper.updateById(PayOrder.builder().id(payOrder.getId()).transState(TransState.PAID.getCode()).build());
            throw new ServiceException("易生退款通讯失败:" + easyPayResponse.getRspHeader().getRspInfo());
        }
    }

    @Override
    public EasypayTransResultVO refundQuery(Long orderId) {
        Order order = orderService.getById(orderId);
        Assert.notNull(order, () -> new ServiceException("订单不存在"));
        PayOrder payOrder = payOrderMapper.selectById(order.getPayId());
        Assert.notNull(payOrder, () -> new ServiceException("订单不存在"));
        EasypayTransResultVO easypayTransResultVO = EasypayTransResultVO.builder()
            .orderId(order.getId())
            .payId(payOrder.getId())
            .totalAmount(order.getTotalAmount())
            .transState(payOrder.getTransState())
            .build();
        if (payOrder.getTransState() == TransState.REFUNDED.getCode()) {
            return easypayTransResultVO;
        }
        Aftersale aftersale = aftersaleService.getOne(Wrappers.lambdaQuery(Aftersale.class)
            .eq(Aftersale::getOrderId, orderId).orderByDesc(Aftersale::getCreateTime).last("limit 1"));
        Assert.notNull(aftersale, () -> new ServiceException("订单无售后单"));
        Assert.isTrue(payOrder.getTransState() == TransState.PAID.getCode() || payOrder.getTransState() == TransState.REFUND_PENDING.getCode(), () -> new ServiceException("订单未支付或退款中"));
        EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader();
        RefundApplyReqBody refundApplyReqBody = RefundApplyReqBody.builder()
            .reqInfo(ReqInfo.builder().mchtCode(easypayConfig.getMchtCode()).build())
            .payInfo(PayInfo.builder().transDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, new Date())).build())
            .reqOrderInfo(RefundReqOrderInfo.builder()
                .orgTrace(StrBuilder.create(TRACE_PREFIX).append(SnowFlake.getIdStr()).toString())
                .oriOrgTrace(aftersale.getOrgTrace())
                .oriOutTrace(String.valueOf(order.getPayId()))
                .oriTransDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, payOrder.getEndTransDate()))
                .refundAmount(payOrder.getTransAmount())
                .build())
            .build();
        String reqSign = getSignStr(reqHeader, refundApplyReqBody);
        EasyPayRequest easyRequest = EasyPayRequest.builder()
            .reqHeader(reqHeader)
            .reqBody(refundApplyReqBody)
            .reqSign(reqSign)
            .build();
        log.debug("调用易生实时退款查询接口请求:{}", JSONObject.toJSONString(easyRequest));
        String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/refund/query").toString();
        String body = HttpRequest.post(url)
            .timeout(3000)
            .body(JSON.toJSONString(easyRequest))
            .execute()
            .body();
        log.debug("调用易实时退款查询接口响应:{}", body);
        EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class);
        if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) {
            verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign());
            RefundApplyRespBody refundApplyRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), RefundApplyRespBody.class);
            RespStateInfo respStateInfo = refundApplyRespBody.getRespStateInfo();
            if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) {
                if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK)) {
                    easypayTransResultVO.setTransState(TransState.REFUNDED.getCode());
                    // 更新支付单退款状态
                    payOrderMapper.updateById(PayOrder.builder().id(payOrder.getId()).transState(TransState.REFUNDED.getCode()).build());
                }
            } else {
                log.error("查询支付结果失败:{}", respStateInfo.getRespDesc());
                throw new ServiceException("查询支付结果失败:" + respStateInfo.getRespDesc());
            }
        } else {
            log.error("查询支付结果通讯失败:{}", easyPayResponse.getRspHeader().getRspInfo());
            throw new ServiceException("查询支付结果通讯失败:" + easyPayResponse.getRspHeader().getRspInfo());
        }
        return easypayTransResultVO;
    }

    @Override
    public SeparateApplyBO separateApply(SeparateApplyBO separateApplyBO) {
        EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader();
        List<SeparateInfo.SeparateOrderDetailList> separateOrderDetailListList = separateApplyBO.getSeparateItemBOList().stream().map(item -> BeanUtil.copyProperties(item, SeparateInfo.SeparateOrderDetailList.class)).toList();
        SeparateApplyReqBody separateApplyReqBody = SeparateApplyReqBody.builder()
            .reqInfo(ReqInfo.builder().mchtCode(easypayConfig.getMchtCode()).build())
            .payInfo(PayInfo.builder().transDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, new Date())).build())
            .reqOrderInfo(SeparateReqOrderInfo.builder()
                .oriOrgTrace(String.valueOf(separateApplyBO.getPayId()))
                .oriTransDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, separateApplyBO.getTransDate()))
                .separateBatchTrace(separateApplyBO.getSeparateBatchTrace())
                .transSumAmt(separateApplyBO.getTransSumAmt())
                .transSumCount(separateApplyBO.getSeparateItemBOList().size())
                .separateOrderDetailList(separateOrderDetailListList)
                .build())
            .build();
        String reqSign = getSignStr(reqHeader, separateApplyReqBody);
        EasyPayRequest easyRequest = EasyPayRequest.builder()
            .reqHeader(reqHeader)
            .reqBody(separateApplyReqBody)
            .reqSign(reqSign)
            .build();
        log.debug("调用易生请求分账接口请求:{}", JSONObject.toJSONString(easyRequest));
        divideMapper.updateStatus(separateApplyBO.getDivideId(), DivideStatus.PENDING.getCode());
        String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/separate/orderApply").toString();
        String body = HttpRequest.post(url)
            .timeout(3000)
            .body(JSON.toJSONString(easyRequest))
            .execute()
            .body();
//        String body = "{\"rspBody\":{\"respStateInfo\":{\"respDesc\":\"结算域处理成功\",\"transStatusDesc\":\"成功\",\"transState\":\"0\",\"respCode\":\"000000\"},\"respOrderInfo\":{\"separateBatchTrace\":\"1966401173213224961\",\"batchNum\":2,\"separateRespInfoList\":[{\"sepaStatusDesc\":\"分账成功\",\"separateTrade\":\"WZJ_1966401200823730176\",\"sepaFeeRatio\":950000,\"sepaTransAmount\":95,\"sepaPlatStlmAmount\":95,\"sepaStatus\":\"0\",\"sepaStlmAmount\":94,\"sepaRatio\":950000,\"receiveMchtCode\":\"KK0000000000985\"},{\"sepaStatusDesc\":\"分账成功\",\"separateTrade\":\"WZJ_1966401200882450432\",\"sepaFeeRatio\":50000,\"sepaTransAmount\":5,\"sepaPlatStlmAmount\":5,\"sepaStatus\":\"0\",\"sepaStlmAmount\":5,\"sepaRatio\":50000,\"receiveMchtCode\":\"631000000003325\"}],\"productTrace\":\"YQ20250912151915270199\",\"batchTransAmount\":100}},\"rspHeader\":{\"easyPayCertificateId\":\"00000000\",\"rspCode\":\"000000\",\"rspInfo\":\"SUCCESS\"},\"rspSign\":\"bFVXEDsVM1oSZxyigMFRQgK2LOee+sy2OiyTjdMkfL58NSKk2VKaH6WCnedassTY5k809+wlqI0smKOhCNCjZz+Z7E6p4dx3uOMtRdHTyYZ07o2qmy3EUOFcDBmfCiSJPEKXcRsRXMjipzvFKLFsNY+W03HdxBswXefcFUSVo4YZ9s+FGQpISjtr0aQiJgMk8mDFbqm6ocYvnbCpnu594YgJFVY7+rQNknY1M7RLTWlyUMiZq6rvDuM+Ii8sHJS7npnbhwLwYDNQKYmcTav2GckVPxlVPsFnlaDAMyObziYVpqL6xLA8A3ourxgpWxgffQuz6IkD0KwKEO1WQ1jjOw==\"}";
        log.debug("调用易生请求分账接口响应:{}", body);
        EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class);
        if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) {
            verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign());
            SeparateApplyRespBody separateApplyRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), SeparateApplyRespBody.class);
            RespStateInfo respStateInfo = separateApplyRespBody.getRespStateInfo();
            if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) {
                if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK)) {
                    // 更新分账状态
                    SeparateRespOrderInfo respOrderInfo = separateApplyRespBody.getRespOrderInfo();
                    handleSeparateInfo(separateApplyBO, respOrderInfo);
                }
            } else {
                log.error("请求分账失败:{}", respStateInfo.getRespDesc());
                throw new ServiceException("请求分账失败:" + respStateInfo.getRespDesc());
            }
        } else {
            log.error("易生请求分账通讯失败:{}", easyPayResponse.getRspHeader().getRspInfo());
            throw new ServiceException("易生请求分账通讯失败:" + easyPayResponse.getRspHeader().getRspInfo());
        }
        return separateApplyBO;
    }


    /**
     * 处理分账结果信息，更新分账详情表
     *
     * @param respOrderInfo
     */
    private void handleSeparateInfo(SeparateApplyBO separateApplyBO, SeparateRespOrderInfo respOrderInfo) {
        Map<String, SeparateItemBO> separateItemBOMap = separateApplyBO.getSeparateItemBOList().stream().collect(Collectors.toMap(SeparateItemBO::getSeparateTrade, o -> o));
        List<DivideDetail> updateDetailList = new ArrayList<>();
        List<SeparateRespInfoList> separateRespInfoList = respOrderInfo.getSeparateRespInfoList();
        separateRespInfoList.forEach(separateRespInfo -> {
            String separateTrade = separateRespInfo.getSeparateTrade();
            SeparateItemBO separateItemBO = separateItemBOMap.get(separateTrade);
            DivideDetail divideDetail = DivideDetail.builder()
                .id(separateItemBO.getDivideDetailId())
                .separateTrade(separateRespInfo.getSeparateTrade())
                .separateTime(LocalDateTime.now())
                .build();
            divideDetail.setMoney(BigDecimal.valueOf(separateRespInfo.getSepaTransAmount()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
            divideDetail.setFee(BigDecimal.valueOf(separateRespInfo.getSepaPlatStlmAmount() - separateRespInfo.getSepaStlmAmount()));
            SepaStatus sepaStatus = SepaStatus.getByValue(separateRespInfo.getSepaStatus());
            switch (sepaStatus) {
                case SUCCESS -> divideDetail.setStatus(DivideStatus.SUCCESS.getCode());
                case FAIL -> divideDetail.setStatus(DivideStatus.FAIL.getCode());
                case PENDING -> divideDetail.setStatus(DivideStatus.PENDING.getCode());
                case REFUNDED -> divideDetail.setStatus(DivideStatus.REFUNDED.getCode());
                case PROCESSING -> divideDetail.setStatus(DivideStatus.PROCESSING.getCode());
                default -> divideDetail.setStatus(DivideStatus.PROCESSING.getCode());
            }
            updateDetailList.add(divideDetail);
        });
        divideDetailMapper.updateBatchById(updateDetailList);
        long itemOkCount = updateDetailList.stream().filter(item -> item.getStatus() == DivideStatus.SUCCESS.getCode())
            .count();
        long totalSeparate = separateRespInfoList.stream().mapToLong(SeparateRespInfoList::getSepaTransAmount).sum();
        Divide divideUpdate = Divide.builder()
            .id(separateApplyBO.getDivideId())
            .actualMoney(BigDecimal.valueOf(totalSeparate).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP))
            .status(DivideStatus.PROCESSING.getCode()).build();
        if (itemOkCount == separateItemBOMap.size()) {
            divideUpdate.setStatus(DivideStatus.SUCCESS.getCode());
            orderService.updateById(Order.builder()
                .id(separateApplyBO.getOrderId())
                .status(OrderStatusEnum.DIVIDED.getValue())
                .build());
        }
        divideMapper.updateById(divideUpdate);
    }


    /**
     * 上传图片至易生
     */
    @Override
    public String merchantPicUpload(MultipartFile pic) throws IOException {
        String originalFilename = pic.getOriginalFilename();
        String base64Image = Base64.encode(pic.getBytes());
        EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader();
        MerchantPicUploadReqBody merchantPicUploadReqBody = MerchantPicUploadReqBody.builder()
            .fileText(base64Image)
            .fileName(originalFilename)
            .build();
        String reqSign = getSignStr(reqHeader, merchantPicUploadReqBody);
        EasyPayRequest easyRequest = EasyPayRequest.builder()
            .reqHeader(reqHeader)
            .reqBody(merchantPicUploadReqBody)
            .reqSign(reqSign)
            .build();
        log.debug("调用易生上传图片接口请求:{}", JSONObject.toJSONString(easyRequest));
        String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/merchant/member/pictureUpload").toString();
        String body = HttpRequest.post(url)
            .timeout(3000)
            .body(JSON.toJSONString(easyRequest))
            .execute()
            .body();
        log.debug("调用易生上传图片接口响应:{}", body);
        EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class);
        if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) {
            verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign());
            MerchantPicUploadRespBody merchantPicUploadRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), MerchantPicUploadRespBody.class);
            if (StrUtil.equals(RSP_BODY_RESP_OK, merchantPicUploadRespBody.getRetCode())) {
                // 绑定分账商户
                return merchantPicUploadRespBody.getFileId();
            } else {
                log.error("上传图片失败:{}", merchantPicUploadRespBody.getRetMsg());
                throw new ServiceException("上传图片户失败:" + merchantPicUploadRespBody.getRetMsg());
            }
        } else {
            log.error("易生上传图片通讯失败:{}", easyPayResponse.getRspHeader().getRspInfo());
            throw new ServiceException("易生上传图片通讯失败:" + easyPayResponse.getRspHeader().getRspInfo());
        }
    }

    /**
     * 添加分账商户
     */
    @Override
    public void merchantAdd(MerchantAddBO merchantAddBO) {
        EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader();
        PayAccInfo payAccInfo = PayAccInfo.builder().build();
        BeanUtil.copyProperties(merchantAddBO, payAccInfo);
        MerchantAddApplyReqBody merchantAddApplyReqBody = MerchantAddApplyReqBody.builder()
            .applyInfo(ApplyInfo.builder().applyNo(StrBuilder.create(TRACE_PREFIX).append(SnowFlake.getIdStr()).toString()).build())
            .merchantInfo(MerchantInfo.builder().mchtName(merchantAddBO.getMchtName()).build())
            .pictureList(PictureList.buildPictureList(merchantAddBO.getFrontIdCard(), merchantAddBO.getBackIdCard()))
            .payAccInfo(payAccInfo)
            .build();
        String reqSign = getSignStr(reqHeader, merchantAddApplyReqBody);
        EasyPayRequest easyRequest = EasyPayRequest.builder()
            .reqHeader(reqHeader)
            .reqBody(merchantAddApplyReqBody)
            .reqSign(reqSign)
            .build();
        log.debug("调用易生添加分账商户接口请求:{}", JSONObject.toJSONString(easyRequest));
        String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/merchant/nonmember/add").toString();
        String body = HttpRequest.post(url)
            .timeout(10000)
            .body(JSON.toJSONString(easyRequest))
            .execute()
            .body();
        log.debug("调用易生添加分账商户接口响应:{}", body);
        EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class);
        if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) {
            verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign());
            MerchantAddApplyRespBody merchantAddApplyRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), MerchantAddApplyRespBody.class);
            if (StrUtil.equals(RSP_BODY_RESP_OK, merchantAddApplyRespBody.getRetCode())) {
                // 绑定分账商户
                merchantBinding(merchantAddApplyRespBody.getMchtCode());
            } else {
                log.error("添加分账商户失败:{}", merchantAddApplyRespBody.getRetMsg());
                throw new ServiceException("添加分账商户失败:" + merchantAddApplyRespBody.getRetMsg());
            }
        } else {
            log.error("易生添加分账商户通讯失败:{}", easyPayResponse.getRspHeader().getRspInfo());
            throw new ServiceException("易生添加分账商户通讯失败:" + easyPayResponse.getRspHeader().getRspInfo());
        }
    }

    /**
     * 绑定分账商户
     *
     * @param mchtCode
     */
    @Override
    public void merchantBinding(String mchtCode) {
        EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader();
        MerchantBindingReqBody merchantBindingReqBody = MerchantBindingReqBody.builder()
            .applyNo(StrBuilder.create(TRACE_PREFIX).append(SnowFlake.getIdStr()).toString())
            .customerCode(mchtCode)
            .mchtCode(easypayConfig.getMchtCode())
            .build();
        String reqSign = getSignStr(reqHeader, merchantBindingReqBody);
        EasyPayRequest easyRequest = EasyPayRequest.builder()
            .reqHeader(reqHeader)
            .reqBody(merchantBindingReqBody)
            .reqSign(reqSign)
            .build();
        log.debug("调用易生绑定分账商户接口请求:{}", JSONObject.toJSONString(easyRequest));
        String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/merchant/member/relationshipBinding").toString();
        String body = HttpRequest.post(url)
            .timeout(3000)
            .body(JSON.toJSONString(easyRequest))
            .execute()
            .body();
        log.debug("调用易生绑定分账商户接口响应:{}", body);
        EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class);
        if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) {
            verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign());
            MerchantAddApplyRespBody merchantAddApplyRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), MerchantAddApplyRespBody.class);
            if (StrUtil.equals(RSP_BODY_RESP_OK, merchantAddApplyRespBody.getRetCode())) {
                log.info("绑定商户[{}]成功:{}", mchtCode, merchantAddApplyRespBody.getRetMsg());
            } else {
                log.error("绑定商户失败:{}", merchantAddApplyRespBody.getRetMsg());
                throw new ServiceException("绑定商户失败:" + merchantAddApplyRespBody.getRetMsg());
            }
        } else {
            log.error("易生绑定商户通讯失败:{}", easyPayResponse.getRspHeader().getRspInfo());
            throw new ServiceException("易生绑定商户通讯失败:" + easyPayResponse.getRspHeader().getRspInfo());
        }
    }

    @Override
    public SeparateRespOrderInfo separateqQuery(Long orderItemId) {
        SeparateRespOrderInfo separateRespOrderInfo = null;
        OrderItem orderItem = orderItemService.getById(orderItemId);
        Assert.notNull(orderItem, () -> new ServiceException("订单子项不存在"));
        Divide divide = divideMapper.selectOne(Wrappers.lambdaQuery(Divide.class).eq(Divide::getOrderItemId, orderItemId).last("limit 1"));
        Assert.notNull(divide, () -> new ServiceException("订单子项未分账"));
        EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader();
        SeparateQueryReqBody queryReqBody = SeparateQueryReqBody.builder()
            .reqInfo(ReqInfo.builder().mchtCode(easypayConfig.getMchtCode()).build())
            .payInfo(PayInfo.builder().transDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, new Date())).build())
            .reqOrderInfo(SeparateQueryReqBody.ReqOrderInfo.builder()
                .oriSeparateBatchTrace(divide.getSeparateBatchTrace())
                .oriTransDate(LocalDateTimeUtil.format(divide.getUpdateTime(), "yyyyMMdd"))
                .build())
            .build();
        String reqSign = getSignStr(reqHeader, queryReqBody);
        EasyPayRequest easyRequest = EasyPayRequest.builder()
            .reqHeader(reqHeader)
            .reqBody(queryReqBody)
            .reqSign(reqSign)
            .build();
        log.debug("调用易生查询分账接口请求:{}", JSONObject.toJSONString(easyRequest));
        String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/separate/query").toString();
        String body = HttpRequest.post(url)
            .timeout(3000)
            .body(JSON.toJSONString(easyRequest))
            .execute()
            .body();
        log.debug("调用易生查询分账接口响应:{}", body);
        EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class);
        if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) {
            verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign());
            SeparateQueryRespBody separateQueryRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), SeparateQueryRespBody.class);
            RespStateInfo respStateInfo = separateQueryRespBody.getRespStateInfo();
            if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) {
                // 支付完成 进入终态
                if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK, RSP_BODY_TRANS_OK_WETCAT, RSP_BODY_TRANS_PARTIALLY_OK)) {
                    SeparateRespOrderInfo respOrderInfo = separateQueryRespBody.getRespOrderInfo();
                    respOrderInfo.setSeparateBatchTrace(divide.getSeparateBatchTrace());
                    // TODO 处理
//                    handleSeparateInfo(null,respOrderInfo);
                    separateRespOrderInfo = respOrderInfo;
                }
            } else {
                log.error("查询支付结果失败:{}", respStateInfo.getRespDesc());
                throw new ServiceException("查询支付结果失败:" + respStateInfo.getRespDesc());
            }
        } else {
            log.error("查询支付结果通讯失败:{}", easyPayResponse.getRspHeader().getRspInfo());
            throw new ServiceException("查询支付结果通讯失败:" + easyPayResponse.getRspHeader().getRspInfo());
        }
        return separateRespOrderInfo;
    }

    /**
     * 分账回退
     *
     * @param separateRefundBO
     */
    @Override
    public boolean separateRefund(SeparateRefundBO separateRefundBO) {
        boolean flag = false;
        EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader();
        SeparateRefundReqBody separateApplyReqBody = SeparateRefundReqBody.builder()
            .reqInfo(ReqInfo.builder().mchtCode(easypayConfig.getMchtCode()).build())
            .payInfo(PayInfo.builder().transDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, new Date())).payType(separateRefundBO.getPayType()).build())
            .reqOrderInfo(BeanUtil.copyProperties(separateRefundBO, SeparateRefundReqBody.ReqOrderInfo.class))
            .build();
        String reqSign = getSignStr(reqHeader, separateApplyReqBody);
        EasyPayRequest easyRequest = EasyPayRequest.builder()
            .reqHeader(reqHeader)
            .reqBody(separateApplyReqBody)
            .reqSign(reqSign)
            .build();
        log.debug("调用易生分账回退接口请求:{}", JSONObject.toJSONString(easyRequest));
        String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/separate/cancel").toString();
        String body = HttpRequest.post(url)
            .timeout(3000)
            .body(JSON.toJSONString(easyRequest))
            .execute()
            .body();
        log.debug("调用易生分账回退接口响应:{}", body);
        EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class);
        if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) {
            verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign());
            SeparateRefundRespBody separateRefundRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), SeparateRefundRespBody.class);
            RespStateInfo respStateInfo = separateRefundRespBody.getRespStateInfo();
            if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) {
                if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK)) {
                    flag = true;
                }
            } else {
                log.error("分账回退失败:{}", respStateInfo.getRespDesc());
                throw new ServiceException("分账回退失败:" + respStateInfo.getRespDesc());
            }
        } else {
            log.error("易生分账回退通讯失败:{}", easyPayResponse.getRspHeader().getRspInfo());
            throw new ServiceException("易生分账回退通讯失败:" + easyPayResponse.getRspHeader().getRspInfo());
        }
        return flag;
    }


    @Override
    public EasypayAccountVO getEasypayAccount(Long memberId) {
        return EasypayAccountVO.builder().balance(new BigDecimal(1000)).build();
    }

    @Override
    public boolean withdraw(Long memberId, BigDecimal money) {
        return true;
    }

    @Override
    public boolean syncMemberAccount() {
        return true;
    }

    @Override
    public void openMerchant(Long memberId) {

    }

    @Override
    public EasypayAccountVO getMerchantAccount(Long memberId) {
        return null;
    }

    @Override
    public boolean split(Long memberId, BigDecimal money) {
        return false;
    }

    @Override
    public boolean merchantAuditNotice(String request) {
        return false;
    }


}
