package com.huiju.estateagents.redpack.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huiju.estateagents.base.ResponseBean;
import com.huiju.estateagents.center.sysUser.entity.SysUser;
import com.huiju.estateagents.center.sysUser.mapper.SysUserMapper;
import com.huiju.estateagents.center.taUser.entity.TaUser;
import com.huiju.estateagents.center.taUser.mapper.TaUserMapper;
import com.huiju.estateagents.common.CommConstant;
import com.huiju.estateagents.common.CommonUtils;
import com.huiju.estateagents.common.wxpay.WXPay;
import com.huiju.estateagents.common.wxpay.WXPayConstants;
import com.huiju.estateagents.common.wxpay.WxConfig;
import com.huiju.estateagents.entity.TaWxPayConfig;
import com.huiju.estateagents.excel.redPacket.AccountConsumeRecordExport;
import com.huiju.estateagents.excel.redPacket.AccountPayRecordExport;
import com.huiju.estateagents.excel.redPacket.AccountRefundRecordExport;
import com.huiju.estateagents.mapper.TaWxPayConfigMapper;
import com.huiju.estateagents.redpack.entity.*;
import com.huiju.estateagents.redpack.mapper.*;
import com.huiju.estateagents.redpack.service.ITaOrgAccountService;
import com.huiju.estateagents.redpack.service.ITaOrgOrderService;
import com.huiju.estateagents.sample.entity.TaContact;
import com.huiju.estateagents.sample.mapper.TaContactMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * <p>
 * 组织订单表  服务实现类
 * </p>
 *
 * @author fxf
 * @since 2020-06-03
 */
@Slf4j
@Service
public class TaOrgOrderServiceImpl extends ServiceImpl<TaOrgOrderMapper, TaOrgOrder> implements ITaOrgOrderService {

    @Value("${pay-notify}")
    private String payNotify;

    @Autowired
    private TaOrgAccountMapper taOrgAccountMapper;

    @Autowired
    private TaOrgOrderMapper taOrgOrderMapper;

    @Autowired
    private TaOrgOrderDetailMapper taOrgOrderDetailMapper;

    @Autowired
    private TaOrgAccountDetailedListMapper taOrgAccountDetailedListMapper;

    @Autowired
    private TaOrgRefundApplicationMapper taOrgRefundApplicationMapper;

    @Autowired
    private TaOrgAccountBlockedMapper taOrgAccountBlockedMapper;

    @Autowired
    private TaOrgAccountCertificateMapper taOrgAccountCertificateMapper;

    @Autowired
    private TaUserMapper taUserMapper;

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private TaWxPayConfigMapper taWxPayConfigMapper;

    @Autowired
    private TaContactMapper taContactMapper;

    @Autowired
    private ITaOrgAccountService taOrgAccountService;

    @Override
    public IPage<TaOrgOrder> listByCondition(IPage<TaOrgOrder> pg,
                                             String orderType,
                                             Integer orgId,
                                             String tradeNo,
                                             Integer isOffline,
                                             String tradingStatus,
                                             String receivePhone,
                                             String itemType,
                                             String startDate,
                                             String endDate,
                                             String miniAppName,
                                             String auditStatus,
                                             String payStartDate,
                                             String payEndDate) {
        IPage<TaOrgOrder> result = pg;

        if (CommConstant.ITEM_TYPE_RECHARGE.equals(orderType) || CommConstant.ITEM_TYPE_REFUND.equals(orderType)) {
            result = taOrgOrderMapper.orderListByRechargeOrRefund(pg, orderType, orgId, tradeNo, isOffline, tradingStatus, startDate, endDate, miniAppName, auditStatus, payStartDate, payEndDate);
            result.getRecords().forEach(e -> {
                e.setCertificateUrlList(getCertificateByOrderId(e.getOrderId()));
            });
            return result;
        } else if (CommConstant.ITEM_TYPE_REDPACKAGE.equals(orderType)) {
            result = taOrgOrderMapper.orderListByConsume(pg, orderType, orgId, tradeNo, tradingStatus, receivePhone, itemType, startDate, endDate, miniAppName);
            return result;
        } else {
            return result;
        }
    }

    @Override
    public List<RechargeOrder> rechargeListByConditionExport(String itemType, String tradeNo, Boolean isOffline, String tradingStatus, String startDate, String endDate, String miniAppName, Integer orgId) {
        List<RechargeOrder> result = taOrgOrderMapper.rechargeListByConditionExport(itemType, tradeNo, isOffline, tradingStatus, startDate, endDate, miniAppName, orgId);
        return result;
    }

    @Override
    public List<ConsumeOrder> consumeListByConditionExport(String itemType, String tradeNo, String receivePhone, String tradingStatus, String startDate, String endDate, String miniAppName, Integer orgId) {
        List<ConsumeOrder> result = taOrgOrderMapper.orderListByConsumeExport(itemType, tradeNo, receivePhone, tradingStatus, startDate, endDate, miniAppName, orgId);
        return result;
    }

    @Override
    public List<RefundOrder> refundListByConditionExport(String itemType, String tradeNo, String startDate, String endDate, String miniAppName, Integer orgId, String auditStatus) {
        List<RefundOrder> result = taOrgOrderMapper.orderListByRefundExport(itemType, tradeNo, startDate, endDate, miniAppName, orgId, auditStatus);
        return result;
    }

    public List<String> getCertificateByOrderId(String orderId) {
        List<String> urlList = new ArrayList<>();
        QueryWrapper<TaOrgAccountCertificate> taOrgAccountCertificateQueryWrapper = new QueryWrapper<>();
        taOrgAccountCertificateQueryWrapper.eq("order_id", orderId);
        List<TaOrgAccountCertificate> taOrgAccountCertificates = taOrgAccountCertificateMapper.selectList(taOrgAccountCertificateQueryWrapper);
        urlList = taOrgAccountCertificates.stream().map(TaOrgAccountCertificate::getUrl).collect(Collectors.toList());
        return urlList;
    }

    public Integer calculateAvailableAmount(Integer orgId){
        TaOrgAccount taOrgAccount = taOrgAccountMapper.getOrgAccountById(orgId);

        //查询总充值金额
        Integer totalRechargeAmount = taOrgOrderMapper.countTotalRechargeAmount(orgId);

        //查询退款冻结额
        Integer totalBlockAmount = taOrgAccountBlockedMapper.countTotalBlockAmount(orgId);

        //账户余额=总充值金额-已消费金额-已退款额-退款冻结额
        Integer balance = totalRechargeAmount - taOrgAccount.getPurchaseAmount() - taOrgAccount.getTotalRefund() - totalBlockAmount;

        return balance;
    }
    /**
     * 微信支付
     *
     * @param taOrgOrder
     * @param clientIp
     * @return
     */
    @Override
    public ResponseBean payUnifiedOrder(TaOrgOrder taOrgOrder, String clientIp) throws Exception {
        taOrgOrder = taOrgOrderMapper.selectById(taOrgOrder.getOrderId());
        //构建微信配置
        WxConfig config = getWxConfig(0);
        WXPay wxpay = new WXPay(config);

        //获取支付的钱数并且判断是自动锁房还是手动锁房,自动锁房需要锁定房源，手动锁房跳过
        String payPrice = String.valueOf(taOrgOrder.getRealAmount());

        //生成商户订单
//        String tradeNo = CommonUtils.generateTradeNo("CJ");
//        taOrgOrder.setTradeNo(tradeNo);

        //下单
        Map<String, String> data = new HashMap<String, String>();
        data.put("body", "微信支付");
        //商品号唯一
        data.put("out_trade_no", taOrgOrder.getTradeNo());
        data.put("device_info", "WEB");
        data.put("fee_type", "CNY");
        //金额 后台计算
        data.put("total_fee", payPrice);
        //终端IP
        data.put("spbill_create_ip", clientIp);
        //回调地址
        data.put("notify_url", payNotify + CommConstant.ITEM_TYPE_RECHARGE);
        // 此处指定为APP支付
        data.put("trade_type", "NATIVE");
        data.put("product_id", taOrgOrder.getOrderId());
        Map<String, String> resp = wxpay.unifiedOrder(data);
        log.info("下单成功：{}", resp);
        if (resp.get("result_code").equalsIgnoreCase(WXPayConstants.FAIL)) {
            throw new Exception(resp.get("err_code_des"));
        }

        return ResponseBean.success(resp.get("code_url"));
    }

    @Override
    public ResponseBean saveCzOrder(Integer amount, Integer orgId, Integer userId, String ip) throws Exception {
        ResponseBean responseBean = new ResponseBean();

        // 获取下单人名称
        TaUser taUser = taUserMapper.selectById(userId);
        if (taUser == null) {
            responseBean.addError("fail");
            return responseBean;
        }

        // 查询账户信息
        QueryWrapper<TaOrgAccount> orgAccountQueryWrapper = new QueryWrapper<>();
        orgAccountQueryWrapper.eq("org_id", orgId);
        orgAccountQueryWrapper.eq("status", CommConstant.STATUS_NORMAL);
        TaOrgAccount taOrgAccount = taOrgAccountMapper.selectOne(orgAccountQueryWrapper);
        if (taOrgAccount == null) {
            responseBean.addError("账户不存在");
            return responseBean;
        }

        // 计算实付金额
        Integer realAmount = calculateRealAmount(amount);

        // 1.创建订单主表信息
        TaOrgOrder taOrgOrder = new TaOrgOrder();
        taOrgOrder.setOrgId(orgId);
        taOrgOrder.setTradeNo(CommonUtils.generateTradeNo(CommConstant.RECHARGE));
        taOrgOrder.setAmount(amount);
        taOrgOrder.setRealAmount(realAmount);
        taOrgOrder.setCreateDate(LocalDateTime.now());
        taOrgOrder.setExpiryDate(LocalDateTime.now().plusMinutes(30));
        taOrgOrder.setRemark("线上充值");
        taOrgOrder.setStatus(CommConstant.STATUS_NORMAL);
        taOrgOrder.setTradingStatus(CommConstant.TRADING_STATUS_PROCESSING);
        taOrgOrder.setIsOffline(false);
        taOrgOrder.setUserId(userId);
        taOrgOrder.setUserName(taUser.getUserName());
        if (taOrgOrderMapper.insert(taOrgOrder) < 1) {
            responseBean.addError("fail");
            return responseBean;
        }

        // 2.创建订单详情表信息
        TaOrgOrderDetail taOrgOrderDetail = new TaOrgOrderDetail();
        taOrgOrderDetail.setOrderId(taOrgOrder.getOrderId());
        taOrgOrderDetail.setOrgId(orgId);
        taOrgOrderDetail.setItemType(CommConstant.ITEM_TYPE_RECHARGE);
        taOrgOrderDetail.setAmount(amount);
        taOrgOrderDetail.setUnitPrice(amount);
        taOrgOrderDetail.setRealAmount(realAmount);
        taOrgOrderDetail.setQuantity(1);
        taOrgOrderDetail.setStatus(CommConstant.STATUS_NORMAL);
        if (taOrgOrderDetailMapper.insert(taOrgOrderDetail) < 1) {
            taOrgOrderMapper.deleteById(taOrgOrder.getOrderId());
            responseBean.addError("fail");
            return responseBean;
        }

        // 3.请求微信接口获取支付二维码
        ResponseBean payResult = payUnifiedOrder(taOrgOrder, ip);
        if (ResponseBean.CODE_SUCCESS != payResult.getCode()) {
            return payResult;
        }

        // 4.发送短信
        ExecutorService threadPool = Executors.newCachedThreadPool();
        threadPool.execute(() -> {
            List<TaContact> taContacts = taOrgAccountService.selectSmsContactUser();
            if (CollectionUtils.isNotEmpty(taContacts)) {
                TaContact taContact = taContacts.get(0);
                String[] smsParams = {"业务平台充值提醒", taOrgOrder.getTradeNo()};
                // 短信发送
                taOrgAccountService.sendSmsMessage(taContact.getPhone(), CommConstant.ITEM_TYPE_RECHARGE, smsParams);
            }
        });

        JSONObject obj = new JSONObject();
        obj.put("amount", amount);
        obj.put("realAmount", realAmount);
        obj.put("serviceFee", realAmount - amount);
        obj.put("mchPayRate", CommConstant.WXMCH_PAY_RATE_STRING);
        obj.put("payQrCode", payResult.getData());
        obj.put("orderInfo", taOrgOrder);
        responseBean.addSuccess(obj);
        return responseBean;
    }

    @Override
    public ResponseBean saveRefundOrder(Integer amount, Integer orgId, Integer userId) {
        ResponseBean responseBean = new ResponseBean();

        // 获取下单人名称
        TaUser taUser = taUserMapper.selectById(userId);
        if (taUser == null) {
            responseBean.addError("fail");
            return responseBean;
        }

        // 查询账户信息
        QueryWrapper<TaOrgAccount> orgAccountQueryWrapper = new QueryWrapper<>();
        orgAccountQueryWrapper.eq("org_id", orgId);
        orgAccountQueryWrapper.eq("status", CommConstant.STATUS_NORMAL);
        TaOrgAccount taOrgAccount = taOrgAccountMapper.selectOne(orgAccountQueryWrapper);
        if (taOrgAccount == null) {
            responseBean.addError("账户不存在");
            return responseBean;
        }

        // 1.校验账户金额
        if (amount > taOrgAccount.getAvailableBalance()) {
            responseBean.addError("账户可用余额不足，请稍后再试");
            return responseBean;
        }

        // 2.创建订单主表信息
        TaOrgOrder taOrgOrder = new TaOrgOrder();
        taOrgOrder.setOrgId(orgId);
        taOrgOrder.setTradeNo(CommonUtils.generateTradeNo(CommConstant.REFUND));
        taOrgOrder.setAmount(amount);
        taOrgOrder.setRealAmount(amount);
        taOrgOrder.setCreateDate(LocalDateTime.now());
        taOrgOrder.setExpiryDate(LocalDateTime.now().plusDays(7));
        taOrgOrder.setRemark("线上退款");
        taOrgOrder.setStatus(CommConstant.STATUS_NORMAL);
        taOrgOrder.setTradingStatus(CommConstant.TRADING_STATUS_PROCESSING);
        taOrgOrder.setIsOffline(false);
        taOrgOrder.setUserId(userId);
        taOrgOrder.setUserName(taUser.getUserName());
        if (taOrgOrderMapper.insert(taOrgOrder) < 1) {
            responseBean.addError("fail");
            return responseBean;
        }

        // 3.创建订单详情表信息
        TaOrgOrderDetail taOrgOrderDetail = new TaOrgOrderDetail();
        taOrgOrderDetail.setOrderId(taOrgOrder.getOrderId());
        taOrgOrderDetail.setOrgId(orgId);
        taOrgOrderDetail.setItemType(CommConstant.ITEM_TYPE_REFUND);
        taOrgOrderDetail.setAmount(amount);
        taOrgOrderDetail.setUnitPrice(amount);
        taOrgOrderDetail.setRealAmount(amount);
        taOrgOrderDetail.setQuantity(1);
        taOrgOrderDetail.setStatus(CommConstant.STATUS_NORMAL);
        if (taOrgOrderDetailMapper.insert(taOrgOrderDetail) < 1) {
            taOrgOrderMapper.deleteById(taOrgOrder.getOrderId());
            responseBean.addError("fail");
            return responseBean;
        }

        // 4.创建冻结表数据
        TaOrgAccountBlocked taOrgAccountBlocked = new TaOrgAccountBlocked();
        taOrgAccountBlocked.setAccountId(taOrgAccount.getAccountId());
        taOrgAccountBlocked.setBlockedBalance(amount);
        taOrgAccountBlocked.setBlockedReason("线上退款申请");
        taOrgAccountBlocked.setCreateDate(LocalDateTime.now());
        taOrgAccountBlocked.setStatus(CommConstant.STATUS_NORMAL);
        taOrgAccountBlocked.setOrgId(orgId);
        taOrgAccountBlockedMapper.insert(taOrgAccountBlocked);

        // 5.创建账户退款申请表信息
        TaOrgRefundApplication taOrgRefundApplication = new TaOrgRefundApplication();
        taOrgRefundApplication.setOrgId(orgId);
        taOrgRefundApplication.setUserId(userId);
        taOrgRefundApplication.setUserName(taUser.getUserName());
        taOrgRefundApplication.setAmount(amount);
        taOrgRefundApplication.setAuditStatus(CommConstant.CHECK_STATUS_CHECKING);
        taOrgRefundApplication.setOrderId(taOrgOrder.getOrderId());
        taOrgRefundApplication.setStatus(CommConstant.STATUS_NORMAL);
        taOrgRefundApplication.setBlockedId(taOrgAccountBlocked.getSerialNo());
        taOrgRefundApplication.setCreateDate(LocalDateTime.now());
        taOrgRefundApplication.setReason("线上申请退款");
        if (taOrgRefundApplicationMapper.insert(taOrgRefundApplication) < 1) {
            taOrgOrderMapper.deleteById(taOrgOrder.getOrderId());
            taOrgOrderDetailMapper.deleteById(taOrgOrderDetail.getSerialNo());
            responseBean.addError("fail");
            return responseBean;
        }

        // 6.更新账户新金额
        taOrgAccountMapper.updateAvailableBalanceById(taOrgAccount.getAccountId(), amount);

        // 7.发送短信
        ExecutorService threadPool = Executors.newCachedThreadPool();
        threadPool.execute(() -> {
            List<TaContact> taContacts = taOrgAccountService.selectSmsContactUser();
            if (CollectionUtils.isNotEmpty(taContacts)) {
                TaContact taContact = taContacts.get(0);
                String[] smsParams = {"业务平台退款申请", taOrgOrder.getTradeNo()};
                // 短信发送
                taOrgAccountService.sendSmsMessage(taContact.getPhone(), CommConstant.ITEM_TYPE_RECHARGE, smsParams);
            }
        });
        responseBean.addSuccess(taOrgOrder);
        return responseBean;
    }

    @Override
    public ResponseBean listCzOrderByCondition(Integer pageNum, Integer pageSize, LocalDateTime payStartTime, LocalDateTime payEndTime,
                                               String orderId, String tradeNo, String wxOrderId, Boolean isOffline, String tradingStatus, Integer orgId) {
        ResponseBean responseBean = new ResponseBean();
        IPage<TaOrgOrder> pg = new Page<>(pageNum, pageSize);
        pg = taOrgOrderMapper.listCzOrderByCondition(pg, payStartTime, payEndTime, orderId, tradeNo, wxOrderId, isOffline, tradingStatus, orgId);
        if (CollectionUtils.isNotEmpty(pg.getRecords())) {
            pg.getRecords().stream().forEach(r -> {
                QueryWrapper<TaOrgAccountCertificate> queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("order_id", r.getOrderId());
                queryWrapper.eq("org_id", orgId);
                List<TaOrgAccountCertificate> accountCertificates = taOrgAccountCertificateMapper.selectList(queryWrapper);
                if (CollectionUtils.isNotEmpty(accountCertificates)) {
                    List<String> certificateUrlList = new ArrayList<>();
                    accountCertificates.stream().forEach(e -> {
                        certificateUrlList.add(e.getUrl());
                    });
                    r.setCertificateUrlList(certificateUrlList);
                }
            });
        }
        responseBean.addSuccess(pg);
        return responseBean;
    }

    @Override
    public void exportCzOrderByCondition(LocalDateTime payStartTime, LocalDateTime payEndTime, String orderId, String tradeNo,
                                         String wxOrderId, Boolean isOffline, String tradingStatus, Integer orgId, HttpServletResponse response) throws Exception {
        response.setContentType("application/octet-stream");
        response.setCharacterEncoding("utf-8");
        List<AccountPayRecordExport> records = taOrgOrderMapper.exportCzOrderByCondition(payStartTime, payEndTime, orderId, tradeNo, wxOrderId, isOffline, tradingStatus, orgId);
        String fileName = URLEncoder.encode("充值订单", "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), AccountPayRecordExport.class).sheet("充值订单").doWrite(records);
    }

    @Override
    public ResponseBean listRefundOrderByCondition(Integer pageNum, Integer pageSize, LocalDateTime refundStartTime, LocalDateTime refundEndTime, String orderId, String auditStatus, String wxOrderId, String tradeNo, Integer orgId) {
        ResponseBean responseBean = new ResponseBean();
        IPage<TaOrgOrder> pg = new Page<>(pageNum, pageSize);
        pg = taOrgOrderMapper.listRefundOrderByCondition(pg, refundStartTime, refundEndTime, orderId, wxOrderId, tradeNo, auditStatus, orgId);

        if (CollectionUtils.isNotEmpty(pg.getRecords())) {
            pg.getRecords().stream().forEach(r -> {
                QueryWrapper<TaOrgAccountCertificate> queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("order_id", r.getOrderId());
                queryWrapper.eq("org_id", orgId);
                List<TaOrgAccountCertificate> accountCertificates = taOrgAccountCertificateMapper.selectList(queryWrapper);
                if (CollectionUtils.isNotEmpty(accountCertificates)) {
                    List<String> certificateUrlList = new ArrayList<>();
                    accountCertificates.stream().forEach(e -> {
                        certificateUrlList.add(e.getUrl());
                    });
                    r.setCertificateUrlList(certificateUrlList);
                }
            });
        }
        responseBean.addSuccess(pg);
        return responseBean;
    }

    @Override
    public void exportRefundOrderByCondition(LocalDateTime refundStartTime, LocalDateTime refundEndTime, String orderId, String auditStatus, String wxOrderId, String tradeNo, Integer orgId, HttpServletResponse response) throws Exception {
        response.setContentType("application/octet-stream");
        response.setCharacterEncoding("utf-8");
        List<AccountRefundRecordExport> records = taOrgOrderMapper.exportRefundOrderByCondition(refundStartTime, refundEndTime, orderId, wxOrderId, tradeNo, auditStatus, orgId);
        String fileName = URLEncoder.encode("退款订单", "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), AccountRefundRecordExport.class).sheet("退款订单").doWrite(records);
    }

    @Override
    public ResponseBean listRedPacketRecords(Integer pageNum, Integer pageSize, String phone, String activityId, String orderId, String wxOrderId, String tradeNo, String tradingStatus, Integer orgId) {
        ResponseBean responseBean = new ResponseBean();
        IPage<TaOrgOrder> pg = new Page<>(pageNum, pageSize);
        responseBean.addSuccess(taOrgOrderMapper.listRedPacketRecords(pg, phone, activityId, orderId, wxOrderId, tradeNo, tradingStatus, orgId));
        return responseBean;
    }

    @Override
    public ResponseBean listConsumeOrderByCondition(Integer pageNum, Integer pageSize, LocalDateTime startTime, LocalDateTime endTime, String orderId, String tradingStatus, String consumeType, String phone, String wxOrderId, String tradeNo, Integer orgId) {
        ResponseBean responseBean = new ResponseBean();
        IPage<TaOrgOrder> pg = new Page<>(pageNum, pageSize);
        responseBean.addSuccess(taOrgOrderMapper.listConsumeOrderByCondition(pg, startTime, endTime, orderId, tradingStatus, consumeType, phone, wxOrderId, tradeNo, orgId));
        return responseBean;
    }

    @Override
    public void exportConsumeOrderByCondition(LocalDateTime startTime, LocalDateTime endTime, String orderId, String tradingStatus, String consumeType, String phone, String wxOrderId, String tradeNo, Integer orgId, HttpServletResponse response) throws Exception {
        response.setContentType("application/octet-stream");
        response.setCharacterEncoding("utf-8");
        List<AccountConsumeRecordExport> records = taOrgOrderMapper.exportConsumeOrderByCondition(startTime, endTime, orderId, tradingStatus, consumeType, wxOrderId, tradeNo, phone, orgId);
        String fileName = URLEncoder.encode("消费订单", "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), AccountConsumeRecordExport.class).sheet("消费订单").doWrite(records);
    }

    /**
     * 获取每个小程序独立的微信账户
     *
     * @param orgId
     * @return
     */
    private WxConfig getWxConfig(Integer orgId) throws Exception {
        //根据orgId获取支付配置
        QueryWrapper<TaWxPayConfig> taWxPayConfigQueryWrapper = new QueryWrapper<>();
        taWxPayConfigQueryWrapper.eq("org_id", orgId);
        TaWxPayConfig taWxPayConfig = taWxPayConfigMapper.selectOne(taWxPayConfigQueryWrapper);
        if (null == taWxPayConfig) {
            throw new Exception("请先完善支付信息");
        }
        log.info("正在配置orgId为{}的微信支付参数，商户号为{}", orgId, taWxPayConfig.getMchId());
        //把库里的值赋值给微信
        URL urlConet = new URL(taWxPayConfig.getApiPath());
        HttpURLConnection con = (HttpURLConnection) urlConet.openConnection();
        con.setRequestMethod("GET");
        con.setConnectTimeout(4 * 1000);
        InputStream inStream = con.getInputStream();
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[2048];
        int len = 0;
        while ((len = inStream.read(buffer)) != -1) {
            outStream.write(buffer, 0, len);
        }
        inStream.close();

        WxConfig config = new WxConfig();
        config.setAppid(taWxPayConfig.getAppid());
        config.setMachId(taWxPayConfig.getMchId());
        config.setCertData(outStream.toByteArray());
        config.setKey(taWxPayConfig.getMchKey());
        return config;
    }

    @Override
    @Transactional(rollbackFor = {RuntimeException.class})
    public ResponseBean refundOrder(String id, TaOrgRefundApplication refundApplication, Integer userId) {
        ResponseBean responseBean = new ResponseBean();

        //订单退款申请，更新账户的实际余额
        refundApplication.setOrderId(id);
        TaOrgAccount taOrgAccount = assembleRefundAccount(refundApplication.getOrgId(), refundApplication, CommConstant.ITEM_TYPE_REFUND);
        taOrgAccountMapper.updateById(taOrgAccount);

        //更新订单记录
        TaOrgOrder taOrgOrder = assembleTaOrgOrder(id);
        taOrgOrderMapper.updateById(taOrgOrder);

        //新建账户消费流水
        TaOrgAccountDetailedList taOrgAccountDetailedList = assembleAccountDetailList(id, refundApplication, userId);
        taOrgAccountDetailedListMapper.insert(taOrgAccountDetailedList);

        //更新账户退款申请
        TaOrgRefundApplication selectRefundApplication = selectRefundApplication(id);

        TaOrgRefundApplication updateRefundApplication = assembleRefundApplication(userId, CommConstant.ITEM_TYPE_REFUND, refundApplication);
        QueryWrapper<TaOrgRefundApplication> taOrgRefundApplicationQueryWrapper = new QueryWrapper<>();
        taOrgRefundApplicationQueryWrapper.eq("order_id", id);
        taOrgRefundApplicationQueryWrapper.eq("blocked_id", selectRefundApplication.getBlockedId());
        taOrgRefundApplicationMapper.update(updateRefundApplication, taOrgRefundApplicationQueryWrapper);

        //更新账户冻结记录
        updateOrgAccountBlock(refundApplication, selectRefundApplication.getBlockedId());

        //插入凭证记录
        if (CollectionUtils.isNotEmpty(refundApplication.getCertificateList())) {
            List<TaOrgAccountCertificate> taOrgAccountCertificateList = assembleAccountCertificate(refundApplication, taOrgAccount, taOrgAccountDetailedList.getSerialNo(), taOrgOrder.getOrderId(), userId, CommConstant.ITEM_TYPE_REFUND);
            taOrgAccountCertificateList.forEach(e -> {
                taOrgAccountCertificateMapper.insert(e);
            });
        }

        return responseBean;
    }

    @Override
    @Transactional(rollbackFor = {RuntimeException.class})
    public ResponseBean rejectOrder(String id, TaOrgRefundApplication refundApplication, Integer userId) {
        ResponseBean responseBean = new ResponseBean();

        //退款订单驳回，更新账户的可用余额
        refundApplication.setOrderId(id);
        TaOrgAccount taOrgAccount = assembleRefundAccount(refundApplication.getOrgId(), refundApplication, CommConstant.ITEM_TYPE_REJECT);
        taOrgAccountMapper.updateById(taOrgAccount);

        //更新退款申请记录
        TaOrgRefundApplication selectRefundApplication = selectRefundApplication(id);

        TaOrgRefundApplication updateRefundApplication = assembleRefundApplication(userId, CommConstant.ITEM_TYPE_REJECT, refundApplication);
        QueryWrapper<TaOrgRefundApplication> taOrgRefundApplicationQueryWrapper = new QueryWrapper<>();
        taOrgRefundApplicationQueryWrapper.eq("order_id", id);
        taOrgRefundApplicationMapper.update(updateRefundApplication, taOrgRefundApplicationQueryWrapper);

        //更新账户冻结记录
        updateOrgAccountBlock(refundApplication, selectRefundApplication.getBlockedId());

        //更新订单状态
        TaOrgOrder taOrgOrder = new TaOrgOrder();
        taOrgOrder.setOrderId(id);
        taOrgOrder.setTradingStatus(CommConstant.TRADING_STATUS_FAIL);
        taOrgOrderMapper.updateById(taOrgOrder);

        return responseBean;

    }

    /**
     * 构造充值账户
     *
     * @param orgId
     * @param refundApplication
     * @return
     */
    private TaOrgAccount assembleRefundAccount(Integer orgId, TaOrgRefundApplication refundApplication, String operateType) {
        TaOrgAccount selectOneAccount = selectTaOrgAccountById(orgId);

        TaOrgAccount rechargeAccount = new TaOrgAccount();
        rechargeAccount.setAccountId(selectOneAccount.getAccountId());

        if (CommConstant.ITEM_TYPE_REFUND.equals(operateType)) {
            TaOrgRefundApplication taOrgRefundApplication = selectRefundApplication(refundApplication.getOrderId());
            rechargeAccount.setRealBalance(selectOneAccount.getRealBalance() - taOrgRefundApplication.getAmount());
            rechargeAccount.setTotalRefund(selectOneAccount.getTotalRefund() + taOrgRefundApplication.getAmount());
        }

        if (CommConstant.ITEM_TYPE_REJECT.equals(operateType)) {
            TaOrgRefundApplication taOrgRefundApplication = selectRefundApplication(refundApplication.getOrderId());
            rechargeAccount.setAvailableBalance(selectOneAccount.getAvailableBalance() + taOrgRefundApplication.getAmount());
        }

        rechargeAccount.setModifyDate(LocalDateTime.now());

        return rechargeAccount;
    }

    /**
     * 构造订单
     *
     * @return
     */
    private TaOrgOrder assembleTaOrgOrder(String orderId) {

        TaOrgOrder taOrgOrder = new TaOrgOrder();
        taOrgOrder.setOrderId(orderId);
        taOrgOrder.setTradingStatus(CommConstant.TRADING_STATUS_SUCCESS);

        return taOrgOrder;
    }

    /**
     * 构造交订单详情信息
     *
     * @param orderId
     * @param orgId
     * @param amount
     * @return
     */
    private TaOrgOrderDetail assembleTaOrgOrderDetail(String orderId, Integer orgId, Integer amount) {
        TaOrgOrderDetail taOrgOrderDetail = new TaOrgOrderDetail();

        taOrgOrderDetail.setOrderId(orderId);
        taOrgOrderDetail.setOrgId(orgId);
        taOrgOrderDetail.setItemType(CommConstant.ITEM_TYPE_REFUND);
        taOrgOrderDetail.setAmount(amount);

        return taOrgOrderDetail;
    }

    /**
     * 构造消费流水记录
     *
     * @param refundApplication
     * @param userId
     * @return
     */
    private TaOrgAccountDetailedList assembleAccountDetailList(String orderId, TaOrgRefundApplication refundApplication, Integer userId) {

        SysUser taUser = selectUserById(userId);
        TaOrgAccount taOrgAccount = selectTaOrgAccountById(refundApplication.getOrgId());

        TaOrgAccountDetailedList taOrgAccountDetailed = new TaOrgAccountDetailedList();
        taOrgAccountDetailed.setAccountId(taOrgAccount.getAccountId());
        taOrgAccountDetailed.setOrgId(refundApplication.getOrgId());
        taOrgAccountDetailed.setAmount(refundApplication.getAmount());
        //1收入,-1支出
        taOrgAccountDetailed.setSignType(CommConstant.STATUS_DELETE);
        taOrgAccountDetailed.setRemark("线下订单退费");
        taOrgAccountDetailed.setOrderId(orderId);
        taOrgAccountDetailed.setUserId(userId);
        taOrgAccountDetailed.setUserName(taUser.getUserName());

        return taOrgAccountDetailed;
    }

    /**
     * 构造交易凭证信息
     *
     * @param orgId
     * @param taOrgAccount
     * @param accountSerialNo
     * @param orderId
     * @param userId
     * @return
     */
    public List<TaOrgAccountCertificate> assembleAccountCertificate(TaOrgRefundApplication taOrgRefundApplication, TaOrgAccount taOrgAccount, Integer accountSerialNo, String orderId, Integer userId, String consumeType) {
        SysUser taUser = selectUserById(userId);
        List<TaOrgAccountCertificate> taOrgAccountCertificateList = new ArrayList<>();

        taOrgRefundApplication.getCertificateList().forEach(e -> {
            TaOrgAccountCertificate taOrgAccountCertificate = new TaOrgAccountCertificate();
            taOrgAccountCertificate.setOrgId(taOrgRefundApplication.getOrgId());
            taOrgAccountCertificate.setAccountId(taOrgAccount.getAccountId());
            taOrgAccountCertificate.setAccountSerialNo(accountSerialNo);
            taOrgAccountCertificate.setOrderId(orderId);
            taOrgAccountCertificate.setConsumptionType(CommConstant.ITEM_TYPE_RECHARGE.equals(consumeType) ? CommConstant.ITEM_TYPE_RECHARGE : CommConstant.ITEM_TYPE_REFUND);
            taOrgAccountCertificate.setRemark(CommConstant.ITEM_TYPE_RECHARGE.equals(consumeType) ? "线下缴费" : "线下退费");
            taOrgAccountCertificate.setUrl(e);
            taOrgAccountCertificate.setCreateDate(LocalDateTime.now());
            taOrgAccountCertificate.setUserId(userId);
            taOrgAccountCertificate.setUserName(taUser.getUserName());
            taOrgAccountCertificateList.add(taOrgAccountCertificate);
        });

        return taOrgAccountCertificateList;
    }

    /**
     * 构造订单退款申请
     *
     * @param userId
     * @return
     */
    private TaOrgRefundApplication assembleRefundApplication(Integer userId, String operateType, TaOrgRefundApplication refundApplication) {
        SysUser taUser = selectUserById(userId);
        TaOrgRefundApplication taOrgRefundApplication = new TaOrgRefundApplication();
        taOrgRefundApplication.setAuditorId(userId);
        taOrgRefundApplication.setAuditorName(taUser.getUserName());
        taOrgRefundApplication.setAuditResult(CommConstant.ITEM_TYPE_REFUND.equals(operateType) ? "" : refundApplication.getAuditResult());
        taOrgRefundApplication.setAuditStatus(CommConstant.ITEM_TYPE_REFUND.equals(operateType) ? "agree" : "unAgree");

        return taOrgRefundApplication;
    }

    private Integer updateOrgAccountBlock(TaOrgRefundApplication refundApplication, Integer blockedId) {
        //更新账户冻结记录
        TaOrgAccountBlocked taOrgAccountBlocked = new TaOrgAccountBlocked();
        taOrgAccountBlocked.setStatus(CommConstant.STATUS_UNACCALIMED);
        QueryWrapper<TaOrgAccountBlocked> taOrgAccountBlockedQueryWrapper = new QueryWrapper<>();
        taOrgAccountBlockedQueryWrapper.eq("org_id", refundApplication.getOrgId());
        taOrgAccountBlockedQueryWrapper.eq("serial_no", blockedId);
        return taOrgAccountBlockedMapper.update(taOrgAccountBlocked, taOrgAccountBlockedQueryWrapper);
    }

    public TaOrgAccount selectTaOrgAccountById(Integer orgId) {
        QueryWrapper<TaOrgAccount> selectOneQuery = new QueryWrapper<>();
        selectOneQuery.eq("org_id", orgId);
        TaOrgAccount selectOneAccount = taOrgAccountMapper.selectOne(selectOneQuery);
        return selectOneAccount;
    }

    /**
     * 获取操作人
     *
     * @param userId
     * @return
     */
    public SysUser selectUserById(Integer userId) {
        //获取操作人
        QueryWrapper<SysUser> sysUserQueryWrapper = new QueryWrapper<>();
        sysUserQueryWrapper.eq("user_id", userId);
        SysUser sysUser = sysUserMapper.selectOne(sysUserQueryWrapper);
        return sysUser;
    }

    public TaOrgRefundApplication selectRefundApplication(String orderId) {
        QueryWrapper<TaOrgRefundApplication> taOrgRefundApplicationQueryWrapper = new QueryWrapper<>();
        taOrgRefundApplicationQueryWrapper.eq("order_id", orderId);
        return taOrgRefundApplicationMapper.selectOne(taOrgRefundApplicationQueryWrapper);
    }

    /**
     * 计算实付金额
     *
     * @param amount
     * @return
     */
    public static Integer calculateRealAmount(Integer amount) {
        Double dAmount = CommonUtils.fenToYuan(amount);
        // 防止误差，结果需要加0.01
        Double dRealAmount = dAmount / (1 - CommConstant.WXMCH_PAY_RATE) + 0.01;
        return CommonUtils.yuanToFen(dRealAmount);
    }
}
