refactor: 重构了对账任务
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/agent/CheckFileProvider.java b/payapi/src/main/java/com/supwisdom/dlpay/agent/CheckFileProvider.java
index b8b3fa5..008dcd7 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/agent/CheckFileProvider.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/agent/CheckFileProvider.java
@@ -3,9 +3,25 @@
 import com.supwisdom.dlpay.api.domain.TSourceTypeCheckStatus;
 
 public interface CheckFileProvider {
+  /**
+   * 请求对账文件接口,由对账任务请求第三查询 checkStatus.checkFileDate 的对账文件
+   *
+   * @param checkStatus
+   * @return AgentResponse.code == SUCCESS 时 ,有两种情况: 当 checkStatus.checkFileOk == true 表示对账文件明细已经
+   * 写入数据库中,不需要 downloadCheckFile ,  当为 false 时才需要
+   * AgentResponse.code == REQUIRE_QUERY 时, 对账任务会调用 queryCheckFile 方法判断是否可以下载对账文件了
+   * 其它值表示失败
+   */
   AgentResponse<TSourceTypeCheckStatus> acquireCheckFile(TSourceTypeCheckStatus checkStatus);
 
   AgentResponse<TSourceTypeCheckStatus> queryCheckFile(TSourceTypeCheckStatus checkStatus);
 
+  /**
+   * 下载checkStatus.checkDate 指定的日期的对账文件,由对账任务调用,
+   * 如果 checkStatus.chkFileUrl == 'none' 表示当日没有对账文件,
+   *
+   * @param checkStatus
+   * @return  AgentResponse.code 返回值只有 SUCCESS 和 FAIL
+   */
   AgentResponse<TSourceTypeCheckStatus> downloadCheckFile(TSourceTypeCheckStatus checkStatus);
 }
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/dao/TransactionChkdtlDao.java b/payapi/src/main/java/com/supwisdom/dlpay/api/dao/TransactionChkdtlDao.java
index 122ce76..51301ce 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/api/dao/TransactionChkdtlDao.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/dao/TransactionChkdtlDao.java
@@ -23,9 +23,19 @@
   CountAmountBean getTransactionSumInfo(String accdate, String sourcetype);
 
   @Query("select a from TTransactionChkdtl a,TPersondtl b where a.localRefno=b.refno and a.sourcetype=b.sourceType and a.accdate=b.accdate and a.amount=b.amount " +
-      " and a.otherStatus='success' and b.status='success' order by a.recordno ")
+      " and a.otherStatus='success' and b.status='success' and a.chkresult <> 'equal' and a.chkfileId=?1 order by a.recordno ")
   List<TTransactionChkdtl> getCheckEqualDetails(String chkfileId);
 
+  @Query("select a from TTransactionChkdtl a,TPersondtl b where a.localRefno=b.refno and a.sourcetype=b.sourceType and a.accdate=b.accdate and a.amount=b.amount " +
+      " and a.otherStatus='success' and b.status='success' and a.chkresult <> 'equal' and a.chkfileId=?1 and a.recordno between ?2 and ?3 order by a.recordno ")
+  List<TTransactionChkdtl> findEqualDtlWithLimit(String chkfileId, Integer start, Integer end);
+
+  @Query("select min(a.recordno) from TTransactionChkdtl a where a.chkfileId=?1 and a.chkresult=?2")
+  Integer getMinRecordNoByStatus(String chkFileId, String status);
+
+  @Query("select min(a.recordno) from TTransactionChkdtl a where a.chkfileId=?1 ")
+  Integer getMinRecordNo(String chkFileId);
+
   @Query("from TTransactionChkdtl t where t.chkfileId=?1 and t.chkresult='uncheck' order by t.recordno ")
   List<TTransactionChkdtl> getUncheckTransactionChkdtls(String chkfileId);
 
@@ -34,6 +44,6 @@
 
   @Query(value = "select t.refno from TB_PERSONDTL t left join TB_TRANSACTION_CHKDTL a on t.REFNO=a.OTHER_REFNO and a.CHKFILE_ID=:chkfileId " +
       " where t.ACCDATE=:accdate and t.SOURCETYPE=:sourcetype and t.status='success' and a.id is null order by t.refno ", nativeQuery = true)
-  List<String> findLocalMoreDtls(@Param("accdate")String accdate, @Param("sourcetype")String sourcetype, @Param("chkfileId") String chkfileId);
+  List<String> findLocalMoreDtls(@Param("accdate") String accdate, @Param("sourcetype") String sourcetype, @Param("chkfileId") String chkfileId);
 
 }
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TSourceTypeCheckStatus.java b/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TSourceTypeCheckStatus.java
index bad55b9..6a13467 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TSourceTypeCheckStatus.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TSourceTypeCheckStatus.java
@@ -1,5 +1,7 @@
 package com.supwisdom.dlpay.api.domain;
 
+import com.sun.org.apache.xpath.internal.operations.Bool;
+
 import javax.persistence.*;
 import javax.validation.constraints.NotNull;
 import java.sql.Timestamp;
@@ -35,6 +37,13 @@
   @NotNull
   private Boolean checkStatus; // 当前对账完成状态
 
+  @Column(name = "repair_status")
+  private Boolean repairStatus; // 补帐完成状态
+
+  @Column(name = "settle_status")
+  @NotNull
+  private Boolean settleStatus; // 是否可以结算
+
   @Column(name = "force_recheck")
   @NotNull
   private Boolean forceRecheck; // 是否对当前日期强制对账
@@ -149,4 +158,20 @@
   public void setRemark(String remark) {
     this.remark = remark;
   }
+
+  public Boolean getSettleStatus() {
+    return settleStatus;
+  }
+
+  public void setSettleStatus(Boolean settleStatus) {
+    this.settleStatus = settleStatus;
+  }
+
+  public Boolean getRepairStatus() {
+    return repairStatus;
+  }
+
+  public void setRepairStatus(Boolean repairStatus) {
+    this.repairStatus = repairStatus;
+  }
 }
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TTransactionChkdtl.java b/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TTransactionChkdtl.java
index f5dc80e..ef5422a 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TTransactionChkdtl.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TTransactionChkdtl.java
@@ -3,11 +3,14 @@
 import org.hibernate.annotations.GenericGenerator;
 
 import javax.persistence.*;
+import javax.validation.constraints.NotNull;
 import java.sql.Timestamp;
 
 @Entity
 @Table(name = "TB_TRANSACTION_CHKDTL",
-    indexes = {@Index(name = "UK_TRANSACTION_CHKDTL", unique = true, columnList = "ACCDATE,SOURCETYPE,OTHER_REFNO,LOCAL_REFNO")})
+    indexes = {@Index(name = "UK_TRANSACTION_CHKDTL", unique = true, columnList = "ACCDATE,SOURCETYPE,OTHER_REFNO,LOCAL_REFNO"),
+        @Index(name = "TRANSACTION_CHKDTL_FILEID", columnList = "CHKFILE_ID"),
+        @Index(name = "TRANSACTION_CHKDTL_RECORDNO", columnList = "CHKFILE_ID, RECORDNO", unique = true)})
 public class TTransactionChkdtl {
   @Id
   @GenericGenerator(name = "idGenerator", strategy = "uuid")
@@ -16,6 +19,7 @@
   private String id;
 
   @Column(name = "CHKFILE_ID", nullable = false, length = 32)
+  @NotNull
   private String chkfileId;
 
   @Column(name = "ACCDATE", nullable = false, length = 8)
@@ -25,6 +29,7 @@
   private String sourcetype;
 
   @Column(name = "RECORDNO", precision = 9)
+  @NotNull
   private Integer recordno;
 
   @Column(name = "AMOUNT", nullable = false, precision = 9, scale = 2)
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/citizencard_checkfile_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/citizencard_checkfile_service.kt
index 615db40..46ae340 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/citizencard_checkfile_service.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/citizencard_checkfile_service.kt
@@ -43,21 +43,32 @@
             val billDate = checkStatus.checkFileDate
             logger.info("大理农商行对账单下载:download checkdate=【$billDate】")
 
-            val chkfile: TTransactionChkfile = transactionReconciliationService.getTransactionChkfile(billDate, checkStatus.sourceType)?.let { file ->
-                when (file.status) {
-                    ConstantUtil.CHKFILE_STATUS_INIT -> file //初始化直接返回
-                    ConstantUtil.CHKFILE_STATUS_ERROR, ConstantUtil.CHKFILE_STATUS_UNCHECK -> reinitCheckFile(file) //错误或未对账的直接重新拉取
-                    else -> {
-                        when (checkStatus.forceRecheck) {
-                            true -> reinitCheckFile(file) //对账已完成但强制重新对账
-                            false -> throw TransactionException(-1, "大理农商行该天[$billDate]已对账完成") //对账已完成报错
+            val chkfile: TTransactionChkfile = transactionReconciliationService
+                    .getTransactionChkfile(billDate, checkStatus.sourceType)?.let { file ->
+                        when (file.status) {
+                            ConstantUtil.CHKFILE_STATUS_INIT -> file //初始化直接返回
+                            ConstantUtil.CHKFILE_STATUS_ERROR -> reinitCheckFile(file) //错误或未对账的直接重新拉取
+                            else -> {
+                                when (checkStatus.forceRecheck) {
+                                    true -> reinitCheckFile(file)  //对账已完成但强制重新对账
+                                    false -> {
+                                        if (file.status == ConstantUtil.CHKFILE_STATUS_UNCHECK) {
+                                            file
+                                        } else {
+                                            throw TransactionException(-1, "大理农商行该天[$billDate]已对账完成")
+                                        }
+                                    } //对账已完成报错
+                                }
+                            }
                         }
                     }
-                }
-            } ?: transactionReconciliationService.saveInitTransactionChkfile(billDate, checkStatus.sourceType)
+                    ?: transactionReconciliationService.saveInitTransactionChkfile(billDate, checkStatus.sourceType)
 
+            if (chkfile.status == ConstantUtil.CHKFILE_STATUS_UNCHECK) {
+                return successDownload("[$billDate]日对账数据入库成功", checkStatus)
+            }
             val resp = citizencardPayService.getChkfilename(billDate, null)
-            when(resp.code) {
+            when (resp.code) {
                 YnrccUtil.CODE_SUCCESS -> {
                     val agentConfig = sourceTypeService.getChargePaytypeConfig(TradeDict.PAYTYPE_CITIZEN_CARD, true)
                     val agentUrl = agentConfig[YnrccUtil.YNRCC_ANGENT_URL] + "/download?filename=" + resp.filename
@@ -75,16 +86,10 @@
                 YnrccUtil.NO_RECORDS_TODAY -> {
                     //当日无交易明细,也创建空记录
                     transactionReconciliationService.doSuccessTransactionChkfile(chkfile, "请求银行返回:当日无交易明细")
-
                     //成功
-                    return AgentResponse<TSourceTypeCheckStatus>().also {
-                        it.code = AgentCode.SUCCESS
-                        it.agentMsg = "当日无交易明细"
-                        it.payload = sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus.apply {
-                            this.chkfileUrl = "none"
-                        })
-                    }
-
+                    return successDownload("当日无交易明细", checkStatus.apply {
+                        this.chkfileUrl = "none"
+                    })
                 }
                 else -> {
                     //报错,退出对账单拉取
@@ -124,18 +129,11 @@
     }
 
     override fun downloadCheckFile(checkStatus: TSourceTypeCheckStatus): AgentResponse<TSourceTypeCheckStatus> {
-        try{
+        try {
             val billDate = checkStatus.checkFileDate
             val agentUrl = checkStatus.chkfileUrl
             if ("none" == agentUrl) {
-                return AgentResponse<TSourceTypeCheckStatus>().also {
-                    it.code = AgentCode.SUCCESS
-                    it.agentMsg = "当日无交易明细"
-                    it.payload = sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus.apply {
-                        this.checkFileOk = true
-                        this.remark = "当日无交易明细"
-                    })
-                }
+                return successDownload("当日无交易明细", checkStatus)
             }
 
             val chkfile = transactionReconciliationService.doInitTransactionChkfile(billDate, checkStatus.sourceType)
@@ -189,14 +187,7 @@
 
                     //成功
                     transactionReconciliationService.doSuccessTransactionChkfile(chkfile, "对账单数据下载成功")
-                    return AgentResponse<TSourceTypeCheckStatus>().also {
-                        it.code = AgentCode.SUCCESS
-                        it.agentMsg = "[$billDate]日对账数据入库成功"
-                        it.payload = sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus.apply {
-                            this.checkFileOk = true
-                            this.remark = "[$billDate]日对账数据入库成功"
-                        })
-                    }
+                    return successDownload("[$billDate]日对账数据入库成功", checkStatus)
                 }
             } else {
                 logger.error("请求前置download[${chkfile.accdate}]对账单返回失败:httpStatus=[${respClient.status}]。获取对账文件数据失败")
@@ -213,15 +204,26 @@
                 }
             }
 
-        }catch (ex:Exception){
+        } catch (ex: Exception) {
             return AgentResponse<TSourceTypeCheckStatus>().also {
                 it.code = AgentCode.FAIL
-                it.agentMsg = ex.message?:"按对账单路径获取对账数据入库异常"
+                it.agentMsg = ex.message ?: "按对账单路径获取对账数据入库异常"
                 it.payload = checkStatus
             }
         }
     }
 
+    private fun successDownload(message: String, checkStatus: TSourceTypeCheckStatus): AgentResponse<TSourceTypeCheckStatus> {
+        return AgentResponse<TSourceTypeCheckStatus>().also {
+            it.code = AgentCode.SUCCESS
+            it.agentMsg = message
+            it.payload = sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus.apply {
+                this.checkFileOk = true
+                this.remark = message
+            })
+        }
+    }
+
     private fun reinitCheckFile(chkfile: TTransactionChkfile): TTransactionChkfile {
         transactionReconciliationService.deleteTransactionChkdtls(chkfile.id) //删除明细
         return transactionReconciliationService.saveOrUpdateTransactionChkfile(chkfile.apply {
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_sourcetype_chk.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_sourcetype_chk.kt
index 4a552f8..2806109 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_sourcetype_chk.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_sourcetype_chk.kt
@@ -5,6 +5,7 @@
 import com.supwisdom.dlpay.agent.CheckFileProvider
 import com.supwisdom.dlpay.api.domain.TSourceType
 import com.supwisdom.dlpay.api.domain.TSourceTypeCheckStatus
+import com.supwisdom.dlpay.api.domain.TTransactionChkdtl
 import com.supwisdom.dlpay.api.domain.TTransactionChkfile
 import com.supwisdom.dlpay.api.service.SourceTypeService
 import com.supwisdom.dlpay.api.service.TransactionReconciliationService
@@ -63,7 +64,7 @@
                 ?: newSourceTypeStatus(sourcetype, accdate)
 
         val interval = DateUtil.getIntervalDay(status.checkAccdate, accdate).toInt()
-        return if (status.checkStatus && interval == sourcetype.tplusN) {
+        return if (status.checkStatus && status.repairStatus && interval == sourcetype.tplusN) {
             // 当前日期对账已完成,并且已经对账到最后一天
             null
         } else if (status.checkStatus && interval > sourcetype.tplusN) {
@@ -72,8 +73,11 @@
             status.checkStatus = false
             status.checkFileDate = DateUtil.getNewDay(status.checkFileDate, 1)
             status.checkFileOk = false
+            status.repairStatus = false
+            status.settleStatus = false
+            status.forceRecheck = false
             sourceTypeService.saveOrUpdateSourceTypeCheckStatus(status)
-        } else if (!status.checkStatus && interval >= sourcetype.tplusN) {
+        } else if ((!status.checkStatus || !status.repairStatus) && interval >= sourcetype.tplusN) {
             // 当前对账并未完成 且满足对账条件
             status
         } else {
@@ -150,6 +154,44 @@
 }
 
 @Component
+/**
+ * 对账业务主要由三张表组成 TSourceTypeCheckStatus(对账状态表), TTransactionChkfile(对账文件表), TTransactionChkDtl(对账明细表)
+ * 对账状态表有五个状态:
+ *  1. checkFileOk: 表示对账文件是否成功导入对账文件表和对账明细表中
+ *  2. checkStatus: 表示核对流水任务是否完成,当核对任务完成后,同时根据对"对账文件表"的对账结果(result)来判断是否完成核对,只有当 result
+ *     是 "一致" 或 "不平" 时 checkStatus 才为 true
+ *  3. repairStatus: 表示核对完成,并且可以进行补帐交易。对账任务判断 repairStatus == true 之后将会对所有不一致的交易进行补帐。
+ *  4. settleStatus: 表示本次对账完成(核对与补帐都完成),对账任务会在补帐完成后将 settleStatus 置为 true。
+ *      同时 TTransactionChkDtl 的 resovled 状态全部为 'equal'。系统结算任务只有当 settleStatus == true 的时候才会进行
+ *  5. forceRecheck : 表示对当前的 checkDate 的对账文件重新下载并对账
+ *
+ *
+ * 对账文件表的 status 表示本次核对的结果。对账文件表 status 有几种情况:
+ *  1. 初始状态(init),表示对账任务已经触发;
+ *  2. 待核对状态(unchecked), 表示对账文件已存入数据库中;
+ *  3. 错误状态(error), 保存数据有错误
+ *  4. 完成状态(finish) , 表示核对完成状态;
+ *
+ * 对账文件表的对账结果字段(result) 表示本次对账处理结果:
+ *  1. 未核对(none), 表示还未与流水进行比对
+ *  2. 一致( equal), 表示对账完全一致
+ *  3. 不平( notequal), 表示对账不一致,但可以通过补帐交易完成结算
+ *  4. 错误( error ) , 表示核对时数据校验不正确,系统不能通过补帐交易完成结算。需人工审核对账文件后,重新下载对账单并核对。
+ *
+ *
+ * 对账明细表(TTransactionChkDtl) 的 chkstatus 表示核对结果:
+ *   1. 未检查(uncheck), 表示记录还未核对
+ *   2. 一直( equal) ,表示记录核对与流水一致
+ *   3. 交易流水不存在(notexist), 表示本地交易流水不存在,影响对账文件表的 result 状态,为error。
+ *   4. 支付未记账 ( nocharge) ,表示本地流水未成功,需要补帐
+ *   5. 金额不相等(diff), 表示对账明细金额与本地流水不一致, 影响对账文件表的 result 状态,为error。
+ *   6. 记账日期或支付方式错误 (error) , 其它类型错误,影响对账文件表的 result 状态,为error。
+ *
+ * 对账明细表(TTransactionChkDtl) 的 result 表示处理结果
+ *   1. 需要补帐("add")
+ *   2. 一致("equal") , 表示一致
+ *   3. 挂起("hangup"), 表示补帐出现异常
+ */
 class SourceTypeCheckExecutor {
     companion object {
         const val SUCCESS = "success"
@@ -213,7 +255,11 @@
                         ExecutorResult(checkStatus, FAIL, "下载失败,未能查到对账文件")
                     }
                 } else if (acResp.code == AgentCode.SUCCESS) {
-                    downloadFile(provider, acResp.payload)
+                    if (!acResp.payload.checkFileOk) {
+                        downloadFile(provider, acResp.payload)
+                    } else {
+                        ExecutorResult(checkStatus, SUCCESS, acResp.agentMsg)
+                    }
                 } else {
                     ExecutorResult(checkStatus, FAIL, acResp.agentMsg)
                 }
@@ -228,51 +274,89 @@
     @Async("sourcetypeCheckTaskExecutor")
     fun reconciliation(checkStatus: TSourceTypeCheckStatus): Future<ExecutorResult> {
         // 3. 完成对账
-        val result = transactionReconciliationService.getTransactionChkfile(checkStatus.checkAccdate, checkStatus.sourceType)?.let { chkfile ->
-            if (ConstantUtil.CHKFILE_STATUS_UNCHECK == chkfile.status) {
-                //TODO:"实现对账逻辑"
-                try {
-                    when (doCheckProcess(chkfile).result) {
-                        ConstantUtil.CHKFILE_RESULT_EQUAL -> {
-                            checkStatus.checkStatus = true //对账成功
-                            sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus)
-                            ExecutorResult(checkStatus, SUCCESS, "对账成功,双方一致")
+        val result = if (!checkStatus.checkStatus) {
+            transactionReconciliationService
+                    .getTransactionChkfile(checkStatus.checkAccdate, checkStatus.sourceType)?.let { chkfile ->
+                        try {
+                            when (doCheckProcess(chkfile, checkStatus).result) {
+                                ConstantUtil.CHKFILE_RESULT_EQUAL -> {
+                                    ExecutorResult(checkStatus, SUCCESS, "对账成功,双方一致")
+                                }
+                                else -> ExecutorResult(checkStatus, FAIL, "对账不平")
+                            }
+                        } catch (ex: Exception) {
+                            ex.printStackTrace()
+                            ExecutorResult(checkStatus, FAIL, "对账异常")
                         }
-                        else -> ExecutorResult(checkStatus, FAIL, "对账不平")
-                    }
-                } catch (ex: Exception) {
-                    ex.printStackTrace()
-                    ExecutorResult(checkStatus, FAIL, "对账异常")
-                }
-            } else {
-                ExecutorResult(checkStatus, FAIL, "[${checkStatus.checkAccdate}]日入库的checkfile状态异常[${chkfile.status}]")
-            }
-        } ?: ExecutorResult(checkStatus, FAIL, "未找到[${checkStatus.checkAccdate}]日入库的对账数据")
+                    } ?: ExecutorResult(checkStatus, FAIL, "未找到[${checkStatus.checkAccdate}]日入库的对账数据")
+        } else if (checkStatus.repairStatus) {
+            // 补帐逻辑
+            ExecutorResult(checkStatus, SUCCESS, "对账成功,双方一致")
+        } else if (checkStatus.settleStatus) {
+            ExecutorResult(checkStatus, SUCCESS, "对账完成,可以结算")
+        } else {
+            ExecutorResult(checkStatus, FAIL, "未找到[${checkStatus.checkAccdate}]日入库的对账数据")
+        }
         return AsyncResult(result)
     }
 
-    fun doCheckProcess(chkfile: TTransactionChkfile): TTransactionChkfile {
-        try {
-            //统一处理交易一致的记录
-            transactionReconciliationService.doCheckEqualTransdtls(chkfile)
-        } catch (e1: Exception) {
+    fun doCheckProcess(chkfile: TTransactionChkfile, checkStatus: TSourceTypeCheckStatus): TTransactionChkfile {
+        // 假设绝大多数记录都是一致的情况下,下面的逻辑采用每1000条批量处理提高性能
+        //统一处理交易一致的记录, 对账 1
+        (0..chkfile.othercnt step 1000).forEach {
+            try {
+                transactionReconciliationService.doCheckEqualTransdtls(chkfile, it)
+            } catch (e1: Exception) {
+            }
         }
-
-        //剩余的不平记录逐一对比
+        //剩余的未对账记录逐一对比, 对账 2
+        // 这个规则业务可以处理 "对账1" 中可能漏掉的记录
+        var errorDtl = false
         transactionReconciliationService.getUncheckTransactionChkdtls(chkfile.id).forEach {
-            transactionReconciliationService.doCheckTransactionChkdtl(it)
+            transactionReconciliationService.doCheckTransactionChkdtl(it).also { chkdtl ->
+                if (chkdtl.chkresult != ConstantUtil.CHKDTL_CHKRESULT_NOCHARGE
+                        && chkdtl.chkresult != ConstantUtil.CHKDTL_CHKRESULT_EQUAL) {
+                    errorDtl = true
+                }
+            }
         }
 
-        //判断chkdtl是否全部对账完成
+        //判断chkdtl是否全部对账完成 , 对账 3
         if (transactionReconciliationService.checkUncheckExists(chkfile.id)) {
             throw TransactionException(-2, "存在未对账的明细记录")
         }
+        //判断本地是否有多余成功流水,并保存 , 对账 4
+        transactionReconciliationService.doCheckLocalMoreDtls(chkfile, checkStatus).also {
+            if (it.isNotEmpty()) {
+                errorDtl = true
+            }
+        }
 
-        //判断本地是否有多余成功流水,并保存
-        transactionReconciliationService.doCheckLocalMoreDtls(chkfile)
+        /**
+         * 当我们将每条明细进行核对后,需要明确是否对账完成、是否可以补帐和是否可以结算
+         * 1. 对账完成是指每条对账明细只存在两种状态 CHKDTL_CHKRESULT_EQUAL , CHKDTL_CHKRESULT_NOCHARGE。其它状态都是异常状态
+         * 2. 对"是否可以补帐"的定义是必须"对账完成"
+         * 3. 当所有明细完成对账或补帐才能结算
+         */
+        return if (!errorDtl) {
+            chkfile.status = ConstantUtil.CHKFILE_STATUS_finish
+            checkStatus.checkStatus = true
+            checkStatus.repairStatus = true
+            checkStatus.settleStatus = false
+            // udpate
+            transactionReconciliationService.doConfirmChkfileStatus(chkfile, checkStatus)
+            // 调用补帐业务进行补帐处理
 
-        return transactionReconciliationService.doConfirmChkfileStatus(chkfile)
+            // 完成补帐业务进入结算状态
+            checkStatus.settleStatus = true
+            transactionReconciliationService.doConfirmChkfileStatus(chkfile, checkStatus)
+
+        } else {
+            chkfile.status = ConstantUtil.CHKFILE_STATUS_ERROR
+            checkStatus.settleStatus = false
+            checkStatus.repairStatus = false
+            checkStatus.checkStatus = true
+            transactionReconciliationService.doConfirmChkfileStatus(chkfile, checkStatus)
+        }
     }
-
-
 }
\ No newline at end of file
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_reconciliation_service_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_reconciliation_service_impl.kt
index be112d1..2e2e89a 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_reconciliation_service_impl.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_reconciliation_service_impl.kt
@@ -6,8 +6,10 @@
 import com.supwisdom.dlpay.api.dao.TransactionChkdtlDao
 import com.supwisdom.dlpay.api.dao.TransactionChkfileDao
 import com.supwisdom.dlpay.api.dao.TransactionMainDao
+import com.supwisdom.dlpay.api.domain.TSourceTypeCheckStatus
 import com.supwisdom.dlpay.api.domain.TTransactionChkdtl
 import com.supwisdom.dlpay.api.domain.TTransactionChkfile
+import com.supwisdom.dlpay.api.service.SourceTypeService
 import com.supwisdom.dlpay.api.service.TransactionReconciliationService
 import com.supwisdom.dlpay.api.service.TransactionServiceProxy
 import com.supwisdom.dlpay.exception.TransactionCheckException
@@ -38,6 +40,9 @@
     @Autowired
     private lateinit var transactionService: TransactionServiceProxy
 
+    @Autowired
+    private lateinit var sourceTypeService: SourceTypeService
+
     override fun getTransactionChkfile(accdate: String, sourcetype: String): TTransactionChkfile? {
         return transactionChkfileDao.getByAccdateAndSourcetype(accdate, sourcetype)
     }
@@ -46,7 +51,8 @@
         return transactionChkfileDao.getByAccdateAndSourcetype(accdate, sourcetype).let {
             if (it != null) {
                 if (ConstantUtil.CHKFILE_STATUS_INIT != it.status)
-                    throw TransactionCheckException(TradeErrorCode.BUSINESS_DEAL_ERROR, "accdate=$accdate,sourcetype=$sourcetype 的chkfile已经存在")
+                    throw TransactionCheckException(TradeErrorCode.BUSINESS_DEAL_ERROR,
+                            "accdate=$accdate,sourcetype=$sourcetype 的chkfile已经存在")
                 it
             } else {
                 saveInitTransactionChkfile(accdate, sourcetype) //保存init对账文件
@@ -116,61 +122,69 @@
         })
     }
 
-    override fun doCheckEqualTransdtls(chkfile: TTransactionChkfile) {
+    override fun doCheckEqualTransdtls(chkfile: TTransactionChkfile, start: Int): Int {
         //所有一致的记录,一次任务处理
-        transactionChkdtlDao.getCheckEqualDetails(chkfile.id)?.forEach { chkdtl ->
-            chkdtl.chkresult = ConstantUtil.CHKDTL_CHKRESULT_EQUAL
-            chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_EQUAL
-            chkdtl.remark = "双方交易一致"
-            transactionChkdtlDao.save(chkdtl) //对账明细表更新
+        return transactionChkdtlDao.findEqualDtlWithLimit(chkfile.id, start, start + 1000)?.also {
+            it.forEach { chkdtl ->
+                chkdtl.chkresult = ConstantUtil.CHKDTL_CHKRESULT_EQUAL
+                chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_EQUAL
+                chkdtl.remark = "双方交易一致"
+                transactionChkdtlDao.save(chkdtl) //对账明细表更新
 
-            transactionService.checkSuccessConfirm(chkdtl.localRefno, chkdtl.otherAccdate)
+                transactionService.checkSuccessConfirm(chkdtl.localRefno, chkdtl.otherAccdate)
+            }
+        }?.size ?: 0
+    }
+
+    override fun getMinChkDtlRecordNo(chkfile: TTransactionChkfile, status: String): Int {
+        return if (status.isEmpty()) {
+            transactionChkdtlDao.getMinRecordNo(chkfile.id)
+        } else {
+            transactionChkdtlDao.getMinRecordNoByStatus(chkfile.id, status)
         }
     }
 
     override fun getUncheckTransactionChkdtls(chkfileId: String): List<TTransactionChkdtl> {
-        return transactionChkdtlDao.getUncheckTransactionChkdtls(chkfileId) ?: ArrayList<TTransactionChkdtl>(0)
+        return transactionChkdtlDao.getUncheckTransactionChkdtls(chkfileId)
+                ?: ArrayList<TTransactionChkdtl>(0)
     }
 
-    override fun doCheckTransactionChkdtl(chkdtl: TTransactionChkdtl) {
+    override fun doCheckTransactionChkdtl(chkdtl: TTransactionChkdtl): TTransactionChkdtl {
         val transMain = transactionMainDao.findByRefno(chkdtl.localRefno)
 
         if (null == transMain || !transMain.person) {
             chkdtl.chkresult = ConstantUtil.CHKDTL_CHKRESULT_NOTEXIST
             chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_HANGUP
             chkdtl.remark = "本地流水不存在"
-            transactionChkdtlDao.save(chkdtl) //对账明细表更新
+            return transactionChkdtlDao.save(chkdtl) //对账明细表更新
         }
 
-        if(!MoneyUtil.moneyEqual(chkdtl.amount,transMain.personDtl.amount)){
+        if (!MoneyUtil.moneyEqual(chkdtl.amount, transMain.personDtl.amount)) {
             chkdtl.chkresult = ConstantUtil.CHKDTL_CHKRESULT_DIFF
             chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_HANGUP
             chkdtl.remark = "交易金额不相等"
-            transactionChkdtlDao.save(chkdtl) //对账明细表更新
+            return transactionChkdtlDao.save(chkdtl) //对账明细表更新
         }
 
-        if(transMain.accdate!=chkdtl.accdate || transMain.sourceType != chkdtl.sourcetype){
-            //TODO: 数据异常??
+        if (transMain.accdate != chkdtl.accdate || transMain.sourceType != chkdtl.sourcetype) {
+            // 数据异常, 对账不能完成
             chkdtl.chkresult = ConstantUtil.CHKDTL_CHKRESULT_ERROR
             chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_HANGUP
             chkdtl.remark = "记账日期或支付方式错误"
-            transactionChkdtlDao.save(chkdtl) //对账明细表更新
+            return transactionChkdtlDao.save(chkdtl) //对账明细表更新
         }
 
-        if (transMain.status == TradeDict.DTL_STATUS_SUCCESS) {
+        return if (transMain.status == TradeDict.DTL_STATUS_SUCCESS) {
             chkdtl.chkresult = ConstantUtil.CHKDTL_CHKRESULT_EQUAL
             chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_EQUAL
             chkdtl.remark = "双方交易一致"
-            transactionChkdtlDao.save(chkdtl) //对账明细表更新
-
             transactionService.checkSuccessConfirm(chkdtl.localRefno, chkdtl.otherAccdate)
+            transactionChkdtlDao.save(chkdtl) //对账明细表更新
         } else {
-            //补账
-            transactionService.repair(transMain.refno, chkdtl.otherAccdate, chkdtl.otherRefno, "补账成功")
-
+            //需要补账
             chkdtl.chkresult = ConstantUtil.CHKDTL_CHKRESULT_NOCHARGE
-            chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_EQUAL
-            chkdtl.remark = "本地未入账,补账成功"
+            chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_ADD
+            chkdtl.remark = "本地未入账,需要"
             transactionChkdtlDao.save(chkdtl) //对账明细表更新
         }
     }
@@ -179,11 +193,19 @@
         return transactionChkdtlDao.getUncheckCount(chkfileId).existed > 0
     }
 
-    override fun doCheckLocalMoreDtls(chkfile: TTransactionChkfile) {
-        transactionChkdtlDao.findLocalMoreDtls(chkfile.accdate, chkfile.sourcetype, chkfile.id)?.forEach { refno ->
-            //保存多余的记录
-            transactionMainDao.findByRefno(refno)?.let { transMain ->
-                if (TradeDict.DTL_STATUS_SUCCESS == transMain.status && transMain.accdate == chkfile.accdate && transMain.sourceType == chkfile.sourcetype) {
+    private fun updateCheckStatusWhenError(message: String, checkStatus: TSourceTypeCheckStatus,
+                                           chkfile: TTransactionChkfile) {
+        checkStatus.checkStatus = false
+        checkStatus.remark = message
+        chkfile.status = ConstantUtil.CHKFILE_STATUS_ERROR
+    }
+
+    override fun doCheckLocalMoreDtls(chkfile: TTransactionChkfile, checkStatus: TSourceTypeCheckStatus): List<TTransactionChkdtl> {
+        val result = mutableListOf<TTransactionChkdtl>()
+        transactionChkdtlDao.findLocalMoreDtls(chkfile.accdate, chkfile.sourcetype, chkfile.id)?.also {
+            it.forEach { refno ->
+                //保存多余的记录
+                transactionMainDao.findByRefno(refno)?.also { transMain ->
                     transactionChkdtlDao.save(TTransactionChkdtl().apply {
                         this.chkfileId = chkfile.id
                         this.accdate = chkfile.accdate
@@ -200,30 +222,54 @@
                         this.otherStatus = "unknown"
                         this.remark = "本地成功流水第三方流水不存在"
                         this.extdata = transMain.personDtl.transdesc
-                        this.chkresult = ConstantUtil.CHKDTL_CHKRESULT_NOTEXIST
+                        this.chkresult = ConstantUtil.CHKDTL_CHKRESULT_ERROR
                         this.resolved = ConstantUtil.CHKDTL_RESOLVED_HANGUP
                         this.lastsaved = systemUtilService.sysdatetime.sysdate
                         this.tenantid = chkfile.tenantid
-                    }) //保存本地多出的记录
+                    }).also { dtl ->
+                        result.add(dtl)
+                    }//保存本地多出的记录
+                }.also { transMain ->
+                    if (transMain == null) {
+                        chkfile.status = ConstantUtil.CHKFILE_STATUS_ERROR
+                        updateCheckStatusWhenError("交易参考号<$refno>异常,未找到Main记录",
+                                checkStatus, chkfile)
+                        return result
+                    }
                 }
             }
+        }?.also {
+            if (it.isNotEmpty()) {
+                chkfile.status = ConstantUtil.CHKFILE_STATUS_ERROR
+                updateCheckStatusWhenError("本地有<${it.size}>条流水不在对账文件中,请联系管理员检查",
+                        checkStatus, chkfile)
+            }
         }
+        return result
     }
 
-    override fun doConfirmChkfileStatus(chkfile: TTransactionChkfile): TTransactionChkfile {
-        val personSumInfo = persondtlDao.getPersondtlSumInfo(chkfile.accdate, chkfile.sourcetype, chkfile.tenantid)
-        chkfile.localcnt = personSumInfo.totalcnt ?: 0
-        chkfile.localamt = personSumInfo.totalamt ?: 0.00
-        chkfile.status = ConstantUtil.CHKFILE_STATUS_finish  //对账结束
-        if (chkfile.othercnt == chkfile.localcnt && MoneyUtil.moneyEqual(chkfile.otheramt, chkfile.localamt)) {
-            //fixme: 是否还需判断chkdtl中resolved是否都equal
-            chkfile.result = ConstantUtil.CHKFILE_RESULT_EQUAL
-            chkfile.remark = "对账完成,双方一致"
-        } else {
-            chkfile.result = ConstantUtil.CHKFILE_RESULT_UNEQUAL
-            chkfile.remark = "对账不平,请查看明细数据"
-        }
-        transactionChkfileDao.save(chkfile)
-        return chkfile
+    override fun doConfirmChkfileStatus(chkfile: TTransactionChkfile, checkStatus: TSourceTypeCheckStatus): TTransactionChkfile {
+//        if (chkfile.status == ConstantUtil.CHKFILE_STATUS_ERROR) {
+//            chkfile.remark = checkStatus.remark
+//        } else {
+//            val personSumInfo = persondtlDao.getPersondtlSumInfo(chkfile.accdate, chkfile.sourcetype, chkfile.tenantid)
+//            chkfile.localcnt = personSumInfo.totalcnt ?: 0
+//            chkfile.localamt = personSumInfo.totalamt ?: 0.00
+//            chkfile.status = ConstantUtil.CHKFILE_STATUS_finish  //对账结束
+//            if (chkfile.othercnt == chkfile.localcnt && MoneyUtil.moneyEqual(chkfile.otheramt, chkfile.localamt)) {
+//                //fixme: 是否还需判断chkdtl中resolved是否都equal
+//                chkfile.result = ConstantUtil.CHKFILE_RESULT_EQUAL
+//                chkfile.remark = "对账完成,双方一致"
+//                checkStatus.settleStatus = true
+//            } else {
+//                chkfile.result = ConstantUtil.CHKFILE_RESULT_UNEQUAL
+//                chkfile.remark = "对账不平,请查看明细数据"
+//            }
+//            checkStatus.checkStatus = true
+//            checkStatus.remark = "对账完成"
+//        }
+
+        sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus)
+        return transactionChkfileDao.save(chkfile)
     }
 }
\ No newline at end of file
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_reconciliation_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_reconciliation_service.kt
index c2935c5..9429940 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_reconciliation_service.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_reconciliation_service.kt
@@ -1,6 +1,7 @@
 package com.supwisdom.dlpay.api.service
 
 import com.supwisdom.dlpay.api.bean.YnrccChkfileBean
+import com.supwisdom.dlpay.api.domain.TSourceTypeCheckStatus
 import com.supwisdom.dlpay.api.domain.TTransactionChkdtl
 import com.supwisdom.dlpay.api.domain.TTransactionChkfile
 import org.springframework.transaction.annotation.Transactional
@@ -31,21 +32,24 @@
     fun saveInitTransactionChkfile(accdate: String, sourcetype: String): TTransactionChkfile
 
     @Transactional(rollbackFor = [Exception::class])
-    fun doCheckEqualTransdtls(chkfile: TTransactionChkfile)
+    fun getMinChkDtlRecordNo(chkfile: TTransactionChkfile, status: String): Int
+
+    @Transactional(rollbackFor = [Exception::class])
+    fun doCheckEqualTransdtls(chkfile: TTransactionChkfile, start: Int): Int
 
     @Transactional(rollbackFor = [Exception::class], readOnly = true)
     fun getUncheckTransactionChkdtls(chkfileId: String): List<TTransactionChkdtl>
 
     @Transactional(rollbackFor = [Exception::class])
-    fun doCheckTransactionChkdtl(chkdtl: TTransactionChkdtl)
+    fun doCheckTransactionChkdtl(chkdtl: TTransactionChkdtl): TTransactionChkdtl
 
     @Transactional(rollbackFor = [Exception::class], readOnly = true)
-    fun checkUncheckExists(chkfileId: String):Boolean
+    fun checkUncheckExists(chkfileId: String): Boolean
 
     @Transactional(rollbackFor = [Exception::class])
-    fun doCheckLocalMoreDtls(chkfile: TTransactionChkfile)
+    fun doCheckLocalMoreDtls(chkfile: TTransactionChkfile, checkStatus: TSourceTypeCheckStatus): List<TTransactionChkdtl>
 
     @Transactional(rollbackFor = [Exception::class])
-    fun doConfirmChkfileStatus(chkfile: TTransactionChkfile): TTransactionChkfile
+    fun doConfirmChkfileStatus(chkfile: TTransactionChkfile, checkStatus: TSourceTypeCheckStatus): TTransactionChkfile
 
 }
\ No newline at end of file