@Transactional(rollbackFor = Exception.class)
  public void overdueRepay(String repayId, String repayerId)
      throws InsufficientBalance, OverdueRepayException {
    LoanRepay lr = ht.get(LoanRepay.class, repayId);
    ht.evict(lr);
    lr = ht.get(LoanRepay.class, repayId, LockMode.UPGRADE);
    if (lr.getStatus().equals(LoanConstants.RepayStatus.OVERDUE)
        || lr.getStatus().equals(LoanConstants.RepayStatus.BAD_DEBT)
        || lr.getStatus().equals(RepayStatus.WAIT_REPAY_VERIFY)) {
      List<InvestRepay> irs =
          ht.find(
              "from InvestRepay ir where ir.invest.loan.id=? and ir.period=?",
              new Object[] {lr.getLoan().getId(), lr.getPeriod()});

      double defaultInterest = lr.getDefaultInterest();

      HashSet<Invest> invests = new HashSet<Invest>();

      // 更改投资的还款信息
      for (InvestRepay ir : irs) {
        ir.setStatus(LoanConstants.RepayStatus.COMPLETE);
        ir.setTime(new Date());
        ht.update(ir);
        invests.add(ir.getInvest());
        userBillBO.transferIntoBalance(
            ir.getInvest().getUser().getId(),
            ArithUtil.add(ir.getCorpus(), ir.getInterest(), ir.getDefaultInterest()),
            OperatorInfo.OVERDUE_REPAY,
            "投资:"
                + ir.getInvest().getId()
                + "收到还款, 还款ID:"
                + lr.getId()
                + "  借款ID:"
                + lr.getLoan().getId()
                + "  本金:"
                + ir.getCorpus()
                + "  利息:"
                + ir.getInterest()
                + "  罚息:"
                + ir.getDefaultInterest());
        // 系统回收体验金
        if (ir.getCorpusToSystem() != null && ir.getCorpusToSystem() > 0) {
          systemBillService.transferInto(
              ir.getCorpusToSystem(),
              OperatorInfo.OVERDUE_REPAY,
              "投资:" + ir.getInvest().getId() + "收到还款,回收体验金, 还款ID:" + ir.getId());
        }

        defaultInterest = ArithUtil.sub(defaultInterest, ir.getDefaultInterest());
        // 投资者手续费
        userBillBO.transferOutFromBalance(
            ir.getInvest().getUser().getId(),
            ir.getFee(),
            OperatorInfo.OVERDUE_REPAY,
            "投资:" + ir.getInvest().getId() + "收到还款,扣除手续费, 还款ID:" + lr.getId());
        systemBillService.transferInto(
            ir.getFee(),
            OperatorInfo.OVERDUE_REPAY,
            "投资:"
                + ir.getInvest().getId()
                + "收到还款,扣除手续费, 还款ID:"
                + lr.getId()
                + ",项目ID:"
                + ir.getInvest().getLoan().getId());
      }

      // 更改借款的还款信息
      double payMoney =
          ArithUtil.add(
              ArithUtil.add(lr.getCorpus(), lr.getInterest()),
              lr.getFee(),
              lr.getDefaultInterest());
      lr.setTime(new Date());
      lr.setStatus(LoanConstants.RepayStatus.COMPLETE);
      // 记录repayWay信息,还款者id,如果有此id,则为代偿
      lr.setRepayWay(repayerId);

      // 代偿账户,扣除还款。
      userBillBO.transferOutFromBalance(
          repayerId,
          payMoney,
          OperatorInfo.OVERDUE_REPAY,
          "借款:"
              + lr.getLoan().getId()
              + "逾期还款, 还款ID:"
              + lr.getId()
              + " 本金:"
              + lr.getCorpus()
              + "  利息:"
              + lr.getInterest()
              + "  手续费:"
              + lr.getFee()
              + "  罚息:"
              + lr.getDefaultInterest());
      // 借款者手续费
      systemBillService.transferInto(
          lr.getFee(),
          OperatorInfo.OVERDUE_REPAY,
          "项目ID:" + lr.getLoan().getId() + "逾期还款,扣除手续费, 还款ID:" + lr.getId());
      // 罚息转入网站账户
      systemBillService.transferInto(
          defaultInterest,
          OperatorInfo.OVERDUE_REPAY,
          "项目ID:" + lr.getLoan().getId() + "逾期还款,扣除罚金, 还款ID:" + lr.getId());
      ht.merge(lr);
      Long count =
          (Long)
              ht.find(
                      "select count(repay) from LoanRepay repay where repay.loan.id=? and (repay.status=? or repay.status=?)",
                      lr.getLoan().getId(),
                      RepayStatus.OVERDUE,
                      RepayStatus.BAD_DEBT)
                  .get(0);
      if (count == 0) {
        // 如果没有逾期或者坏账的还款,则更改借款状态。
        lr.getLoan().setStatus(LoanStatus.REPAYING);
        ht.update(lr.getLoan());
        for (Invest invest : invests) {
          invest.setStatus(InvestStatus.REPAYING);
          ht.update(invest);
        }
      }
      // 如果不是自己还款,则产生代偿
      if (!lr.getLoan().getUser().getId().equals(repayerId)) {
        RepayCompensation rc = new RepayCompensation();
        rc.setId(IdGenerator.randomUUID());
        rc.setLoanRepay(lr);
        rc.setRepayer(new User(repayerId));
        rc.setCompensateTime(new Date());
        rc.setStatus(CompensationStatus.COMPENSATED);
        ht.save(rc);
      }
      // 判断是否所有还款结束,更改等待还款的投资状态和还款状态,还有项目状态。
      loanService.dealComplete(lr.getLoan().getId());
      try {
        cancelTransfering(lr.getLoan().getId());
      } catch (RepayException e) {
        throw new OverdueRepayException(e.getMessage(), e.getCause());
      }
    } else {
      throw new OverdueRepayException("还款不处于逾期还款状态");
    }
  }
  @SuppressWarnings("unchecked")
  @Override
  @Transactional(rollbackFor = Exception.class)
  public void receiveOperationS2SCallback(ServletRequest request, ServletResponse response) {
    try {
      request.setCharacterEncoding("UTF-8");
    } catch (UnsupportedEncodingException e) {
      throw new RuntimeException(e);
    }

    String respXML = request.getParameter("notify"); // 响应的参数 为xml格式
    log.debug("债权转让s2sCallback respXML:" + respXML.toString());
    String sign = request.getParameter("sign"); // 签名
    boolean flag = CFCASignUtil.isVerifySign(respXML, sign);

    if (flag) { // 验签成功

      Map<String, String> map = Dom4jUtil.xmltoMap(respXML);
      String code = map.get("code");
      String requestNo =
          map.get("requestNo").replaceFirst(YeePayConstants.RequestNoPre.TRANSFER, "");
      if ("1".equals(code)) { // 易宝冻结金额成功

        TrusteeshipOperation to =
            trusteeshipOperationBO.get(YeePayConstants.OperationType.TRANSFER, requestNo, "yeepay");
        ht.evict(to);
        to =
            trusteeshipOperationBO.get(YeePayConstants.OperationType.TRANSFER, requestNo, "yeepay");
        ht.evict(to);
        to = ht.get(TrusteeshipOperation.class, to.getId(), LockMode.UPGRADE);
        String[] params = to.getOperator().split("&");
        Invest invest = ht.get(Invest.class, requestNo); // 债权购买人购买的债权转换成的一笔投资
        TransferApply ta = ht.get(TransferApply.class, invest.getTransferApply().getId()); // 债权转让申请
        if (TrusteeshipConstants.Status.PASSED.equals(to.getStatus())) {
          return;
        }
        // 调用易宝转账确认端口
        boolean result =
            yeepayCpTransacionOperation.transactionComform(
                YeePayConstants.RequestNoPre.TRANSFER + requestNo, "CONFIRM");
        if (result) { // 易宝转账确认成功

          to.setStatus(TrusteeshipConstants.Status.PASSED);
          ht.update(to);

          /**
           * ***********************************平台操作begin**************************************************
           */
          if (invest != null && ta != null) {

            invest.setStatus(InvestConstants.InvestStatus.REPAYING);
            invest.setTime(new Date()); // 成交时间

            Invest orignInvest = ta.getInvest(); // 债权转让人转让的一笔投资
            double investReaminCorpus =
                ArithUtil.sub(orignInvest.getMoney(), invest.getMoney()); // 原投资债权转让后剩余的投资金额
            if (investReaminCorpus == 0.0) {
              orignInvest.setStatus(InvestStatus.COMPLETE);
            }
            orignInvest.setMoney(investReaminCorpus);

            double remainCorpus = transferService.calculateRemainCorpus(ta.getId()); // 未转出的本金
            if (remainCorpus > 0.0) { // 债权未全部转出
              ta.setStatus(TransferStatus.TRANSFERING);
            } else { // 债权全部转出
              ta.setStatus(TransferStatus.TRANSFED);
            }
            // 债权的购买金额
            double buyPrice = Double.parseDouble(params[0]);
            // 购买时候,扣除手续费,从转让人收到的金额中扣除。费用根据购买价格计算
            double fee =
                feeConfigBO.getFee(FeePoint.TRANSFER, FeeType.FACTORAGE, null, null, buyPrice);
            // 购买时候,扣除手续费,从转让人收到的金额中扣除。费用根据购买价格计算
            try {
              userBillBO.transferOutFromBalance(
                  invest.getUser().getId(),
                  buyPrice,
                  OperatorInfo.TRANSFER_BUY,
                  "债权:" + invest.getId() + "购买成功");
              userBillBO.transferIntoBalance(
                  ta.getInvest().getUser().getId(),
                  buyPrice,
                  OperatorInfo.TRANSFER,
                  "债权:" + invest.getId() + "转让成功");
              if (fee > 0.0) {
                sbs.transferInto(fee, OperatorInfo.TRANSFER, "购买债权手续费,编号:" + invest.getId());
                userBillBO.transferOutFromBalance(
                    ta.getInvest().getUser().getId(),
                    fee,
                    OperatorInfo.TRANSFER,
                    "债权转让成功手续费,编号:" + ta.getId());
              }
            } catch (InsufficientBalance e) {
              log.debug("s2sCallback债权转让平台划款时出错!");
              // throw new RuntimeException("债权转让平台划款时出错!");
            }

            ta.setInvest(orignInvest);
            ht.update(invest);
            ht.update(ta);

            double corpusRate = Double.parseDouble(params[1]); // 购买的本金占剩余本金的比例
            generateTransferRepay(
                ta.getInvest().getInvestRepays(), invest, corpusRate); // 生成购买债权后的还款数据,调整之前的还款数据

            /**
             * ***********************************平台操作end*********************************************************
             */
          }

        } else { // 易宝转账确认失败

          to.setStatus(TrusteeshipConstants.Status.REFUSED);
          ht.update(to);

          /**
           * ***********************************平台操作begin**************************************************
           * if(invest != null && ta != null){
           * invest.setStatus(InvestConstants.InvestStatus.CANCEL);
           * ta.setStatus(TransferStatus.TRANSFERING); double buyPrice
           * =Double.parseDouble(params[0]) ;//债权的购买金额 try {
           * userBillBO.unfreezeMoney(invest.getUser().getId(),buyPrice,OperatorInfo.TRANSFER, "债权:"
           * + invest.getId()+ "购买失败"); } catch (InsufficientBalance e) { throw new
           * RuntimeException("购买债权:"+ invest.getId() + "失败,解冻金额时出错!"); } ht.update(invest);
           * ht.update(ta); }
           * ***********************************平台操作end**************************************************
           */
          // throw new YeePayOperationException("债权转让易宝转账确认失败");
        }
      }

    } else { // 验签失败
      log.debug("债权转让验签失败");
    }
  }
  @Transactional(readOnly = false, rollbackFor = Exception.class)
  public void normalRepay(LoanRepay repay, String repayerId)
      throws InsufficientBalance, NormalRepayException {
    ht.evict(repay);
    repay = ht.get(LoanRepay.class, repay.getId(), LockMode.UPGRADE);
    // 正常还款
    if (!(repay.getStatus().equals(LoanConstants.RepayStatus.REPAYING)
        && !(repay.getStatus().equals(LoanConstants.RepayStatus.REPAYING_BACK)))) {
      // 该还款不处于正常还款状态。
      throw new NormalRepayException("还款:" + repay.getId() + "不处于正常还款状态。");
    }
    List<InvestRepay> irs =
        ht.find(
            "from InvestRepay ir where ir.invest.loan.id=? and ir.period=?",
            new Object[] {repay.getLoan().getId(), repay.getPeriod()});

    // TODO:投资的所有还款信息加和,判断是否等于借款的还款信息,如果不相等,抛异常

    // 更改投资的还款信息
    for (InvestRepay ir : irs) {

      ir.setStatus(LoanConstants.RepayStatus.COMPLETE);
      ir.setTime(new Date());
      ht.update(ir);

      userBillBO.transferIntoBalance(
          ir.getInvest().getUser().getId(),
          ArithUtil.add(ir.getCorpus(), ir.getInterest()),
          OperatorInfo.NORMAL_REPAY,
          "投资:"
              + ir.getInvest().getId()
              + "收到还款, 还款ID:"
              + repay.getId()
              + "  借款ID:"
              + repay.getLoan().getId()
              + "  本金:"
              + ir.getCorpus()
              + "  利息:"
              + ir.getInterest());
      if (ir.getCorpusToSystem() != null && ir.getCorpusToSystem() != 0) {
        // 系统回收体验金
        systemBillService.transferInto(
            ir.getCorpusToSystem(),
            OperatorInfo.NORMAL_REPAY,
            "投资:" + ir.getInvest().getId() + "收到还款,回收体验金, 还款ID:" + repay.getId());
      }
      // 投资者手续费
      userBillBO.transferOutFromBalance(
          ir.getInvest().getUser().getId(),
          ir.getFee(),
          OperatorInfo.NORMAL_REPAY,
          "投资:" + ir.getInvest().getId() + "收到还款,扣除手续费, 还款ID:" + repay.getId());
      systemBillService.transferInto(
          ir.getFee(),
          OperatorInfo.NORMAL_REPAY,
          "投资:"
              + ir.getInvest().getId()
              + "收到还款,扣除手续费, 还款ID:"
              + repay.getId()
              + ",项目ID:"
              + ir.getInvest().getLoan().getId());
    }

    try {
      cancelTransfering(repay.getLoan().getId());
    } catch (RepayException e) {
      throw new NormalRepayException(e.getMessage(), e.getCause());
    }

    // 更改借款的还款信息
    double payMoney =
        ArithUtil.add(ArithUtil.add(repay.getCorpus(), repay.getInterest()), repay.getFee());
    repay.setTime(new Date());
    repay.setStatus(LoanConstants.RepayStatus.COMPLETE);
    // 记录repayWay信息,还款者id,如果有此id,则为代偿
    repay.setRepayWay(repayerId);

    // 借款者的账户,扣除还款。
    userBillBO.transferOutFromBalance(
        repayerId,
        payMoney,
        OperatorInfo.NORMAL_REPAY,
        "借款:"
            + repay.getLoan().getId()
            + "正常还款, 还款ID:"
            + repay.getId()
            + " 本金:"
            + repay.getCorpus()
            + "  利息:"
            + repay.getInterest()
            + "  手续费:"
            + repay.getFee());
    // 借款者手续费
    systemBillService.transferInto(
        repay.getFee(),
        OperatorInfo.NORMAL_REPAY,
        "项目ID:" + repay.getLoan().getId() + "正常还款,扣除手续费, 还款ID:" + repay.getId());

    ht.merge(repay);

    // 如果不是自己还款,则产生代偿
    if (!repay.getLoan().getUser().getId().equals(repayerId)) {
      RepayCompensation rc = new RepayCompensation();
      rc.setId(IdGenerator.randomUUID());
      rc.setLoanRepay(repay);
      rc.setRepayer(new User(repayerId));
      rc.setCompensateTime(new Date());
      rc.setStatus(CompensationStatus.COMPENSATED);
      ht.save(rc);
    }

    // 判断是否所有还款结束,更改等待还款的投资状态和还款状态,还有项目状态。
    loanService.dealComplete(repay.getLoan().getId());
  }