public void generateTransferRepay(
     List<InvestRepay> investRepays, Invest invest, double corpusRate) {
   for (Iterator iterator = investRepays.iterator(); iterator.hasNext(); ) {
     InvestRepay ir = (InvestRepay) iterator.next();
     if (ir.getStatus().equals(RepayStatus.WAIT_REPAY_VERIFY)
         || ir.getStatus().equals(RepayStatus.OVERDUE)
         || ir.getStatus().equals(RepayStatus.BAD_DEBT)) {
       throw new RuntimeException(
           "investRepay with status " + RepayStatus.WAIT_REPAY_VERIFY + "exist!");
     } else if (ir.getStatus().equals(RepayStatus.REPAYING)) {
       // 根据购买本金比例,生成债权还款信息
       InvestRepay irNew = new InvestRepay();
       try {
         BeanUtils.copyProperties(irNew, ir);
       } catch (Exception e) {
         throw new RuntimeException(e);
       }
       irNew.setId(IdGenerator.randomUUID());
       irNew.setCorpus(ArithUtil.mul(ir.getCorpus(), corpusRate, 2));
       irNew.setDefaultInterest(ArithUtil.mul(ir.getDefaultInterest(), corpusRate, 2));
       irNew.setFee(ArithUtil.mul(ir.getFee(), corpusRate, 2));
       irNew.setInterest(ArithUtil.mul(ir.getInterest(), corpusRate, 2));
       irNew.setInvest(invest);
       // 修改原投资的还款信息
       ir.setCorpus(ArithUtil.sub(ir.getCorpus(), irNew.getCorpus()));
       ir.setDefaultInterest(ArithUtil.sub(ir.getDefaultInterest(), irNew.getDefaultInterest()));
       ir.setFee(ArithUtil.sub(ir.getFee(), irNew.getFee()));
       ir.setInterest(ArithUtil.sub(ir.getInterest(), irNew.getInterest()));
       ht.merge(irNew);
       if (corpusRate == 1) {
         ht.delete(ir);
         iterator.remove();
       } else {
         ht.update(ir);
       }
     }
   }
 }
  @Override
  @Transactional(rollbackFor = Exception.class)
  public TrusteeshipOperation createOperation(Invest invest, FacesContext fc) throws IOException {
    String transferApplyId = invest.getTransferApply().getId();
    // 购买债权人的id
    String userId = invest.getUser().getId();
    // 购买的债权本金 transferCorpus(投资时填写的投资额)
    double transferCorpus = invest.getInvestMoney();
    double remainCorpus = transferService.calculateRemainCorpus(transferApplyId);
    if (transferCorpus <= 0 || transferCorpus > remainCorpus) {
      throw new YeePayOperationException("购买本金必须小于等于" + remainCorpus + "且大于0");
    }
    TransferApply ta = ht.get(TransferApply.class, transferApplyId);
    // 购买的本金占剩余本金的比例corpusRate
    double corpusRate = ArithUtil.div(transferCorpus, remainCorpus);
    // 购买的本金占所有转让本金的比例corpusRateInAll
    double corpusRateInAll = ArithUtil.div(transferCorpus, ta.getCorpus());
    // 投资人实际出的钱=债权的购买金额=债权转出价格*corpusRateInAll (债权转出价格=本金-折让金)
    double buyPrice = ArithUtil.mul(ta.getPrice(), corpusRateInAll, 2);

    if (buyPrice > userBillBO.getBalance(userId)) {
      throw new YeePayOperationException("余额不足。");
    }

    // 购买时候,扣除手续费,从转让人收到的金额中扣除。费用根据购买价格计算
    double fee = feeConfigBO.getFee(FeePoint.TRANSFER, FeeType.FACTORAGE, null, null, buyPrice);

    Invest investNew = new Invest();
    investNew.setId(IdGenerator.randomUUID());
    investNew.setMoney(transferCorpus);
    investNew.setStatus(InvestConstants.InvestStatus.CANCEL);
    investNew.setRate(ta.getInvest().getRate());
    investNew.setTransferApply(ta);
    investNew.setLoan(ta.getInvest().getLoan());
    investNew.setTime(new Date());
    investNew.setUser(new User(userId));
    investNew.setInvestMoney(transferCorpus);
    // 在这里添加投标状态是手动投标
    investNew.setIsAutoInvest(false);
    ht.save(investNew);
    ht.update(ta);

    DecimalFormat currentNumberFormat = new DecimalFormat("#0.00");
    // 构建投资参数实体
    CPTransaction cp = new CPTransaction();
    cp.setNotifyUrl(
        YeePayConstants.ResponseS2SUrl.PRE_RESPONSE_URL + YeePayConstants.OperationType.TRANSFER);
    cp.setCallbackUrl(
        YeePayConstants.ResponseWebUrl.PRE_RESPONSE_URL + YeePayConstants.OperationType.TRANSFER);
    // cp.setExpired(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(DateUtil.addMinute(new
    // Date(), 10)));
    cp.setRequestNo(YeePayConstants.RequestNoPre.TRANSFER + investNew.getId());
    cp.setPlateformNo(YeePayConstants.Config.MER_CODE);
    cp.setPlatformUserNo(userId);
    cp.setBizType(YeepayCpTransactionConstant.CREDIT_ASSIGNMENT.name());
    cp.setUserType("MEMBER");

    // 资金明细
    FundDetail fd = new FundDetail();
    // 给转让债权的人的钱
    fd.setAmount(currentNumberFormat.format(buyPrice - fee));
    fd.setTargetPlatformUserNo(ta.getInvest().getUser().getId());
    fd.setTargetUserType("MEMBER");
    fd.setBizType(YeepayCpTransactionConstant.CREDIT_ASSIGNMENT.name());
    cp.getFundDetails().add(fd);

    // 平台分润
    if (fee > 0.0) {
      fd = new FundDetail();
      // 投资人给平台的钱=fee
      fd.setAmount(currentNumberFormat.format(fee));
      fd.setTargetPlatformUserNo(YeePayConstants.Config.MER_CODE);
      fd.setTargetUserType("MERCHANT");
      fd.setBizType(YeepayCpTransactionConstant.COMMISSION.name());
      cp.getFundDetails().add(fd);
    }

    // 扩展参数
    Extend extend = new Extend();
    Loan loan = ta.getInvest().getLoan();
    extend.setTenderOrderNo(loan.getId());
    extend.setCreditorPlatformUserNo(ta.getInvest().getUser().getId());
    // 判断购买的债券是否是自动投标
    if (ta.getInvest().getIsAutoInvest()) {
      extend.setOriginalRequestNo(
          YeePayConstants.RequestNoPre.AUTO_INVEST + ta.getInvest().getId());
    } else {
      extend.setOriginalRequestNo(YeePayConstants.RequestNoPre.INVEST + ta.getInvest().getId());
    }
    cp.setExtend(extend);
    log.debug(cp.toString());

    Map<String, String> params = new HashMap<String, String>();
    params.put("req", cp.toString());
    String sign = CFCASignUtil.sign(cp.toString());
    params.put("sign", sign);

    // 保存本地
    TrusteeshipOperation to = new TrusteeshipOperation();
    to.setId(IdGenerator.randomUUID());
    to.setMarkId(investNew.getId());
    to.setOperator(Double.toString(buyPrice) + "&" + Double.toString(corpusRate) + "&" + userId);
    to.setRequestUrl(YeePayConstants.RequestUrl.TRANSFER);
    to.setRequestData(MapUtil.mapToString(params));
    to.setStatus(TrusteeshipConstants.Status.UN_SEND);
    to.setType(YeePayConstants.OperationType.TRANSFER);
    to.setTrusteeship("yeepay");
    trusteeshipOperationBO.save(to);
    try {
      // 发送信息
      sendOperation(to.getId(), "utf-8", fc);
    } catch (IOException e) {
      e.printStackTrace();
    }

    return null;
  }
  @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());
  }
  @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("还款不处于逾期还款状态");
    }
  }
  @Override
  @Transactional(rollbackFor = Exception.class)
  public TrusteeshipOperation createOperation(BankCard bankCard, FacesContext fc)
      throws IOException {

    // String resquestNo = IdGenerator.randomUUID();
    String userId = bankCard.getUser().getId();
    String hql = "from BankCard bc where bc.user.id = ? and bc.status = ?";
    List l = ht.find(hql, new String[] {userId, "VERIFIED"});
    BankCard bc = null;
    if (l.size() > 0) {
      bc = (BankCard) l.get(0);
    } else {
      FacesUtil.addErrorMessage("未找到银行卡信息。");
      return null;
    }

    StringBuffer content = new StringBuffer();
    content.append("<?xml version='1.0' encoding='utf-8'?>");
    // 商户编号:商户在易宝唯一标识
    content.append("<request platformNo='" + YeePayConstants.Config.MER_CODE + "'>");
    // 商户平台会员标识:会员在商户平台唯一标识
    content.append("<platformUserNo>" + userId + "</platformUserNo>");
    // 请求流水号 银行卡的 id
    content.append(
        "<requestNo>"
            + YeePayConstants.RequestNoPre.UNBINDING_YEEPAY_BANKCARD
            + bc.getId()
            + "</requestNo>");
    // 页面回跳URL
    content.append(
        "<callbackUrl>"
            + YeePayConstants.ResponseWebUrl.PRE_RESPONSE_URL
            + YeePayConstants.OperationType.UNBINDING_YEEPAY_BANKCARD
            + "</callbackUrl>");
    // 服务器通知 URL:服务器通知 URL
    content.append(
        "<notifyUrl>"
            + YeePayConstants.ResponseS2SUrl.PRE_RESPONSE_URL
            + YeePayConstants.OperationType.UNBINDING_YEEPAY_BANKCARD
            + "</notifyUrl>");
    content.append("</request>");

    // 包装参数
    Map<String, String> params = new HashMap<String, String>();
    params.put("req", content.toString());
    String sign = CFCASignUtil.sign(content.toString());
    params.put("sign", sign);
    log.debug(content.toString());
    log.debug(sign);

    // 保存本地
    TrusteeshipOperation to = new TrusteeshipOperation();
    to.setId(IdGenerator.randomUUID());
    to.setMarkId(bc.getId());
    to.setOperator(bc.getId());
    to.setRequestUrl(YeePayConstants.RequestUrl.UNBINDING_YEEPAY_BANKCARD);
    to.setRequestData(MapUtil.mapToString(params));
    to.setStatus(TrusteeshipConstants.Status.UN_SEND);
    to.setType(YeePayConstants.OperationType.UNBINDING_YEEPAY_BANKCARD);
    to.setTrusteeship("yeepay");
    trusteeshipOperationBO.save(to);
    try {
      sendOperation(to.getId(), "utf-8", fc);
    } catch (IOException e) {
      e.printStackTrace();
    }
    return null;
  }