package com.izouma.nineth.service;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.domain.OrderShopInfoResult;
import com.izouma.nineth.config.RedisKeys;
import com.izouma.nineth.config.SysConstants;
import com.izouma.nineth.domain.*;
import com.izouma.nineth.dto.PageQuery;
import com.izouma.nineth.dto.pay.TradeType;
import com.izouma.nineth.dto.pay.UniformNotifyRequest;
import com.izouma.nineth.dto.userfund.*;
import com.izouma.nineth.enums.*;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.lock.RedisLockable;
import com.izouma.nineth.repo.*;
import com.izouma.nineth.service.pay.PayHelper;
import com.izouma.nineth.service.pay.UniformPayConstants;
import com.izouma.nineth.utils.SnowflakeIdWorker;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Service
@Slf4j
@AllArgsConstructor
public class UserFundAccountForLianlianpayService {

    //默认续费时长
    public static final Integer RENEW_DAYS = 30;

    /**
     * 单笔转账限制
     */
    public static final BigDecimal TRANSFER_LIMIT = new BigDecimal(10000);


    private UserFundAccountRepo userFundAccountRepo;

    private LianlianPayService lianlianPayService;

    private IdentityAuthRepo identityAuthRepo;

    private UserBankCardRepo userBankCardRepo;

    private RechargeOrderRepo rechargeOrderRepo;

    private WithdrawApplyRepo withdrawApplyRepo;

    private SnowflakeIdWorker snowflakeIdWorker;

    private PayHelper payHelper;

    private UserFundChangeLogService userFundChangeLogService;

    private PaymentInfoRepo paymentInfoRepo;

    private UserRepo userRepo;

    private SysConfigService sysConfigService;

    private WalletRenewOrderRepo walletRenewOrderRepo;

    private DivMoneyRepo divMoneyRepo;

    private TransferMoneyInfoRepo transferMoneyInfoRepo;

    private OrderRepo orderRepo;

    /**
     * 获取连连付账户
     * @param userId
     * @return
     */
    public UserFundAccount getAccount(Long userId){
        UserFundAccount userFundAccount = userFundAccountRepo.findByUserIdAndAccountType(userId, AccountType.LIANLIANPAY_USER)
                .orElse(null);

        if(userFundAccount != null){
            //设置失效时间
            LocalDateTime expireTime = userFundAccount.getExpireTime();
            if(expireTime == null){
                //固定免费天数
                Integer freeDays = UserFundAccountForLianlianpayService.RENEW_DAYS;
                userFundAccount.setExpireTime(userFundAccount.getCreatedAt().plusDays(freeDays));
            }

            BigDecimal manageCost;
            if(userFundAccount.getAccountStatus() == AccountStatus.NORMAL){
                //续费费用
                manageCost = sysConfigService.getBigDecimal(SysConstants.WALLET_MANAGE_COST);
            }else{
                //开户费用
                manageCost = sysConfigService.getBigDecimal(SysConstants.WALLET_REGISTER_COST);
            }

            userFundAccount.setManageCost(manageCost);
            //是否需要续费
            if(userFundAccount.getExpireTime().compareTo(LocalDateTime.now()) <= 0
                    && manageCost.compareTo(BigDecimal.ZERO) > 0){
                userFundAccount.setNeedRenew(true);
            }

        }

        return  userFundAccount;
    }



    /**
     * 获取正常状态的账户
     * @param userId
     * @return
     */
    public UserFundAccount getNormalAccount(Long userId){
        UserFundAccount account = getAccount(userId);
        if(account == null){
            throw new BusinessException("用户未开通钱包");
        }

        if(account.getAccountStatus() != AccountStatus.NORMAL){
            throw new BusinessException("连连付钱包账户未激活");
        }

        return account;
    }

    /**
     * 获取有效账户
     * @param userId
     * @return
     */
    public UserFundAccount getValidAccount(Long userId){
        UserFundAccount account = getNormalAccount(userId);

        if(account.isNeedRenew()){
            throw new BusinessException("钱包服务已到期，请续费后再使用");
        }

        return account;
    }


    /**
     * 获取随机因子
     * @param userId
     * @return
     */
    public JSONObject getRandomWithCache(Long userId, String algorithm){
        JSONObject res = lianlianPayService.getRandom(userId, algorithm);

        JSONObject result = new JSONObject();
        result.put("license", res.getString("license"));
        result.put("rsaPublicKey", res.getString("rsa_public_content"));
        result.put("sm2KeyHex", res.getString("sm2_key_hex"));
        result.put("mapArr", res.getString("map_arr"));
        result.put("randomKey", res.getString("random_key"));
        result.put("randomValue", res.getString("random_value"));
        result.put("freshTime", LocalDateTime.now());
        return result;
    }



    /**
     * 添加连连支付账户
     * @param userId
     * @return
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public UserFundAccount registerApply(Long userId, String returnUrl){

        IdentityAuth identityAuth = identityAuthRepo.findFirstByUserIdAndStatusAndDelFalseOrderByCreatedAtDesc(userId, AuthStatus.SUCCESS)
                .orElseThrow(new BusinessException("用户未通过实名认证"));

        User user = userRepo.findById(userId).orElseThrow(new BusinessException("用户不存在"));

        //自动绑卡
        UserBankCard userBankCard = userBankCardRepo.findByUserId(userId).stream().findAny()
                .orElseThrow(new BusinessException("用户未绑定银行卡"));


        //待激活，未开通成功
        AccountStatus accountStatus = AccountStatus.ACTIVATE_PENDING;

        //记录资金账户信息
        UserFundAccount account  = getAccount(userId);
        //是否设置了开户金额
        BigDecimal manageCost = sysConfigService.getBigDecimal(SysConstants.WALLET_REGISTER_COST);
        if(account == null && manageCost.compareTo(BigDecimal.ZERO) <= 0){
            //无需续费，自动初始化空的失效日期（免费一个月）
            UserFundAccount.UserFundAccountBuilder builder = UserFundAccount.builder();
            builder.userId(userId).accountType(AccountType.LIANLIANPAY_USER);
            account = builder.build();
        }

        if(account == null || account.isNeedRenew()){
            throw new BusinessException("您尚未支付钱包管理费，请支付后再操作");
        }

        //审核中
        if(account.getAccountStatus() == AccountStatus.CHECK_PENDING){
            throw new BusinessException("钱包账户正在审核，请等待审核完成");
        }

        //开户成功
        if(account.getAccountStatus() == AccountStatus.NORMAL){
            throw new BusinessException("钱包账户已经开户成功，请勿重复操作");
        }


        String orderNo = null;
        String token = null;
        String gatewayUrl = null;
        if(StringUtils.isNotEmpty(returnUrl)){
            //使用页面开户
            JSONObject res = lianlianPayService.registerApplyWithPage(identityAuth, userBankCard, returnUrl);
            orderNo = res.getString("txn_seqno");//商户系统唯一流水
            gatewayUrl = res.getString("gateway_url");//开户地址
        }else{
            //使用api开户
            JSONObject res = lianlianPayService.registerApply(identityAuth, userBankCard);
            orderNo = res.getString("txn_seqno");//商户系统唯一流水
            token = res.getString("token");//30分钟有效
        }



        account.setAccountNo(user.getMemberId());
        account.setAccountName(identityAuth.getRealName());
        account.setAccountStatus(accountStatus);
        account.setUserBankCardId(userBankCard.getId());
        account.setOrderNo(orderNo);
        account.setToken(token);
        account.setGatewayUrl(gatewayUrl);
        account.setRegisterPhone(user.getPhone());
        account.setApplyTime(LocalDateTime.now());
        userFundAccountRepo.save(account);

        return account;
    }

    /**
     * 注册校验
     * @return
     */
    @RedisLockable(prefix = RedisKeys.WALLET_PAY_CONFIRM_LOCK, key = "#userId")
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public UserFundAccount registerVerify(Long userId, String password, String randomKey, String verifyCode){
        if(StringUtils.isEmpty(verifyCode)){
            throw new BusinessException("短信验证码不能为空");
        }

        //记录资金账户信息
        UserFundAccount account  = userFundAccountRepo.findByUserIdAndAccountType(userId, AccountType.LIANLIANPAY_USER)
                .orElseThrow(new BusinessException("连连付钱包不存在"));

        JSONObject res = lianlianPayService.registerVerify(account, password, randomKey, verifyCode);
        String oiUserNo = res.getString("oid_userno");
        String userStatus = res.getString("user_status");
        AccountStatus accountStatus = AccountStatus.NORMAL;
        String accountError = "";
        if(!"NORMAL".equals(userStatus)){
            //待审核
            accountStatus = AccountStatus.CHECK_PENDING;
            accountError = "开户状态（" + userStatus + ")";
        }

        account.setThirdUserId(oiUserNo);
        account.setAccountStatus(accountStatus);
        account.setAccountError(accountError);
        userFundAccountRepo.save(account);
        return account;
    }

    /**
     * 连连账户通知
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public UserFundAccount registerResultNotify(UniformNotifyRequest request){
        String orderNo = request.getOrderNo();
        UserFundAccount userFundAccount = userFundAccountRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("资金账户未找到"));
        String requestBody = request.getRequestBody();
        String fromSign = request.getHeader("Signature-Data");
        JSONObject reqData = lianlianPayService.checkSuccessBody(requestBody, fromSign);
        String userId = reqData.getString("user_id");
        //ACCP系统交易单号。
        String txnSeqno = reqData.getString("txn_seqno");
        String accpTxno = reqData.getString("accp_txno");
        String oidUserno = reqData.getString("oid_userno");
        String userStatus = reqData.getString("user_status");
        String remark = reqData.getString("remark");
        //绑定的银行账户
        String linkedAgrtno = reqData.getString("linked_agrtno");
        //合作银行账号。ACCP合作银行账户
        String bankAccount = reqData.getString("bank_account");
        String errorMsg = "通知状态（" +  userStatus + ")";
        if(StringUtils.isNotEmpty(remark)){
            errorMsg = remark;
        }

        AccountStatus accountStatus = userFundAccount.getAccountStatus();
        if("NORMAL".equals(userStatus)){
            //状态正常
            accountStatus = AccountStatus.NORMAL;
        }


        //开户成功
        userFundAccount.setThirdUserId(oidUserno);
        userFundAccount.setAccountStatus(accountStatus);
        userFundAccount.setThirdBankId(linkedAgrtno);
        userFundAccount.setThirdBankNo(bankAccount);
        userFundAccount.setAccountError(errorMsg);

        userFundAccount.setNotifyTime(LocalDateTime.now());
        userFundAccountRepo.save(userFundAccount);

        //更新绑卡协议
        UserBankCard userBankCard = userBankCardRepo.findById(userFundAccount.getUserBankCardId()).orElse(null);
        if(userBankCard != null){
            userBankCard.setThirdAccountId(oidUserno);
            userBankCard.setThirdBankId(linkedAgrtno);
            userBankCardRepo.save(userBankCard);
        }

        return userFundAccount;
    }


    /**
     * 销户申请
     * @param userId
     * @return
     */
    public JSONObject cancelApply(Long userId, String password, String randomKey){
        UserFundAccount lianlianAccount = getNormalAccount(userId);
        IdentityAuth identityAuth = identityAuthRepo.findFirstByUserIdAndStatusAndDelFalseOrderByCreatedAtDesc(userId, AuthStatus.SUCCESS)
                .orElseThrow(new BusinessException("用户未通过实名认证"));

        JSONObject res = lianlianPayService.cancelApply(lianlianAccount, identityAuth, password, randomKey);

        return res;
    }


    /**
     * 连连账户销户通知
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public UserFundAccount cancelResultNotify(UniformNotifyRequest request){
        String requestBody = request.getRequestBody();
        String fromSign = request.getHeader("Signature-Data");
        JSONObject reqData = lianlianPayService.checkSuccessBody(requestBody, fromSign);
        String userId = reqData.getString("user_id");
        //ACCP系统交易单号。
        String txnSeqno = reqData.getString("txn_seqno");
        String result = reqData.getString("result");
        UserFundAccount userFundAccount = userFundAccountRepo.findByAccountNo(userId).orElseThrow(new BusinessException("未找资金账户"));
        if("SUCCESS".equals(result)){
            userFundAccount.setAccountStatus(AccountStatus.CANCEL);
            userFundAccount.setAccountError("已销户");
            userFundAccount.setCancelTime(LocalDateTime.now());
            userFundAccountRepo.save(userFundAccount);
        }

        return userFundAccount;
    }


    /**
     * 余额查询
     * @param userId
     * @return
     */
    @Cacheable(RedisKeys.WALLET_LIANLIAN_BALANCE_CACHE)
    public BalanceInfoDTO getBalance(Long userId){
        //记录资金账户信息
        UserFundAccount account  = userFundAccountRepo.findByUserIdAndAccountType(userId, AccountType.LIANLIANPAY_USER)
                .orElseThrow(new BusinessException("连连付账户不存在"));
        JSONObject balanceRes = lianlianPayService.accountInfoQuery(account.getUserId());
        JSONArray accountList = balanceRes.getJSONArray("acctinfo_list");
        if(accountList == null || accountList.size() == 0){
            throw new BusinessException("未获取到余额信息");
        }

        //USEROWN_AVAILABLE 自有账户， USEROWN_PSETTLE 待结算账户
        JSONObject mainAccount = null;
        JSONObject psettleAccount = null;
        for(int i = 0; i < accountList.size(); i++){
            JSONObject item = accountList.getJSONObject(i);
            String actType = item.getString("acct_type");
            if("USEROWN_AVAILABLE".equals(actType)){
                mainAccount = item;
            }
            //待结算账户
            if("USEROWN_PSETTLE".equals(actType)){
                psettleAccount = item;
            }
        }

        String bankAccount = balanceRes.getString("bank_account");//合作银行账户
        BalanceInfoDTO balanceInfo = new BalanceInfoDTO();
        balanceInfo.setBankAccount(bankAccount);
        if(mainAccount != null){
            //String accountName = mainAccount.getString("oid_acctno");
            String accountType = mainAccount.getString("acct_type");
            BigDecimal amtBalaval = mainAccount.getBigDecimal("amt_balaval");
            BigDecimal amtBalfrz = mainAccount.getBigDecimal("amt_balfrz");
            String acctState = mainAccount.getString("acct_state");
            balanceInfo.setAccountType(accountType);
            balanceInfo.setAvailableBalance(amtBalaval);
            balanceInfo.setFreezeBalance(amtBalfrz);
            balanceInfo.setAccountStatus(acctState);
        }

        if(psettleAccount != null){
            BigDecimal amtBalaval = psettleAccount.getBigDecimal("amt_balaval");
            balanceInfo.setPsettleBalance(amtBalaval);//可用余额
        }

        return balanceInfo;
    }


    /**
     * 清除余额缓存
     * @param userId
     */
    @CacheEvict(RedisKeys.WALLET_LIANLIAN_BALANCE_CACHE)
    public void clearBalanceCache(Long userId){
        //nothing
    }

    /**
     * 检查用户信息
     * @param userId
     * @return
     */
    @Transactional
    public JSONObject checkUserInfo(Long userId){
        JSONObject res = lianlianPayService.userInfoQuery(userId);
        String userStatus = res.getString("user_status");
        //银行开户标识
        //0：未开户
        //1：已开户
        //2：开户中
        String bankOpenFlag = res.getString("bank_open_flag");
        String oidUserNo = res.getString("oid_userno");
        String bankAccount = res.getString("bank_account");
        String errorInfo = "检查状态（" + userStatus + ")";
        //修复账户状态
        UserFundAccount account = getAccount(userId);
        if("1".equals(bankOpenFlag) && "NORMAL".equals(userStatus)
                && account.getAccountStatus() != AccountStatus.NORMAL){
            account.setAccountStatus(AccountStatus.NORMAL);
            account.setThirdUserId(oidUserNo);
            account.setThirdBankNo(bankAccount);
            account.setAccountError(errorInfo);
            userFundAccountRepo.save(account);
        }

        return res;
    }

    /**
     * 修改密码
     * @param userId
     * @param password
     * @param randomKey
     * @param passwordNew
     * @return
     */
    public JSONObject changePassword(Long userId, String password, String randomKey, String passwordNew){
        String goodsName = "重置密码";
        JSONObject riskItem = lianlianPayService.getRiskItem(userId, goodsName);
        JSONObject res = lianlianPayService.changePassword(userId, password, randomKey, passwordNew, riskItem);
        return res;
    }

    public JSONObject findPasswordApply(Long userId){
        UserFundAccount account = getNormalAccount(userId);
        String goodsName = "找回密码";
        JSONObject riskItem = lianlianPayService.getRiskItem(userId, goodsName);
        UserBankCard userBankCard = userBankCardRepo.findById(account.getUserBankCardId()).orElseThrow(new BusinessException("银行卡信息不存在"));
        JSONObject res = lianlianPayService.findPasswordApply(userId, userBankCard.getBankNo(), riskItem);
        return res;
    }

    @RedisLockable(prefix = RedisKeys.WALLET_PAY_CONFIRM_LOCK, key = "#userId")
    public JSONObject findPasswordVerify(Long userId, String token, String verifyCode, String password, String randomKey){
        JSONObject res = lianlianPayService.findPasswordVerify(userId, token, verifyCode, password, randomKey);
        return res;
    }

    /**
     * 创建充值订单
     * @param rechargeOrderForm
     * @return
     */
    @Transactional
    public RechargeOrder createRechargeOrder(RechargeOrderForm rechargeOrderForm){
        UserFundAccount account = getNormalAccount(rechargeOrderForm.getUserId());

        BigDecimal rechargeAmount = rechargeOrderForm.getRechargeAmount();
        if(rechargeAmount == null || rechargeAmount.compareTo(BigDecimal.ZERO) <= 0){
            throw new BusinessException("充值金额不能小于0");
        }

        UserBankCard userBankcard = userBankCardRepo.findById(account.getUserBankCardId())
                .orElseThrow(new BusinessException("绑卡记录未找到，请重新绑卡"));

        //需要绑卡
        if(StringUtils.isEmpty(account.getThirdBankId())) {
            throw new BusinessException("未找到关联卡信息，请重新绑卡");
        }

        String frontUrl = rechargeOrderForm.getFrontUrl();
        if(StringUtils.isEmpty(frontUrl)){
            frontUrl = payHelper.resolveHome(rechargeOrderForm.getProjectId());
        }

        String orderNo = String.valueOf(snowflakeIdWorker.nextId());
        String notifyUrl = payHelper.resolveRechargeNotifyUrl(orderNo);

        RechargeOrder.RechargeOrderBuilder builder = RechargeOrder.builder();
        builder.title("用户充值")
                .orderNo(orderNo)
                .userId(account.getUserId())
                .userFundAccountId(account.getId())
                .orderAmount(rechargeAmount)
                .userBankCardId(userBankcard.getId())
                .bankName(userBankcard.getBankName())
                .bankNo(userBankcard.getBankNo())
                .relatedCardNo(account.getThirdBankId())
                .status(RechargeStatus.CREATED)
                .notifyUrl(notifyUrl)
                .frontUrl(frontUrl)
                .payPlatform(UniformPayConstants.LIANLIANPAY_PLATFORM);

        RechargeOrder rechargeOrder = builder.build();
        rechargeOrder.setPayTool("QUICKPAY");
        //充值不应该收取手续费
        //rechargeOrder.setUserFeeAmt(BigDecimal.ZERO);

        //使用后台充值方式
        JSONObject res = lianlianPayService.recharge(rechargeOrder);
        String accpTxno = res.getString("accp_txno");

        //商户手续费，暂不做到帐金额计算
        rechargeOrder.setFeeAmt(BigDecimal.ZERO);
        //到账金额
        BigDecimal amount = rechargeOrder.getOrderAmount();
        rechargeOrder.setAmount(amount);

        rechargeOrderRepo.save(rechargeOrder);

        return rechargeOrder;
    }

    /**
     * 快捷支付充值
     * @return
     */
    @RedisLockable(prefix = RedisKeys.WALLET_PAY_CONFIRM_LOCK, key = "#orderNo")
    @Transactional
    public RechargeOrder rechargeWithQuickpay(String orderNo, String password, String randomKey){
        RechargeOrder  rechargeOrder = rechargeOrderRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("充值订单未找到"));
        JSONObject riskItem = lianlianPayService.getRiskItem(rechargeOrder.getUserId(), rechargeOrder.getTitle());
        UserFundAccount lianlianAccount  = getNormalAccount(rechargeOrder.getUserId());
        UserBankCard userBankCard = userBankCardRepo.findById(lianlianAccount.getUserBankCardId())
                .orElseThrow(new BusinessException("钱包绑卡信息未找到，无法使用快捷支付"));
        JSONObject res = lianlianPayService.paymentQuickpay(userBankCard, orderNo, rechargeOrder.getOrderAmount(),
                password, randomKey, riskItem);

        String token = res.getString("token");
        rechargeOrder.setToken(token);
        if(StringUtils.isEmpty(token)){
            //不需要二次确认
            rechargeOrder.setStatus(RechargeStatus.PENDING);
            //记录充值流水
            userFundChangeLogService.createByRecharge(rechargeOrder);
        }
        rechargeOrderRepo.saveAndFlush(rechargeOrder);
        return rechargeOrder;
    }


    /**
     * 充值确认
     * @param orderNo
     * @param verifyCode
     * @return
     */
    @RedisLockable(prefix = RedisKeys.WALLET_PAY_CONFIRM_LOCK, key = "#orderNo")
    @Transactional
    public RechargeOrder rechargeConfirm(String orderNo, String verifyCode){
        if(StringUtils.isEmpty(verifyCode)){
            throw new BusinessException("短信验证码不能为空");
        }

        RechargeOrder  rechargeOrder = rechargeOrderRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("充值订单未找到"));
        if(StringUtils.isEmpty(rechargeOrder.getToken())){
            throw new BusinessException("缺失授权令牌，请确认是否提交充值方式");
        }

        if(rechargeOrder.getStatus() != RechargeStatus.CREATED){
            throw new BusinessException("充值状态不正确(" + rechargeOrder.getStatus().getDescription() + ")");
        }


        rechargeOrder.setStatus(RechargeStatus.PENDING);
        rechargeOrderRepo.saveAndFlush(rechargeOrder);
        //记录充值流水
        userFundChangeLogService.createByRecharge(rechargeOrder);

        lianlianPayService.tradeValidationSms(rechargeOrder.getUserId(), rechargeOrder.getOrderNo()
                , rechargeOrder.getOrderAmount(), rechargeOrder.getToken(), verifyCode);

        return rechargeOrder;

    }

    /**
     * 充值通知处理
     * @return
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public String rechargeNotify(UniformNotifyRequest request){

        String content = "Success";

        String orderNo = request.getOrderNo();
        String reqData = request.getRequestBody();
        String fromSign = request.getHeader("Signature-Data");

        RechargeOrder order = rechargeOrderRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("充值订单未找到"));

        JSONObject bodyJson = lianlianPayService.checkSuccessBody(reqData, fromSign);

        String finishTime = bodyJson.getString("finish_time");//格式：yyyyMMddHHmmss
        String transactionId = bodyJson.getString("accp_txno");//系统交易单号
        String orderStatus = bodyJson.getString("txn_status");

        if(StringUtils.isNotEmpty(finishTime)){
            LocalDateTime finishTimeObj = DateUtil.parse(finishTime).toLocalDateTime();
            order.setFinishTime(finishTimeObj);
        }

        //1：成功
        order.setNotifyTime(LocalDateTime.now());
        order.setTransactionId(transactionId);
        if("TRADE_SUCCESS".equals(orderStatus)){
            order.setStatus(RechargeStatus.SUCCESS);
            order.setResult("成功");
            //更新资金流水
            userFundChangeLogService.updateSuccess(order.getUserId(), FundChangeType.RECHARGE,
                    order.getOrderNo());

        }else{
            order.setStatus(RechargeStatus.FAILED);
            order.setResult("失败:" + orderStatus);

            //更新资金流水
            userFundChangeLogService.updateFailed(order.getUserId(), FundChangeType.RECHARGE,
                    order.getOrderNo());
        }

        rechargeOrderRepo.save(order);
        return content;

    }



    /**
     * 创建账户管理订单
     * @return
     */
    @RedisLockable(prefix = RedisKeys.WALLET_PAY_CONFIRM_LOCK, key = "#orderNo")
    @Transactional
    public WalletRenewOrder createRenewOrder(Long userId, TradeType tradeType){
        //是否有续费记录正在进行
        Integer pendingCount = walletRenewOrderRepo.countPendingStatus(userId);
        if(pendingCount > 0){
            throw new BusinessException("有一笔支付中的续费记录正在处理，请稍后再试");
        }

        //购买周期，固定天数
        Integer buyDuration = RENEW_DAYS;

        //支持匿名用户，不注册钱包也可以支付
        UserFundAccount userFundAccount = getAccount(userId);
        boolean hasWallet = false;
        LocalDateTime startTime = LocalDateTime.now();
        if(userFundAccount == null){
            UserFundAccount.UserFundAccountBuilder builder = UserFundAccount.builder();
            builder.userId(userId).accountType(AccountType.LIANLIANPAY_USER).expireTime(startTime);
            userFundAccount = builder.build();
            userFundAccountRepo.saveAndFlush(userFundAccount);
        }

        //审核中
        if(userFundAccount.getAccountStatus() == AccountStatus.CHECK_PENDING){
            throw new BusinessException("钱包账户正在审核，请等待审核完成");
        }

        if(userFundAccount.getAccountStatus() == AccountStatus.NORMAL) {
            hasWallet = true;
            //之前有钱包,续费时间
            startTime = userFundAccount.getExpireTime();
            if(startTime == null){
                //默认免费一个月
                startTime = userFundAccount.getCreatedAt().plusDays(buyDuration);
            }


            //非续费周期不许支付,过期前两天(下一周期已续费成功)
            LocalDateTime nowDate = LocalDateTime.now();
            LocalDateTime beforeTwoDays = startTime.minusDays(2);
            if(nowDate.compareTo(beforeTwoDays) < 0){
                throw new BusinessException("已续费成功，请勿重复操作");
            }

            //是否小于今天,账户过期后再次续费，是否从今天算起?
            if(startTime.compareTo(LocalDateTime.now()) < 0){
                startTime = LocalDateTime.now();
            }

        }

        BigDecimal manageCost;
        if(userFundAccount.getAccountStatus() == AccountStatus.NORMAL){
            //续费费用
            manageCost = sysConfigService.getBigDecimal(SysConstants.WALLET_MANAGE_COST);
        }else{
            //开户费用
            manageCost = sysConfigService.getBigDecimal(SysConstants.WALLET_REGISTER_COST);
        }

        if(manageCost == null || manageCost.compareTo(BigDecimal.ZERO) <= 0){
            throw new BusinessException("续费金额不能小于0");
        }


        PayMethod payMethod = PayMethod.WALLET;
        if(tradeType == TradeType.QUICK_PAY){
            payMethod = PayMethod.BANK;
        }

        String orderNo = String.valueOf(snowflakeIdWorker.nextId());
        String notifyUrl = payHelper.resolveRenewNotifyUrl(orderNo);

        LocalDateTime endTime = startTime.plusDays(buyDuration);

        LocalDateTime expireTime = payHelper.getExpireTime(LocalDateTime.now());
        String timeExpire = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(expireTime);

        WalletRenewOrder.WalletRenewOrderBuilder builder = WalletRenewOrder.builder();
        builder.goodsTitle("钱包管理费")
                .orderNo(orderNo)
                .userId(userId)
                .buyDuration(buyDuration)
                .startTime(startTime)
                .endTime(endTime)
                .userFundAccountId(userFundAccount.getId())
                .payMethod(payMethod)
                .userBankCardId(userFundAccount.getUserBankCardId())
                .payAmt(manageCost)
                .status(PayStatus.UNPAID)
                .timeExpire(timeExpire)
                .notifyUrl(notifyUrl)
                .payPlatform(UniformPayConstants.LIANLIANPAY_PLATFORM);

        WalletRenewOrder walletRenewOrder = builder.build();
        walletRenewOrder.setPayTool(tradeType.name());

        //使用后台充值方式
        JSONObject res = lianlianPayService.payCreate(walletRenewOrder, hasWallet);
        String accpTxno = res.getString("accp_txno");

        walletRenewOrderRepo.save(walletRenewOrder);

        return walletRenewOrder;
    }


    /**
     * 支付续费
     * @return
     */
    @RedisLockable(prefix = RedisKeys.WALLET_PAY_CONFIRM_LOCK, key = "#orderNo")
    @Transactional
    public WalletRenewOrder renewOrderExecute(String orderNo, String password, String randomKey){
        WalletRenewOrder walletRenewOrder = walletRenewOrderRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("续费记录未找到"));
        if(walletRenewOrder.getStatus() != PayStatus.UNPAID){
            throw new BusinessException("支付状态不正确(" + walletRenewOrder.getStatus().getDescription() + ")");
        }

        JSONObject riskItem = lianlianPayService.getRiskItem(walletRenewOrder.getUserId(), walletRenewOrder.getGoodsTitle());

        JSONObject tradeResult;
        if(walletRenewOrder.getPayMethod() == PayMethod.BANK){
            UserBankCard userBankCard = userBankCardRepo.findByUserId(walletRenewOrder.getUserId())
                    .stream().findAny().orElseThrow(new BusinessException("用户未绑定银行卡，无法使用快捷支付"));
            tradeResult = lianlianPayService.paymentQuickpay(userBankCard, orderNo, walletRenewOrder.getPayAmt(),
                    password, randomKey, riskItem);

            //首次支付返回绑卡协议
            String linkedAgrtno = tradeResult.getString("linked_agrtno");
            if(StringUtils.isNotEmpty(linkedAgrtno)){
                userBankCard.setThirdBankId(linkedAgrtno);
                userBankCardRepo.save(userBankCard);
            }

            walletRenewOrder.setUserBankCardId(userBankCard.getId());

        }else{
            //允许余额支付续费订单
            UserFundAccount lianlianAccount = getNormalAccount(walletRenewOrder.getUserId());
            tradeResult = lianlianPayService.paymentBalance(lianlianAccount, orderNo, walletRenewOrder.getPayAmt(),
                    password, randomKey, riskItem);
        }


        //有token表示需要二次校验
        String token = tradeResult.getString("token");
        walletRenewOrder.setToken(token);
        if(StringUtils.isEmpty(token) && walletRenewOrder.getStatus() == PayStatus.UNPAID){
            walletRenewOrder.setStatus(PayStatus.PENDING);
            //记录钱包支付流水，快捷支付的用户不一定有钱包账户，所以不记录支付流水！！！
            if(walletRenewOrder.getPayMethod() == PayMethod.WALLET){
                userFundChangeLogService.createByRenew(walletRenewOrder);
            }
        }

        walletRenewOrderRepo.saveAndFlush(walletRenewOrder);
        return walletRenewOrder;
    }


    /**
     * 续费通知处理
     * @return
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public String renewNotify(UniformNotifyRequest request){

        String content = "Success";

        String orderNo = request.getOrderNo();
        String reqData = request.getRequestBody();
        String fromSign = request.getHeader("Signature-Data");

        WalletRenewOrder renewOrder = walletRenewOrderRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("续费订单未找到"));

        JSONObject bodyJson = lianlianPayService.checkSuccessBody(reqData, fromSign);

        String finishTime = bodyJson.getString("finish_time");//格式：yyyyMMddHHmmss
        String transactionId = bodyJson.getString("accp_txno");//系统交易单号
        String orderStatus = bodyJson.getString("txn_status");

        if(StringUtils.isNotEmpty(finishTime)){
            LocalDateTime finishTimeObj = DateUtil.parse(finishTime).toLocalDateTime();
            renewOrder.setPayTime(finishTimeObj);
        }

        //1：成功
        renewOrder.setNotifyTime(LocalDateTime.now());
        renewOrder.setTransactionId(transactionId);
        if("TRADE_SUCCESS".equals(orderStatus)){
            renewOrder.setStatus(PayStatus.HAVE_PAID);
            renewOrder.setResult("成功");
            //更新资金流水
            userFundChangeLogService.updateSuccess(renewOrder.getUserId(), FundChangeType.RENEW,
                    renewOrder.getOrderNo());

            //更新续费时长
            UserFundAccount userFundAccount = userFundAccountRepo.findById(renewOrder.getUserFundAccountId()).orElse(null);
            if(userFundAccount != null){
                userFundAccount.setExpireTime(renewOrder.getEndTime());
                userFundAccountRepo.save(userFundAccount);
            }

        }else{
            renewOrder.setStatus(PayStatus.FAILED_PAID);
            renewOrder.setResult("失败:" + orderStatus);

            //更新资金流水
            userFundChangeLogService.updateFailed(renewOrder.getUserId(), FundChangeType.RENEW,
                    renewOrder.getOrderNo());
        }

        walletRenewOrderRepo.save(renewOrder);
        return content;

    }


    /**
     * 续费确认
     * @param orderNo
     * @param verifyCode
     * @return
     */
    @RedisLockable(prefix = RedisKeys.WALLET_PAY_CONFIRM_LOCK, key = "#orderNo")
    @Transactional
    public WalletRenewOrder renewOrderConfirm(String orderNo, String verifyCode){
        if(StringUtils.isEmpty(verifyCode)){
            throw new BusinessException("短信验证码不能为空");
        }

        WalletRenewOrder walletRenewOrder = walletRenewOrderRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("续费记录未找到"));
        if(StringUtils.isEmpty(walletRenewOrder.getToken())){
            throw new BusinessException("缺失授权令牌，请确认是否需要二次校验");
        }

        if(walletRenewOrder.getStatus() != PayStatus.UNPAID){
            throw new BusinessException("支付状态不正确(" + walletRenewOrder.getStatus().getDescription() + ")");
        }

        if(walletRenewOrder.getPayMethod() == PayMethod.WALLET){
            //记录钱包支付流水
            userFundChangeLogService.createByRenew(walletRenewOrder);
        }

        walletRenewOrder.setStatus(PayStatus.PENDING);
        walletRenewOrderRepo.saveAndFlush(walletRenewOrder);

        lianlianPayService.tradeValidationSms(walletRenewOrder.getUserId(), walletRenewOrder.getOrderNo()
                , walletRenewOrder.getPayAmt(), walletRenewOrder.getToken(), verifyCode);

        return walletRenewOrder;
    }


    /**
     * 支付订单
     * @return
     */
    @RedisLockable(prefix = RedisKeys.WALLET_PAY_CONFIRM_LOCK, key = "#orderNo")
    @Transactional
    public PaymentInfo collectionOrderExecute(String orderNo, String password, String randomKey){
        PaymentInfo paymentInfo = paymentInfoRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("支付记录未找到"));
        if(paymentInfo.getStatus() != PayStatus.UNPAID){
            throw new BusinessException("支付状态不正确(" + paymentInfo.getStatus().getDescription() + ")");
        }

        JSONObject riskItem = lianlianPayService.getRiskItem(paymentInfo.getUserId(), paymentInfo.getGoodsTitle());
        JSONObject tradeResult;
        if(paymentInfo.getPayMethod() == PayMethod.BANK){
            UserBankCard userBankCard = userBankCardRepo.findByUserId(paymentInfo.getUserId())
                    .stream().findAny().orElseThrow(new BusinessException("用户未绑定银行卡，无法使用快捷支付"));
            tradeResult = lianlianPayService.paymentQuickpay(userBankCard, orderNo, paymentInfo.getPayAmt(),
                    password, randomKey, riskItem);

            //首次支付返回绑卡协议
            String linkedAgrtno = tradeResult.getString("linked_agrtno");
            if(StringUtils.isNotEmpty(linkedAgrtno)){
                userBankCard.setThirdBankId(linkedAgrtno);
                userBankCardRepo.save(userBankCard);
            }

            paymentInfo.setUserBankCardId(userBankCard.getId());
            paymentInfo.setPayChannel("quickpay");

        }else{
            //余额支付
            UserFundAccount lianlianAccount = getValidAccount(paymentInfo.getUserId());
            tradeResult = lianlianPayService.paymentBalance(lianlianAccount, orderNo, paymentInfo.getPayAmt(),
                    password, randomKey, riskItem);
            paymentInfo.setPayChannel("balance");
        }


        //有token表示需要二次校验
        String token = tradeResult.getString("token");
        paymentInfo.setToken(token);
        if(StringUtils.isEmpty(token) && paymentInfo.getStatus() == PayStatus.UNPAID){
            paymentInfo.setStatus(PayStatus.PENDING);
            //记录钱包支付流水，快捷支付的用户不一定有钱包账户，所以不记录支付流水！！！
            if(paymentInfo.getPayMethod() == PayMethod.WALLET){
                userFundChangeLogService.createByPayment(paymentInfo);
            }
        }

        paymentInfoRepo.saveAndFlush(paymentInfo);
        return paymentInfo;
    }


    /**
     * 消费支付确认
     * @param orderNo
     * @param verifyCode
     * @return
     */
    @RedisLockable(prefix = RedisKeys.WALLET_PAY_CONFIRM_LOCK, key = "#orderNo")
    @Transactional
    public PaymentInfo payConfirm(String orderNo, String verifyCode){
        if(StringUtils.isEmpty(verifyCode)){
            throw new BusinessException("短信验证码不能为空");
        }

        PaymentInfo paymentInfo = paymentInfoRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("支付订单未找到"));
        if(StringUtils.isEmpty(paymentInfo.getToken())){
            throw new BusinessException("缺失授权令牌，请确认是否需要二次校验");
        }

        if(paymentInfo.getStatus() != PayStatus.UNPAID){
            throw new BusinessException("支付状态不正确(" + paymentInfo.getStatus().getDescription() + ")");
        }

        if(paymentInfo.getPayMethod() == PayMethod.WALLET){
            //记录钱包支付流水
            userFundChangeLogService.createByPayment(paymentInfo);
        }

        paymentInfo.setStatus(PayStatus.PENDING);
        paymentInfoRepo.saveAndFlush(paymentInfo);

        lianlianPayService.tradeValidationSms(paymentInfo.getUserId(), paymentInfo.getOrderNo()
                , paymentInfo.getPayAmt(), paymentInfo.getToken(), verifyCode);

        return paymentInfo;
    }


    /**
     * 提现申请
     * @param withdrawApplyDTO
     * @return
     */
    @CacheEvict(value=RedisKeys.WALLET_LIANLIAN_BALANCE_CACHE, key="#withdrawApplyDTO.userId")
    @Transactional
    public WithdrawApply createWithdrawApply(WithdrawApplyForm withdrawApplyDTO){
        UserFundAccount account = getValidAccount(withdrawApplyDTO.getUserId());

        UserBankCard userBankcard = userBankCardRepo.findById(account.getUserBankCardId())
                .orElseThrow(new BusinessException("绑卡记录未找到，请重新绑卡"));

        //最小提现金额判断，最小1元，
        BigDecimal withdrawCharge  = sysConfigService.getBigDecimal(SysConstants.WALLET_WITHDRAW_CHARGE);
        BigDecimal manageCost  = sysConfigService.getBigDecimal(SysConstants.WALLET_MANAGE_COST);
        BigDecimal withdrawAmount = withdrawApplyDTO.getWithdrawAmount();
        BigDecimal minAmount = withdrawCharge.add(new BigDecimal(1)).setScale(0, RoundingMode.UP);
        if(minAmount.compareTo(manageCost) < 0){
            minAmount = manageCost;
        }

        if(withdrawAmount == null || withdrawAmount.compareTo(minAmount) < 0){
            throw new BusinessException("提现金额不能小于" + minAmount);
        }

        String password = withdrawApplyDTO.getPassword();
        String randomKey = withdrawApplyDTO.getRandomKey();
//        if(StringUtils.isEmpty(password) || StringUtils.isEmpty(randomKey)){
//            throw new BusinessException("支付密码或随机因数不能为空");
//        }

        String frontUrl = withdrawApplyDTO.getFrontUrl();
        if(StringUtils.isEmpty(frontUrl)){
            frontUrl = payHelper.resolveHome(withdrawApplyDTO.getProjectId());
        }

        String orderNo = String.valueOf(snowflakeIdWorker.nextId());
        String notifyUrl = payHelper.resolveWithdrawNotifyUrl(orderNo);

        WithdrawApply.WithdrawApplyBuilder builder = WithdrawApply.builder();
        builder.title("用户提现")
                .orderNo(orderNo)
                .userId(account.getUserId())
                .userFundAccountId(account.getId())
                .orderAmount(withdrawAmount)
                .userBankCardId(userBankcard.getId())
                .bankName(userBankcard.getBankName())
                .bankNo(userBankcard.getBankNo())
                .relatedCardNo(userBankcard.getThirdBankId())
                .status(WithdrawStatus.CREATED)
                .notifyUrl(notifyUrl)
                .frontUrl(frontUrl)
                .payPlatform(UniformPayConstants.LIANLIANPAY_PLATFORM);

        WithdrawApply withdrawApply = builder.build();

        //收取用户的手续费
        withdrawApply.setUserFeeAmt(withdrawCharge);

        JSONObject riskItem = lianlianPayService.getRiskItem(account.getUserId(), withdrawApply.getTitle());
        //提现操作
        JSONObject res = lianlianPayService.withdrawApply(withdrawApply, password, randomKey, riskItem);
        BigDecimal feeAmt = res.getBigDecimal("fee_amount");
        if(feeAmt == null){
            feeAmt = BigDecimal.ZERO;
        }

        withdrawApply.setFeeAmt(feeAmt);
        //到账金额
        BigDecimal amount = withdrawApply.getOrderAmount().subtract(feeAmt);
        withdrawApply.setAmount(amount);
        withdrawApply.setAuthWay(AuthWay.PASSWORD);

        //支付授权令牌
        String token = res.getString("token");
        withdrawApply.setToken(token);
        if(StringUtils.isEmpty(token)){
            //不需要二次确认
            withdrawApply.setStatus(WithdrawStatus.PENDING);
            //记录提现流水
            userFundChangeLogService.createByWithdraw(withdrawApply);
        }

        withdrawApplyRepo.save(withdrawApply);

        return withdrawApply;

    }


    /**
     * 提现确认
     * @param orderNo
     * @param verifyCode
     * @return
     */
    @RedisLockable(prefix = RedisKeys.WALLET_PAY_CONFIRM_LOCK, key = "#orderNo")
    @Transactional
    public WithdrawApply withdrawConfirm(String orderNo, String verifyCode){
        if(StringUtils.isEmpty(verifyCode)){
            throw new BusinessException("短信验证码不能为空");
        }

        WithdrawApply  withdrawApply = withdrawApplyRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("提现订单未找到"));
        if(StringUtils.isEmpty(withdrawApply.getToken())){
            throw new BusinessException("缺失授权令牌，请确认是否提交提现方式");
        }

        if(withdrawApply.getStatus() != WithdrawStatus.CREATED){
            throw new BusinessException("提现状态不正确(" + withdrawApply.getStatus().getDescription() + ")");
        }

        withdrawApply.setStatus(WithdrawStatus.PENDING);
        withdrawApplyRepo.saveAndFlush(withdrawApply);
        //记录提现流水
        userFundChangeLogService.createByWithdraw(withdrawApply);

        lianlianPayService.tradeValidationSms(withdrawApply.getUserId(), withdrawApply.getOrderNo()
                , withdrawApply.getOrderAmount(), withdrawApply.getToken(), verifyCode);

        return withdrawApply;
    }

    /**
     * 提现通知处理
     * @return
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public String withdrawNotify(UniformNotifyRequest request){
        String content = "Success";
        String orderNo = request.getOrderNo();
        String reqData = request.getRequestBody();
        String fromSign = request.getHeader("Signature-Data");

        WithdrawApply withdrawApply = withdrawApplyRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("提现申请未找到"));

        JSONObject bodyJson = lianlianPayService.checkSuccessBody(reqData, fromSign);

        String finishTime = bodyJson.getString("finish_time");//格式：yyyyMMddHHmmss
        String transactionId = bodyJson.getString("accp_txno");//系统交易单号
        String orderStatus = bodyJson.getString("txn_status");
        String failure_reason = bodyJson.getString("failure_reason");//失败原因
        String chnl_reason = bodyJson.getString("chnl_reason");//渠道失败原因

        String errorInfo = "失败:" + orderStatus;
        if(StringUtils.isNotEmpty(failure_reason)){
            errorInfo = failure_reason;
        }else if(StringUtils.isNotEmpty(chnl_reason)){
            errorInfo = chnl_reason;
        }


        if(StringUtils.isNotEmpty(finishTime)){
            LocalDateTime finishTimeObj = DateUtil.parse(finishTime).toLocalDateTime();
            withdrawApply.setFinishTime(finishTimeObj);
        }

        //1：成功
        withdrawApply.setNotifyTime(LocalDateTime.now());
        withdrawApply.setTransactionId(transactionId);
        if("TRADE_SUCCESS".equals(orderStatus)){
            withdrawApply.setStatus(WithdrawStatus.SUCCESS);
            withdrawApply.setResult("成功");
            withdrawApplyRepo.saveAndFlush(withdrawApply);
            //更新提现流水
            userFundChangeLogService.updateSuccess(withdrawApply.getUserId(), FundChangeType.WITHDRAW, withdrawApply.getOrderNo());
        }else{
            withdrawApply.setStatus(WithdrawStatus.FAILED);
            withdrawApply.setResult(errorInfo);
            //更新提现流水
            userFundChangeLogService.updateFailed(withdrawApply.getUserId(), FundChangeType.WITHDRAW, withdrawApply.getOrderNo());
        }

        withdrawApplyRepo.save(withdrawApply);
        return content;

    }


    /**
     * 解绑卡通知
     * @param request
     * @return
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public String bindOrUnbindCardNotify(UniformNotifyRequest request){
        String content = "Success";
        String orderNo = request.getOrderNo();
        String reqData = request.getRequestBody();
        String fromSign = request.getHeader("Signature-Data");

        JSONObject bodyJson = lianlianPayService.checkSuccessBody(reqData, fromSign);

        String bizUserId = bodyJson.getString("user_id");
        String txnSeqno = bodyJson.getString("txn_seqno");
        String accpTxno = bodyJson.getString("accp_txno");
        String action = bodyJson.getString("action");
        String result = bodyJson.getString("result");


        //个人解绑银行卡
        if("SUCCESS".equals(result)){
            if("UNLINKEDACCT_IND".equals(action)){
                //解绑成功
                UserBankCard userBankCard = userBankCardRepo.findByUnbindOrderNo(orderNo).orElseThrow(new BusinessException("解绑卡记录未找到"));
                userBankCardRepo.softDeleteById(userBankCard.getId());
            }else if("LINKEDACCT_IND".equals(action)){
                log.info("用户新增绑卡成功，订单：{}", orderNo);
            }else{
                log.warn("不支持的操作类型:{}, 订单：{}" , action, orderNo);
            }

        }else{
            log.warn("处理失败，类型和状态不正确,orderNo：{}， action:{}, result:{}", orderNo, action, result);
        }

        return content;
    }

    /**
     * 分页查询资金流水
     * @param userId
     * @param pageQuery
     * @return
     */
    public TradeLogPageDTO tradeLogList(Long userId, PageQuery pageQuery){
        JSONObject res = lianlianPayService.tradeLogList(Long.valueOf(userId), pageQuery);
        Integer pageNo = res.getInteger("page_no");
        Long totalNum = res.getLong("total_num");
        Integer totalPage = res.getInteger("total_page");

        JSONArray dataList = res.getJSONArray("acctbal_list");
        List<JSONObject> objList = new ArrayList<>();
        dataList.forEach((item) -> {
            objList.add((JSONObject) item);
        });

        PageRequest pageRequest = PageRequest.of(pageQuery.getPage(), pageQuery.getSize());
        TradeLogPageDTO<JSONObject> page = new TradeLogPageDTO<>(objList, pageRequest, totalNum);

        BigDecimal totalOutAmt = res.getBigDecimal("total_out_amt");//出账总金额
        BigDecimal totalInAmt = res.getBigDecimal("total_in_amt");//入账总金额


        page.setTotalOutAmount(totalOutAmt);
        page.setTotalInAmount(totalInAmt);

        return page;

    }



    /**
     * 转账给分账用户
     * @param divMoneyId
     * @return
     */
    @Transactional
    public TransferMoneyInfo transferDivMoney(Long divMoneyId, String reason){
        DivMoney divMoney = divMoneyRepo.findById(divMoneyId).orElseThrow(new BusinessException("分账记录不存在"));
        if(divMoney.getUserId() == null){
            throw new BusinessException("分账的用户ID不存在");
        }

        Order order = orderRepo.findById(divMoney.getOrderId()).orElseThrow(new BusinessException("分账订单未找到"));
        if(order.getStatus() != OrderStatus.FINISH){
            throw new BusinessException("订单状态不正确");
        }

        if(divMoney.getAmount().compareTo(order.getPrice()) > 0){
            throw new BusinessException("分账金额不正确");
        }

        UserFundAccount account = getValidAccount(divMoney.getUserId());
        //无论成功与否，直接标记已处理
        divMoney.setStatus(DivStatus.PROCESSED);
        divMoney.setProcessTime(LocalDateTime.now());


        if(divMoney.getTransferMoneyId() != null){
            //历史记录
            TransferMoneyInfo transferMoneyInfo = transferMoneyInfoRepo.findById(divMoney.getTransferMoneyId()).orElseThrow(new BusinessException("历史转账记录不存在"));
            //是否卡号不重复
            if(transferMoneyInfo.getAccountNo().equals(account.getAccountNo())){
                throw new BusinessException("不允许对重复的账号重新转账");
            }
        }

        //保存新的转账记录
        String tradeNo = String.valueOf(snowflakeIdWorker.nextId());
        TransferMoneyInfo.TransferMoneyInfoBuilder builder = TransferMoneyInfo.builder();
        builder.tradeNo(tradeNo).toUserId(divMoney.getUserId()).transferMethod(TransferMethod.THIRD_ACCOUNT)
                .accountType(AccountType.LIANLIANPAY_USER.name())
                .accountNo(account.getAccountNo())
                .accountName(account.getAccountName())
                .thirdAccountId(account.getThirdUserId())
                .amount(divMoney.getAmount())
                .payPlatform(divMoney.getPayPlatform())
                .reason(reason)
                .divMoneyId(divMoneyId);
        TransferMoneyInfo transferMoneyInfo = transferMoneyInfoRepo.save(builder.build());


        //记录转账记录ID
        divMoney.setTransferMoneyId(transferMoneyInfo.getId());

        try {
            JSONObject res = lianlianPayService.transferMoney(transferMoneyInfo.getToUserId(),
                    transferMoneyInfo.getTradeNo(),
                    transferMoneyInfo.getAmount(), transferMoneyInfo.getReason() );
            String txnSeqno = res.getString("txn_seqno");
            String accpTxno = res.getString("accp_txno");
            String tranTime = res.getString("accounting_date");

            String resultStr = "处理中";
            transferMoneyInfo.setStatus(TransferStatus.PROCESSING);

            transferMoneyInfo.setTransferOrderId(txnSeqno);
            transferMoneyInfo.setTransactionId(accpTxno);
            transferMoneyInfo.setTransferDate(tranTime);
            transferMoneyInfo.setResult(resultStr);

            divMoney.setTransferResult(resultStr);

        }catch(Exception e){
            log.error("转账失败，分账ID:" + divMoneyId, e);
            transferMoneyInfo.setStatus(TransferStatus.FAILED);
            transferMoneyInfo.setResult(e.getMessage());
            divMoney.setStatus(DivStatus.ERROR);
            divMoney.setTransferResult(e.getMessage());
        }

        transferMoneyInfoRepo.save(transferMoneyInfo);
        divMoneyRepo.save(divMoney);

        return transferMoneyInfo;
    }


    /**
     * 转账给用户
     * @param userId
     * @param transferAmount
     * @param reason
     * @return
     */
    @Transactional
    public TransferMoneyInfo createTransferToUser(Long userId, BigDecimal transferAmount, String reason){

        if(transferAmount.compareTo(BigDecimal.ZERO) <= 0){
            throw new BusinessException("转账金额不能小于0");
        }

        if(transferAmount.compareTo(TRANSFER_LIMIT)  > 0){
            throw new BusinessException("单笔转账不能超过：" + TRANSFER_LIMIT);
        }

        if(StringUtils.isBlank(reason)){
            throw new BusinessException("转账理由不能为空");
        }

        List<TransferMoneyInfo> sameTransferList = transferMoneyInfoRepo.findBySameReason(userId, reason);
        if(sameTransferList.size() > 0){
            throw new BusinessException("存在一笔相同的转账记录，请换一个转账理由");
        }

        UserFundAccount account = getNormalAccount(userId);

        //保存新的转账记录
        String tradeNo = String.valueOf(snowflakeIdWorker.nextId());
        TransferMoneyInfo.TransferMoneyInfoBuilder builder = TransferMoneyInfo.builder();
        builder.tradeNo(tradeNo).toUserId(userId)
                .transferMethod(TransferMethod.THIRD_ACCOUNT)
                .accountType(AccountType.LIANLIANPAY_USER.name())
                .accountNo(account.getAccountNo())
                .accountName(account.getAccountName())
                .thirdAccountId(account.getThirdUserId())
                .amount(transferAmount)
                .status(TransferStatus.CREATED)
                .payPlatform(UniformPayConstants.LIANLIANPAY_PLATFORM)
                .reason(reason)
                .divMoneyId(null);
        TransferMoneyInfo transferMoneyInfo = transferMoneyInfoRepo.save(builder.build());

        transferMoneyInfoRepo.save(transferMoneyInfo);
        return transferMoneyInfo;
    }


    /**
     * 执行转账交易(参数名不能随意修改)
     * @param orderNo
     * @return
     */
    @RedisLockable(prefix = RedisKeys.WALLET_PAY_CONFIRM_LOCK, key = "#orderNo",
            isWaiting = true, retryCount=20, retryWaitingTime = 100)
    @Transactional
    public TransferMoneyInfo executeTransfer(String orderNo){
        TransferMoneyInfo transferMoneyInfo = transferMoneyInfoRepo.findByTradeNo(orderNo).orElseThrow(new BusinessException("转账记录不存在"));
        if(transferMoneyInfo.getStatus() != TransferStatus.CREATED){
            throw new BusinessException("转账操作已处理过，不能重复执行");
        }

        try {
            JSONObject res = lianlianPayService.transferMoney(transferMoneyInfo.getToUserId(),
                    transferMoneyInfo.getTradeNo(),
                    transferMoneyInfo.getAmount(), transferMoneyInfo.getReason() );
            String txnSeqno = res.getString("txn_seqno");
            String accpTxno = res.getString("accp_txno");
            String tranTime = res.getString("accounting_date");

            String resultStr = "处理中";
            transferMoneyInfo.setStatus(TransferStatus.PROCESSING);
            transferMoneyInfo.setTransferOrderId(txnSeqno);
            transferMoneyInfo.setTransactionId(accpTxno);
            transferMoneyInfo.setTransferDate(tranTime);
            transferMoneyInfo.setResult(resultStr);

            transferMoneyInfoRepo.save(transferMoneyInfo);

            //记录交易流水
            userFundChangeLogService.createByIncome(transferMoneyInfo);

        }catch(Exception e){
            log.error("转账失败，转账订单号:" + orderNo, e);
            transferMoneyInfo.setStatus(TransferStatus.FAILED);
            transferMoneyInfo.setResult(e.getMessage());
        }

        return transferMoneyInfo;
    }




}
