package com.liquidnet.service.reconciliation.strategy.accounting.impl;

import com.liquidnet.commons.lang.util.StringUtil;
import com.liquidnet.commons.lang.util.spring.SpringUtils;
import com.liquidnet.service.reconciliation.biz.ReconciliationDataGetBiz;
import com.liquidnet.service.reconciliation.constant.ReconConstants;
import com.liquidnet.service.reconciliation.constant.ReconConstants.BankChannelEnum;
import com.liquidnet.service.reconciliation.constant.ReconConstants.MistakeHandleStatusEnum;
import com.liquidnet.service.reconciliation.constant.ReconConstants.MistakeTypeEnum;
import com.liquidnet.service.reconciliation.constant.ReconConstants.TransStatusEnum;
import com.liquidnet.service.reconciliation.dto.TdFundflowUploadVO;
import com.liquidnet.service.reconciliation.entity.LrAccountCheckBatch;
import com.liquidnet.service.reconciliation.entity.LrAccountCheckBatchSub;
import com.liquidnet.service.reconciliation.entity.LrAccountCheckMistake;
import com.liquidnet.service.reconciliation.entity.LrAccountCheckMistakeScratchPool;
import com.liquidnet.service.reconciliation.service.ILrAccountCheckBatchService;
import com.liquidnet.service.reconciliation.service.ILrAccountCheckMistakeScratchPoolService;
import com.liquidnet.service.reconciliation.service.core.ILrAccountCheckTransactionService;
import com.liquidnet.service.reconciliation.service.core.impl.LrAccountCheckTransactionServiceImpl;
import com.liquidnet.service.reconciliation.service.impl.LrAccountCheckMistakeScratchPoolServiceImpl;
import com.liquidnet.service.reconciliation.strategy.accounting.IComparingAccountStrategy;
import com.liquidnet.service.reconciliation.vo.ReconciliationEntityVo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.sql.DataSource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author AnJiabin <jiabin.an@lightnet.io>
 * @version V1.0
 * @Description: 对账的核心业务模版方法
 * @class: AbstractComparingAccountStrategy
 * @Package com.liquidnet.service.reconciliation.strategy.accounting.impl
 * @Copyright: LightNet @ Copyright (c) 2020
 * @date 2020/10/21 14:20
 */
public abstract class AbstractComparingAccountStrategy implements IComparingAccountStrategy {

	protected final Logger log = LoggerFactory.getLogger(getClass());
	
	//资金流对应银行通道
	protected BankChannelEnum bankChannel;

	//信息流对应银行通道(该平台默认信息流数据获取渠道为 adam，子类根据业务场景动态设置信息流渠道)
	protected BankChannelEnum infoBankChannel = BankChannelEnum.LIQUIDNET_ADAM;

	@Autowired
	protected ReconciliationDataGetBiz reconciliationDataGetBiz;

	@Autowired
	protected ILrAccountCheckMistakeScratchPoolService lrAccountCheckMistakeScratchPoolService;

	@Autowired
	protected ILrAccountCheckTransactionService lrAccountCheckTransactionService;


	/**
	 * 对账主流程
	 * Job执行时，取得至今仍未对账的资金流文件，依时间顺序依次对账
	 * 如果没有未对账的文件，则直接跳出对账流程
	 * <p>
	 * 优化点：增加对账文件的状态--9对账中
	 * 0未对账   9对账中 1已对账
	 * <p>
	 * 对账流程: 针对一个资金流文件进行对账
	 * <p>
	 * 数据源：
	 * 信息流集合： 按照会计日期对应的时间段内的成功交易 + 会计日期之前的所有存疑交易
	 * 资金流集合：该日资金流文件内的所有交易， 根据文件ID找资金流数据
	 * <p>
	 * 对比过程：
	 * 1. 拿资金流集合中的每条交易到信息流中去找
	 * 1.1 如果找到，并匹配一致， 则信息流、资金流都标志为对账一致
	 * 1.2 如果找到，并匹配不一致， 则信息流、资金流都标志为金额错误；并登记差错表；
	 * 1.3 如果找不到， 资金流标记为多账（长款），登记差错表，线下处理；
	 * 2. 信息流集合中剩余的记录（本周期内还未设置对账结果的信息流数据）， 信息流标志为存疑（如果以前就是存疑的，仍保持存疑）；
	 * 如果是非RBS的交易，登记差错表；
	 * <p>
	 * 例外情况：
	 * 每日跑Job，检查RBS的信息流对账结果，如果存疑超过一个月，则登记差错表；
	 */

	/**
	 * 对账核心方法
	 *
	 * @param bankList
	 *            对账文件解析出来的数据
	 * @param batch
	 *            对账批次记录
	 */
	public void check(List<ReconciliationEntityVo> bankList,LrAccountCheckBatch batch) {
		// 判断bankList是否为空
		if (bankList == null) {
			bankList = new ArrayList<ReconciliationEntityVo>();
		}
		// 查询平台bill_date,interfaceCode成功的交易
		List<ReconciliationEntityVo> platSucessDateList = this.reconciliationDataGetBiz.getSuccessPlatformDateByBillDate(batch.getBillDate(), infoBankChannel,true);

		// 查询平台bill_date,interfaceCode所有的交易
		List<ReconciliationEntityVo> platAllDateList = this.reconciliationDataGetBiz.getAllPlatformDateByBillDate(batch.getBillDate(), infoBankChannel);

		// 查询平台缓冲池中所有的数据
		List<LrAccountCheckMistakeScratchPool> platScreatchRecordList = this.lrAccountCheckMistakeScratchPoolService.list(null);

		// 差错list
		List<LrAccountCheckMistake> mistakeList = new ArrayList<LrAccountCheckMistake>();

		// 需要放入缓冲池中平台长款list
		List<LrAccountCheckMistakeScratchPool> insertScreatchRecordList = new ArrayList<LrAccountCheckMistakeScratchPool>();

		// 需要从缓冲池中移除的数据
		List<LrAccountCheckMistakeScratchPool> removeScreatchRecordList = new ArrayList<LrAccountCheckMistakeScratchPool>();

		//需要存入批次子表的数据
		Map<String,LrAccountCheckBatchSub> batchSubMap = this.getBatchSubMap(batch);

		log.info("  开始以平台的数据为准对账,平台长款记入缓冲池");
		baseOnPaltForm(platSucessDateList, bankList, mistakeList, insertScreatchRecordList, batch,batchSubMap);
		log.info("结束以平台的数据为准对账");

		log.info("  开始以银行通道的数据为准对账");
		baseOnBank(platAllDateList, bankList, platScreatchRecordList, mistakeList, batch, removeScreatchRecordList,batchSubMap);
		log.info(" 结束以银行通道的数据为准对账");

		// 保存数据
		lrAccountCheckTransactionService.saveDatasaveDate(batch, mistakeList, insertScreatchRecordList, removeScreatchRecordList,batchSubMap);

	}

	public BankChannelEnum getBankChannel() {
		return bankChannel;
	}

	protected abstract void setBankChannel(BankChannelEnum bankChannel);

	public BankChannelEnum getInfoBankChannel() {
		return infoBankChannel;
	}

	protected abstract void setInfoBankChannel(BankChannelEnum infoBankChannel);

	/**
	 * 以平台的数据为准对账
	 *
	 * @param platformDateList
	 *            平台dilldate的成功数据
	 * @param bankList
	 *            银行成功对账单数据
	 *
	 * @param misTakeList
	 *            差错list
	 * @param screatchRecordList
	 *            需要放入缓冲池中平台长款list
	 *
	 * @param batch
	 *            对账批次
	 */
	private void baseOnPaltForm(List<ReconciliationEntityVo> platformDateList, List<ReconciliationEntityVo> bankList, List<LrAccountCheckMistake> misTakeList, List<LrAccountCheckMistakeScratchPool> screatchRecordList, LrAccountCheckBatch batch,Map<String,LrAccountCheckBatchSub> batchSubMap) {
		BigDecimal platTradeAmount = BigDecimal.ZERO;// 平台交易总金额
		BigDecimal platFee = BigDecimal.ZERO;// 平台总手续费
		Integer tradeCount = 0;// 平台订单总数
		Integer mistakeCount = 0;

		for (ReconciliationEntityVo record : platformDateList) {
			Boolean flag = false;// 用于标记是否有匹配
			// 累计平台交易总金额和总手续费
			platTradeAmount = platTradeAmount.add(record.getAmount());
			platFee = platFee.add(record.getCostFee() == null ? BigDecimal.ZERO : record.getCostFee());
			tradeCount++;
			//设置batch子表统计值
			if(record.getTransType().equalsIgnoreCase(ReconConstants.TransTypeEnum.RECHARGE.getCode())){
				batchSubMap.get(ReconConstants.TransTypeEnum.RECHARGE.getCode()).setTradeCount(batchSubMap.get(ReconConstants.TransTypeEnum.RECHARGE.getCode()).getTradeCount().add(BigDecimal.ONE));
				batchSubMap.get(ReconConstants.TransTypeEnum.RECHARGE.getCode()).setTradeAmount(batchSubMap.get(ReconConstants.TransTypeEnum.RECHARGE.getCode()).getTradeAmount().add(record.getAmount()));
			}else if(record.getTransType().equalsIgnoreCase(ReconConstants.TransTypeEnum.PAYOUT.getCode())){
				batchSubMap.get(ReconConstants.TransTypeEnum.PAYOUT.getCode()).setTradeCount(batchSubMap.get(ReconConstants.TransTypeEnum.PAYOUT.getCode()).getTradeCount().add(BigDecimal.ONE));
				batchSubMap.get(ReconConstants.TransTypeEnum.PAYOUT.getCode()).setTradeAmount(batchSubMap.get(ReconConstants.TransTypeEnum.PAYOUT.getCode()).getTradeAmount().add(record.getAmount()));
			}else if(record.getTransType().equalsIgnoreCase(ReconConstants.TransTypeEnum.EXCHANGE.getCode())){
				batchSubMap.get(ReconConstants.TransTypeEnum.EXCHANGE.getCode()).setTradeCount(batchSubMap.get(ReconConstants.TransTypeEnum.EXCHANGE.getCode()).getTradeCount().add(BigDecimal.ONE));
				batchSubMap.get(ReconConstants.TransTypeEnum.EXCHANGE.getCode()).setTradeAmount(batchSubMap.get(ReconConstants.TransTypeEnum.EXCHANGE.getCode()).getTradeAmount().add(record.getAmount()));
			}
			for (ReconciliationEntityVo bankRecord : bankList) {
				// 如果银行账单中有匹配数据：进行金额，手续费校验
				if (record.getTransactionId().equalsIgnoreCase(bankRecord.getTransactionId())) {
					flag = true;// 标记已经找到匹配

					/** step1:匹配订单金额 **/
					// 平台金额多
					if (record.getAmount().compareTo(bankRecord.getAmount()) == 1) {
						// 金额不匹配，创建差错记录
						LrAccountCheckMistake misktake = createMisktake(null, record, bankRecord, MistakeTypeEnum.PLATFORM_OVER_CASH_MISMATCH, batch);
						misTakeList.add(misktake);
						mistakeCount++;
						//设置batch子表统计值
						this.calculateMistakeCound(record,batchSubMap);
						break;
					}
					// 平台金额少
					else if (record.getAmount().compareTo(bankRecord.getAmount()) == -1) {
						// 金额不匹配，创建差错记录
						LrAccountCheckMistake misktake = createMisktake(null, record, bankRecord, MistakeTypeEnum.PLATFORM_SHORT_CASH_MISMATCH, batch);
						misTakeList.add(misktake);
						mistakeCount++;
						//设置batch子表统计值
						this.calculateMistakeCound(record,batchSubMap);
						break;
					}

					/** step2:匹配订单手续费 **/
					if (record.getCostFee().compareTo(bankRecord.getCostFee()) != 0) {
						// 金额不匹配，创建差错记录
						LrAccountCheckMistake misktake = createMisktake(null, record, bankRecord, MistakeTypeEnum.FEE_MISMATCH, batch);
						misTakeList.add(misktake);
						mistakeCount++;
						//设置batch子表统计值
						this.calculateMistakeCound(record,batchSubMap);
						break;
					}

				}
			}
			// 没有找到匹配的记录，把这个订单记录到缓冲池中
			if (!flag) {
				LrAccountCheckMistakeScratchPool screatchRecord = getScratchRecord(record, batch);
				screatchRecordList.add(screatchRecord);
			}
		}

		// 统计数据保存
		batch.setTradeAmount(platTradeAmount);
		batch.setTradeCount(tradeCount);
		batch.setFee(platFee);
		batch.setMistakeCount(mistakeCount);
	}

	/**
	 * 以银行的数据为准对账
	 *
	 * @param bankList
	 *            银行对账单数据
	 *
	 * @param misTakeList
	 *            差错list
	 *
	 * @param platScreatchRecordList
	 *            平台缓冲池中的数据
	 *
	 * @param batch
	 *            对账批次
	 */
	private void baseOnBank(List<ReconciliationEntityVo> platAllDateList, List<ReconciliationEntityVo> bankList, List<LrAccountCheckMistakeScratchPool> platScreatchRecordList, List<LrAccountCheckMistake> misTakeList, LrAccountCheckBatch batch, List<LrAccountCheckMistakeScratchPool> removeScreatchRecordList,Map<String,LrAccountCheckBatchSub> batchSubMap) {
		BigDecimal platTradeAmount = BigDecimal.ZERO;// 平台交易总金额
		BigDecimal platFee = BigDecimal.ZERO;// 平台总手续费
		Integer tradeCount = 0;// 平台订单总数
		Integer mistakeCount = 0;
		// 拿银行数据去对账
		for (ReconciliationEntityVo bankRecord : bankList) {
			//设置batch子表bankTradeCount统计值
			if(bankRecord.getTransType().equalsIgnoreCase(ReconConstants.TransTypeEnum.RECHARGE.getCode())){
				batchSubMap.get(ReconConstants.TransTypeEnum.RECHARGE.getCode()).setBankTradeCount(batchSubMap.get(ReconConstants.TransTypeEnum.RECHARGE.getCode()).getBankTradeCount().add(BigDecimal.ONE));
				batchSubMap.get(ReconConstants.TransTypeEnum.RECHARGE.getCode()).setBankTradeAmount(batchSubMap.get(ReconConstants.TransTypeEnum.RECHARGE.getCode()).getBankTradeAmount().add(bankRecord.getAmount()));
			}else if(bankRecord.getTransType().equalsIgnoreCase(ReconConstants.TransTypeEnum.PAYOUT.getCode())){
				batchSubMap.get(ReconConstants.TransTypeEnum.PAYOUT.getCode()).setBankTradeCount(batchSubMap.get(ReconConstants.TransTypeEnum.PAYOUT.getCode()).getBankTradeCount().add(BigDecimal.ONE));
				batchSubMap.get(ReconConstants.TransTypeEnum.PAYOUT.getCode()).setBankTradeAmount(batchSubMap.get(ReconConstants.TransTypeEnum.PAYOUT.getCode()).getBankTradeAmount().add(bankRecord.getAmount()));
			}else if(bankRecord.getTransType().equalsIgnoreCase(ReconConstants.TransTypeEnum.EXCHANGE.getCode())){
				batchSubMap.get(ReconConstants.TransTypeEnum.EXCHANGE.getCode()).setBankTradeCount(batchSubMap.get(ReconConstants.TransTypeEnum.EXCHANGE.getCode()).getBankTradeCount().add(BigDecimal.ONE));
				batchSubMap.get(ReconConstants.TransTypeEnum.EXCHANGE.getCode()).setBankTradeAmount(batchSubMap.get(ReconConstants.TransTypeEnum.EXCHANGE.getCode()).getBankTradeAmount().add(bankRecord.getAmount()));
			}

			boolean flag = false;// 用于标记是否有匹配
			for (ReconciliationEntityVo record : platAllDateList) {
				/** step1 检查有匹配的数据 **/
				if (bankRecord.getTransactionId().equals(record.getTransactionId())) {
					flag = true;
					/** step2： 判断平台状态是否匹配 **/
					/** 注意：状态匹配不需要做金额和手续费验证，以平台数据为基准对账已经做了验证 **/
					// 不匹配记录差错。
					if (!TransStatusEnum.SUCCESS.name().equals(record.getTradeStatus())) {
						LrAccountCheckMistake misktake1 = createMisktake(null, record, bankRecord, MistakeTypeEnum.PLATFORM_SHORT_STATUS_MISMATCH, batch);
						misTakeList.add(misktake1);
						mistakeCount++;
						//设置batch子表统计值
						this.calculateMistakeCound(bankRecord,batchSubMap);
						// break;

						/** 订单状态不匹配验证完之后，在验证金额和手续费，差错处理必须先处理状态不符的情况 **/
						// 验证金额和手续费
						/** step1:匹配订单金额 **/
						// 平台金额多
						if (record.getAmount().compareTo(bankRecord.getAmount()) == 1) {
							// 金额不匹配，创建差错记录
							LrAccountCheckMistake misktake = createMisktake(null, record, bankRecord, MistakeTypeEnum.PLATFORM_OVER_CASH_MISMATCH, batch);
							misTakeList.add(misktake);
							mistakeCount++;
							//设置batch子表统计值
							this.calculateMistakeCound(bankRecord,batchSubMap);
							break;
						}
						// 平台金额少
						else if (record.getAmount().compareTo(bankRecord.getAmount()) == -1) {
							// 金额不匹配，创建差错记录
							LrAccountCheckMistake misktake = createMisktake(null, record, bankRecord, MistakeTypeEnum.PLATFORM_SHORT_CASH_MISMATCH, batch);
							misTakeList.add(misktake);
							mistakeCount++;
							//设置batch子表统计值
							this.calculateMistakeCound(bankRecord,batchSubMap);
							break;
						}

						/** step2:匹配订单手续费 **/
						if (record.getCostFee().compareTo(bankRecord.getCostFee()) != 0) {
							// 金额不匹配，创建差错记录
							LrAccountCheckMistake misktake = createMisktake(null, record, bankRecord, MistakeTypeEnum.FEE_MISMATCH, batch);
							misTakeList.add(misktake);
							mistakeCount++;
							//设置batch子表统计值
							this.calculateMistakeCound(bankRecord,batchSubMap);
							break;
						}

					}
				}
			}

			/** step3： 如果没有匹配的数据，去缓冲池中查找对账，如果没有记录差错 **/
			if (!flag) {
				// 去缓冲池中查找对账(前提是缓冲池里面有数据)
				if (platScreatchRecordList != null)
					for (LrAccountCheckMistakeScratchPool scratchRecord : platScreatchRecordList) {

						// 找到匹配的
						if (scratchRecord.getTrxNo().equals(bankRecord.getTransactionId())) {
							// 累计平台交易总金额和总手续费
							platTradeAmount = platTradeAmount.add(scratchRecord.getOrderAmount());
							platFee = platFee.add(scratchRecord.getPlatCost() == null ? BigDecimal.ZERO : scratchRecord.getPlatCost());
							tradeCount++;
							//设置batch子表统计值
							if(bankRecord.getTransType().equalsIgnoreCase(ReconConstants.TransTypeEnum.RECHARGE.getCode())){
								batchSubMap.get(ReconConstants.TransTypeEnum.RECHARGE.getCode()).setTradeCount(batchSubMap.get(ReconConstants.TransTypeEnum.RECHARGE.getCode()).getTradeCount().add(BigDecimal.ONE));
								batchSubMap.get(ReconConstants.TransTypeEnum.RECHARGE.getCode()).setTradeAmount(batchSubMap.get(ReconConstants.TransTypeEnum.RECHARGE.getCode()).getTradeAmount().add(scratchRecord.getOrderAmount()));
							}else if(bankRecord.getTransType().equalsIgnoreCase(ReconConstants.TransTypeEnum.PAYOUT.getCode())){
								batchSubMap.get(ReconConstants.TransTypeEnum.PAYOUT.getCode()).setTradeCount(batchSubMap.get(ReconConstants.TransTypeEnum.PAYOUT.getCode()).getTradeCount().add(BigDecimal.ONE));
								batchSubMap.get(ReconConstants.TransTypeEnum.PAYOUT.getCode()).setTradeAmount(batchSubMap.get(ReconConstants.TransTypeEnum.PAYOUT.getCode()).getTradeAmount().add(scratchRecord.getOrderAmount()));
							}else if(bankRecord.getTransType().equalsIgnoreCase(ReconConstants.TransTypeEnum.EXCHANGE.getCode())){
								batchSubMap.get(ReconConstants.TransTypeEnum.EXCHANGE.getCode()).setTradeCount(batchSubMap.get(ReconConstants.TransTypeEnum.EXCHANGE.getCode()).getTradeCount().add(BigDecimal.ONE));
								batchSubMap.get(ReconConstants.TransTypeEnum.EXCHANGE.getCode()).setTradeAmount(batchSubMap.get(ReconConstants.TransTypeEnum.EXCHANGE.getCode()).getTradeAmount().add(scratchRecord.getOrderAmount()));
							}

							flag = true;

							// 验证金额和手续费
							/** step1:匹配订单金额 **/
							// 平台金额多
							if (scratchRecord.getOrderAmount().compareTo(bankRecord.getAmount()) == 1) {
								// 金额不匹配，创建差错记录
								LrAccountCheckMistake misktake = createMisktake(scratchRecord, null, bankRecord, MistakeTypeEnum.PLATFORM_OVER_CASH_MISMATCH, batch);
								misTakeList.add(misktake);
								mistakeCount++;
								//设置batch子表统计值
								this.calculateMistakeCound(bankRecord,batchSubMap);
								break;
							}
							// 平台金额少
							else if (scratchRecord.getOrderAmount().compareTo(bankRecord.getAmount()) == -1) {
								// 金额不匹配，创建差错记录
								LrAccountCheckMistake misktake = createMisktake(scratchRecord, null, bankRecord, MistakeTypeEnum.PLATFORM_SHORT_CASH_MISMATCH, batch);
								misTakeList.add(misktake);
								mistakeCount++;
								//设置batch子表统计值
								this.calculateMistakeCound(bankRecord,batchSubMap);
								break;
							}

							/** step2:匹配订单手续费 **/
							if (scratchRecord.getPlatCost().compareTo(bankRecord.getCostFee()) != 0) {
								// 金额不匹配，创建差错记录
								LrAccountCheckMistake misktake = createMisktake(scratchRecord, null, bankRecord, MistakeTypeEnum.FEE_MISMATCH, batch);
								misTakeList.add(misktake);
								mistakeCount++;
								//设置batch子表统计值
								this.calculateMistakeCound(bankRecord,batchSubMap);
								break;
							}

							/** step3:把缓存池中匹配的记录删除掉 **/
							removeScreatchRecordList.add(scratchRecord);
						}
					}
			}

			// 缓冲池中还是没有这条记录,直接记录差错，差错类型为 PLATFORM_MISS("平台漏单")
			if (!flag) {
				LrAccountCheckMistake misktake = createMisktake(null, null, bankRecord, MistakeTypeEnum.PLATFORM_MISS, batch);
				misTakeList.add(misktake);
				mistakeCount++;
				//设置batch子表统计值
				this.calculateMistakeCound(bankRecord,batchSubMap);
			}
		}

		// 统计数据保存
		batch.setTradeAmount(batch.getTradeAmount().add(platTradeAmount));
		batch.setTradeCount(batch.getTradeCount() + tradeCount);
		batch.setFee(batch.getFee().add(platFee));
		batch.setMistakeCount(batch.getMistakeCount() + mistakeCount);
	}

	private void calculateMistakeCound(ReconciliationEntityVo record,Map<String,LrAccountCheckBatchSub> batchSubMap){
		if(record.getTransType().equalsIgnoreCase(ReconConstants.TransTypeEnum.RECHARGE.getCode())){
			batchSubMap.get(ReconConstants.TransTypeEnum.RECHARGE.getCode()).setMistakeCount(batchSubMap.get(ReconConstants.TransTypeEnum.RECHARGE.getCode()).getMistakeCount().add(BigDecimal.ONE));
			batchSubMap.get(ReconConstants.TransTypeEnum.RECHARGE.getCode()).setUnhandleMistakeCount(batchSubMap.get(ReconConstants.TransTypeEnum.RECHARGE.getCode()).getUnhandleMistakeCount().add(BigDecimal.ONE));
		}else if(record.getTransType().equalsIgnoreCase(ReconConstants.TransTypeEnum.PAYOUT.getCode())){
			batchSubMap.get(ReconConstants.TransTypeEnum.PAYOUT.getCode()).setMistakeCount(batchSubMap.get(ReconConstants.TransTypeEnum.PAYOUT.getCode()).getMistakeCount().add(BigDecimal.ONE));
			batchSubMap.get(ReconConstants.TransTypeEnum.PAYOUT.getCode()).setUnhandleMistakeCount(batchSubMap.get(ReconConstants.TransTypeEnum.PAYOUT.getCode()).getUnhandleMistakeCount().add(BigDecimal.ONE));
		}else if(record.getTransType().equalsIgnoreCase(ReconConstants.TransTypeEnum.EXCHANGE.getCode())){
			batchSubMap.get(ReconConstants.TransTypeEnum.EXCHANGE.getCode()).setMistakeCount(batchSubMap.get(ReconConstants.TransTypeEnum.EXCHANGE.getCode()).getMistakeCount().add(BigDecimal.ONE));
			batchSubMap.get(ReconConstants.TransTypeEnum.EXCHANGE.getCode()).setUnhandleMistakeCount(batchSubMap.get(ReconConstants.TransTypeEnum.EXCHANGE.getCode()).getUnhandleMistakeCount().add(BigDecimal.ONE));
		}
	}

	/**
	 * 创建差错记录
	 *
	 * @param scratchRecord
	 *            平台缓冲池中的订单记录
	 * @param record
	 *            平台订单记录
	 * @param bankRecord
	 *            银行账单记录
	 * @param mistakeType
	 *            差错类型
	 * @return 注意：scratchRecord和record 至少有一个为空
	 */
	private LrAccountCheckMistake createMisktake(LrAccountCheckMistakeScratchPool scratchRecord, ReconciliationEntityVo record, ReconciliationEntityVo bankRecord, MistakeTypeEnum mistakeType, LrAccountCheckBatch batch) {

		LrAccountCheckMistake mistake = new LrAccountCheckMistake();
		mistake.setBatchNo(batch.getBatchNo());
		mistake.setBillDate(batch.getBillDate());
		mistake.setErrType(mistakeType.name());
		mistake.setHandleStatus(MistakeHandleStatusEnum.NOHANDLE.name());
		mistake.setBankType(batch.getBankType());
		if (record != null) {
			mistake.setMerchantName("");
			mistake.setMerchantNo("");
			mistake.setOrderNo(record.getOrderNo());
			mistake.setTradeTime(record.getTransTime());
			mistake.setTrxNo(record.getTransactionId());
			mistake.setOrderAmount(record.getAmount());
			mistake.setRefundAmount(record.getRefundAmount());
			mistake.setTradeStatus(record.getTradeStatus());
			mistake.setFee(record.getCostFee());
		}

		if (scratchRecord != null) {
			mistake.setOrderNo(scratchRecord.getMerchantOrderNo());
			mistake.setTradeTime(scratchRecord.getPaySuccessTime());
			mistake.setTrxNo(scratchRecord.getTrxNo());
			mistake.setOrderAmount(scratchRecord.getOrderAmount());
			mistake.setRefundAmount(scratchRecord.getSuccessRefundAmount());
			mistake.setTradeStatus(scratchRecord.getStatus());
			mistake.setFee(scratchRecord.getPlatCost());
		}

		if (bankRecord != null) {
			mistake.setBankAmount(bankRecord.getAmount());
			mistake.setBankFee(bankRecord.getCostFee());
			mistake.setBankOrderNo(bankRecord.getOrderNo());
			mistake.setBankRefundAmount(bankRecord.getRefundAmount());
			mistake.setBankTradeStatus(bankRecord.getTradeStatus());
			mistake.setBankTradeTime(bankRecord.getTransTime());
			mistake.setBankTrxNo(bankRecord.getTransactionId());
		}
		return mistake;

	}

	/**
	 * 得到缓存记录：用于放入缓冲池
	 *
	 * @param record
	 *            支付记录
	 * @param batch
	 *            对账批次记录
	 * @return
	 */
	private LrAccountCheckMistakeScratchPool getScratchRecord(ReconciliationEntityVo record, LrAccountCheckBatch batch) {

		LrAccountCheckMistakeScratchPool scratchRecord = new LrAccountCheckMistakeScratchPool();
		scratchRecord.setBankOrderNo(record.getTransactionId());
		scratchRecord.setBankTrxNo(record.getBankTrxNo());
		scratchRecord.setCompleteTime(record.getTransTime());
		scratchRecord.setPaySuccessTime(record.getTransTime());
		scratchRecord.setMerchantOrderNo(record.getOrderNo());
		scratchRecord.setOrderAmount(record.getAmount());
		scratchRecord.setPlatCost(record.getCostFee());
		scratchRecord.setPayWayCode(record.getBankChannel());
		scratchRecord.setTrxNo(record.getTransactionId());
		scratchRecord.setStatus(TransStatusEnum.SUCCESS.name());
		scratchRecord.setBatchNo(batch.getBatchNo());
		scratchRecord.setBillDate(batch.getBillDate());
		return scratchRecord;
	}

	/**
	 * 获取transType对应的batch初始信息
	 * @return
	 */
	protected abstract Map<String,LrAccountCheckBatchSub> getBatchSubMap(LrAccountCheckBatch batch);
}